diff --git a/doc/langref.html.in b/doc/langref.html.in index 6e03d3ec6d67..9860cf061b83 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -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 91b24e311059..e169b84020c2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -33,7 +33,8 @@ struct TypeStructField; struct CodeGen; struct ConstExprValue; struct IrInstruction; -struct IrInstructionCast; +struct IrInstructionAllocaGen; +struct IrInstructionResultPtrCast; struct IrBasicBlock; struct ScopeDecls; struct ZigWindowsSDK; @@ -56,9 +57,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 +76,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 +92,9 @@ enum OutType { enum ConstParentId { ConstParentIdNone, ConstParentIdStruct, + ConstParentIdErrUnionCode, + ConstParentIdErrUnionPayload, + ConstParentIdOptionalPayload, ConstParentIdArray, ConstParentIdUnion, ConstParentIdScalar, @@ -107,6 +112,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 +132,11 @@ struct ConstParent { struct ConstStructValue { ConstExprValue *fields; - ConstParent parent; }; struct ConstUnionValue { BigInt tag; ConstExprValue *payload; - ConstParent parent; }; enum ConstArraySpecial { @@ -138,7 +150,6 @@ struct ConstArrayValue { union { struct { ConstExprValue *elements; - ConstParent parent; } s_none; Buf *s_buf; } data; @@ -153,19 +164,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 { @@ -178,6 +199,9 @@ enum ConstPtrMut { // The pointer points to memory that is known only at runtime. // For example it may point to the initializer value of a variable. ConstPtrMutRuntimeVar, + // The pointer points to memory for which it must be inferred whether the + // value is comptime known or not. + ConstPtrMutInfer, }; struct ConstPtrValue { @@ -199,6 +223,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 +242,7 @@ struct ConstPtrValue { }; struct ConstErrValue { - ErrorTableEntry *err; + ConstExprValue *error_set; ConstExprValue *payload; }; @@ -265,6 +298,7 @@ struct ConstGlobalRefs { struct ConstExprValue { ZigType *type; ConstValSpecial special; + ConstParent parent; ConstGlobalRefs *global_refs; union { @@ -433,13 +467,14 @@ enum NodeType { NodeTypeArrayType, NodeTypeErrorType, NodeTypeIfErrorExpr, - NodeTypeTestExpr, + NodeTypeIfOptional, NodeTypeErrorSetDecl, NodeTypeCancel, NodeTypeResume, NodeTypeAwaitExpr, NodeTypeSuspend, NodeTypePromiseType, + NodeTypeErrorLiteral, }; enum CallingConvention { @@ -604,11 +639,9 @@ enum CastOp { CastOpIntToFloat, CastOpFloatToInt, CastOpBoolToInt, - CastOpResizeSlice, CastOpNumLitToConcrete, CastOpErrSet, CastOpBitCast, - CastOpPtrOfArrayToSlice, }; struct AstNodeFnCallExpr { @@ -677,7 +710,7 @@ struct AstNodeUse { AstNode *expr; TldResolution resolution; - IrInstruction *value; + ConstExprValue *value; }; struct AstNodeIfBoolExpr { @@ -910,6 +943,10 @@ struct AstNodePromiseType { AstNode *payload_type; // can be NULL }; +struct AstNodeErrorLiteral { + Buf *name; +}; + struct AstNode { enum NodeType type; size_t line; @@ -970,6 +1007,7 @@ struct AstNode { AstNodeAwaitExpr await_expr; AstNodeSuspend suspend; AstNodePromiseType promise_type; + AstNodeErrorLiteral error_literal; } data; }; @@ -1313,7 +1351,7 @@ struct ZigFn { AstNode *fn_no_inline_set_node; AstNode *fn_static_eval_set_node; - ZigList alloca_list; + ZigList alloca_list; ZigList variable_list; Buf *section_name; @@ -1610,7 +1648,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; @@ -1646,6 +1684,7 @@ struct CodeGen { ZigType *entry_c_int[CIntTypeCount]; ZigType *entry_c_longdouble; ZigType *entry_c_void; + ZigType *entry_infer; ZigType *entry_u8; ZigType *entry_u16; ZigType *entry_u32; @@ -1802,10 +1841,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 +1853,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 { @@ -1838,6 +1880,22 @@ struct ErrorTableEntry { ConstExprValue *cached_error_name_val; }; +enum LVal { + // The instruction must return the actual value. + LValNone, + // The instruction must return a pointer to the actual value. + LValPtr, + // The instruction must populate the result location, which is + // an error union, and return the optional error code value. + LValErrorUnionVal, + // The instruction must populate the result location, which is + // an error union, and return a pointer to optional error code value. + LValErrorUnionPtr, + // The instruction must populate the result location, which is + // an optional, and return non-null bool value. + LValOptional, +}; + enum ScopeId { ScopeIdDecls, ScopeIdBlock, @@ -1888,13 +1946,16 @@ struct ScopeBlock { Buf *name; IrBasicBlock *end_block; IrInstruction *is_comptime; + IrInstruction *result_loc; ZigList *incoming_values; ZigList *incoming_blocks; - bool safety_off; AstNode *safety_set_node; - bool fast_math_on; AstNode *fast_math_set_node; + + LVal lval; + bool safety_off; + bool fast_math_on; }; // This scope is created from every defer expression. @@ -1940,6 +2001,7 @@ struct ScopeLoop { IrBasicBlock *break_block; IrBasicBlock *continue_block; IrInstruction *is_comptime; + IrInstruction *result_loc; ZigList *incoming_values; ZigList *incoming_blocks; }; @@ -2030,8 +2092,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 +2113,6 @@ enum IrInstructionId { IrInstructionIdPhi, IrInstructionIdUnOp, IrInstructionIdBinOp, - IrInstructionIdDeclVar, IrInstructionIdLoadPtr, IrInstructionIdStorePtr, IrInstructionIdFieldPtr, @@ -2054,8 +2126,6 @@ enum IrInstructionId { IrInstructionIdCast, IrInstructionIdContainerInitList, IrInstructionIdContainerInitFields, - IrInstructionIdStructInit, - IrInstructionIdUnionInit, IrInstructionIdUnreachable, IrInstructionIdTypeOf, IrInstructionIdToPtrType, @@ -2069,7 +2139,8 @@ enum IrInstructionId { IrInstructionIdAsm, IrInstructionIdSizeOf, IrInstructionIdTestNonNull, - IrInstructionIdUnwrapOptional, + IrInstructionIdOptionalUnwrapPtr, + IrInstructionIdOptionalUnwrapVal, IrInstructionIdOptionalWrap, IrInstructionIdUnionTag, IrInstructionIdClz, @@ -2085,7 +2156,8 @@ enum IrInstructionId { IrInstructionIdCompileLog, IrInstructionIdErrName, IrInstructionIdEmbedFile, - IrInstructionIdCmpxchg, + IrInstructionIdCmpxchgSrc, + IrInstructionIdCmpxchgGen, IrInstructionIdFence, IrInstructionIdTruncate, IrInstructionIdIntCast, @@ -2114,8 +2186,8 @@ enum IrInstructionId { IrInstructionIdErrWrapPayload, IrInstructionIdFnProto, IrInstructionIdTestComptime, - IrInstructionIdPtrCast, - IrInstructionIdBitCast, + IrInstructionIdPtrCastSrc, + IrInstructionIdPtrCastGen, IrInstructionIdWidenOrShorten, IrInstructionIdIntToPtr, IrInstructionIdPtrToInt, @@ -2170,9 +2242,35 @@ enum IrInstructionId { IrInstructionIdBswap, IrInstructionIdBitReverse, IrInstructionIdErrSetCast, - IrInstructionIdToBytes, - IrInstructionIdFromBytes, IrInstructionIdCheckRuntimeScope, + IrInstructionIdResultOptionalPayload, + IrInstructionIdResultErrorUnionPayload, + IrInstructionIdResultErrorUnionCode, + IrInstructionIdResultSlicePtr, + IrInstructionIdResultReturn, + IrInstructionIdResultChild, + IrInstructionIdResultBytesToSlice, + IrInstructionIdResultSliceToBytesSrc, + IrInstructionIdResultSliceToBytesPlaceholder, + IrInstructionIdResultSliceToBytesGen, + IrInstructionIdResultPtrCast, + IrInstructionIdResultCast, + IrInstructionIdAllocaSrc, + IrInstructionIdAllocaGen, + IrInstructionIdAssertNonError, + IrInstructionIdAssertNonNull, + IrInstructionIdErrorUnionFieldErrorSet, + IrInstructionIdFirstArgResultLoc, + IrInstructionIdInferArrayType, + IrInstructionIdInferCompTime, + IrInstructionIdFromBytesLenSrc, + IrInstructionIdFromBytesLenGen, + IrInstructionIdToBytesLenSrc, + IrInstructionIdToBytesLenGen, + IrInstructionIdSetNonNullBit, + IrInstructionIdErrorLiteral, + IrInstructionIdPtrOfArrayToSlice, + IrInstructionIdArrayToSlice, }; struct IrInstruction { @@ -2194,6 +2292,22 @@ struct IrInstruction { bool is_gen; }; +struct IrInstructionDeclVarSrc { + IrInstruction base; + + ZigVar *var; + IrInstruction *var_type; + IrInstruction *align_value; + IrInstruction *ptr; +}; + +struct IrInstructionDeclVarGen { + IrInstruction base; + + ZigVar *var; + IrInstruction *var_ptr; +}; + struct IrInstructionCondBr { IrInstruction base; @@ -2201,6 +2315,7 @@ struct IrInstructionCondBr { IrBasicBlock *then_block; IrBasicBlock *else_block; IrInstruction *is_comptime; + IrInstruction *result_loc; }; struct IrInstructionBr { @@ -2224,6 +2339,7 @@ struct IrInstructionSwitchBr { IrInstructionSwitchBrCase *cases; IrInstruction *is_comptime; IrInstruction *switch_prongs_void; + IrInstruction *result_loc; }; struct IrInstructionSwitchVar { @@ -2302,24 +2418,16 @@ 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; IrInstruction *ptr; + IrInstruction *result_loc; }; struct IrInstructionStorePtr { @@ -2335,7 +2443,8 @@ struct IrInstructionFieldPtr { IrInstruction *container_ptr; Buf *field_name_buffer; IrInstruction *field_name_expr; - bool is_const; + IrInstruction *container_type; + bool initialize; }; struct IrInstructionStructFieldPtr { @@ -2346,12 +2455,18 @@ struct IrInstructionStructFieldPtr { bool is_const; }; +enum IrInstructionUnionFieldPtrId { + IrInstructionUnionFieldPtrIdRef, + IrInstructionUnionFieldPtrIdSwitch, + IrInstructionUnionFieldPtrIdResultPtr, +}; + struct IrInstructionUnionFieldPtr { IrInstruction base; IrInstruction *union_ptr; TypeUnionField *field; - bool is_const; + IrInstructionUnionFieldPtrId id; }; struct IrInstructionElemPtr { @@ -2378,13 +2493,14 @@ 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; + IrInstruction *result_loc; + IrInstruction *first_arg_result_loc; + FnInline fn_inline; + LVal lval; + bool is_async; + bool is_comptime; }; struct IrInstructionConst { @@ -2398,6 +2514,7 @@ struct IrInstructionReturn { IrInstruction base; IrInstruction *value; + IrInstruction *result_loc; }; // TODO get rid of this instruction, replace with instructions for each op code @@ -2405,23 +2522,37 @@ struct IrInstructionCast { IrInstruction base; IrInstruction *value; + IrInstruction *result_loc; ZigType *dest_type; CastOp cast_op; - LLVMValueRef tmp_ptr; +}; + +struct IrInstructionPtrOfArrayToSlice { + IrInstruction base; + + IrInstruction *value; + IrInstruction *result_loc; +}; + +struct IrInstructionArrayToSlice { + IrInstruction base; + + IrInstruction *array; + IrInstruction *result_loc; }; struct IrInstructionContainerInitList { IrInstruction base; IrInstruction *container_type; - size_t item_count; - IrInstruction **items; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; + size_t elem_count; + IrInstruction **elem_result_loc_list; }; struct IrInstructionContainerInitFieldsField { Buf *name; - IrInstruction *value; + IrInstruction *result_loc; AstNode *source_node; TypeStructField *type_struct_field; }; @@ -2432,29 +2563,7 @@ struct IrInstructionContainerInitFields { IrInstruction *container_type; size_t field_count; IrInstructionContainerInitFieldsField *fields; -}; - -struct IrInstructionStructInitField { - IrInstruction *value; - TypeStructField *type_struct_field; -}; - -struct IrInstructionStructInit { - IrInstruction base; - - ZigType *struct_type; - size_t field_count; - IrInstructionStructInitField *fields; - LLVMValueRef tmp_ptr; -}; - -struct IrInstructionUnionInit { - IrInstruction base; - - ZigType *union_type; - TypeUnionField *field; - IrInstruction *init_value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; }; struct IrInstructionUnreachable { @@ -2527,9 +2636,9 @@ struct IrInstructionSliceType { IrInstruction base; IrInstruction *align_value; + IrInstruction *child_type; bool is_const; bool is_volatile; - IrInstruction *child_type; }; struct IrInstructionAsm { @@ -2557,10 +2666,20 @@ 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; +}; + +// Takes an optional value, returns the payload. +struct IrInstructionOptionalUnwrapVal { + IrInstruction base; + + IrInstruction *opt; bool safety_check_on; }; @@ -2598,7 +2717,7 @@ struct IrInstructionRef { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; bool is_const; bool is_volatile; }; @@ -2651,7 +2770,7 @@ struct IrInstructionEmbedFile { IrInstruction *name; }; -struct IrInstructionCmpxchg { +struct IrInstructionCmpxchgSrc { IrInstruction base; IrInstruction *type_value; @@ -2660,15 +2779,24 @@ struct IrInstructionCmpxchg { IrInstruction *new_value; IrInstruction *success_order_value; IrInstruction *failure_order_value; + IrInstruction *result_loc; + + bool is_weak; +}; + +struct IrInstructionCmpxchgGen { + IrInstruction base; - // if this instruction gets to runtime then we know these values: ZigType *type; + IrInstruction *ptr; + IrInstruction *cmp_value; + IrInstruction *new_value; + IrInstruction *result_loc; + AtomicOrder success_order; AtomicOrder failure_order; bool is_weak; - - LLVMValueRef tmp_ptr; }; struct IrInstructionFence { @@ -2708,19 +2836,6 @@ struct IrInstructionErrSetCast { IrInstruction *target; }; -struct IrInstructionToBytes { - IrInstruction base; - - IrInstruction *target; -}; - -struct IrInstructionFromBytes { - IrInstruction base; - - IrInstruction *dest_child_type; - IrInstruction *target; -}; - struct IrInstructionIntToFloat { IrInstruction base; @@ -2776,8 +2891,8 @@ struct IrInstructionSlice { IrInstruction *ptr; IrInstruction *start; IrInstruction *end; + IrInstruction *result_loc; bool safety_check_on; - LLVMValueRef tmp_ptr; }; struct IrInstructionMemberCount { @@ -2851,13 +2966,21 @@ struct IrInstructionTestErr { struct IrInstructionUnwrapErrCode { IrInstruction base; - IrInstruction *value; + IrInstruction *err_union; +}; + +struct IrInstructionErrorUnionFieldErrorSet { + IrInstruction base; + + IrInstruction *ptr; + bool safety_check_on; }; struct IrInstructionUnwrapErrPayload { IrInstruction base; IrInstruction *value; + IrInstruction *result_loc; bool safety_check_on; }; @@ -2865,21 +2988,21 @@ struct IrInstructionOptionalWrap { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; }; struct IrInstructionErrWrapPayload { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; }; struct IrInstructionErrWrapCode { IrInstruction base; IrInstruction *value; - LLVMValueRef tmp_ptr; + IrInstruction *result_loc; }; struct IrInstructionFnProto { @@ -2899,18 +3022,24 @@ struct IrInstructionTestComptime { IrInstruction *value; }; -struct IrInstructionPtrCast { +struct IrInstructionPtrCastSrc { IrInstruction base; IrInstruction *dest_type; IrInstruction *ptr; }; -struct IrInstructionBitCast { +struct IrInstructionPtrCastGen { IrInstruction base; - IrInstruction *dest_type; - IrInstruction *value; + IrInstruction *ptr; + + // This instruction supports being created with the child + // type of the pointer being "infer" which means that a later + // instruction will replace this one. + // so we have a reference here to the pass-1 instruction so that + // the child pointer can be updated to the new pass-2 instruction. + IrInstruction *pass1_parent; }; struct IrInstructionWidenOrShorten { @@ -2983,16 +3112,10 @@ struct IrInstructionTypeName { IrInstruction *type_value; }; -enum LVal { - LValNone, - LValPtr, -}; - struct IrInstructionDeclRef { IrInstruction base; Tld *tld; - LVal lval; }; struct IrInstructionPanic { @@ -3262,6 +3385,181 @@ struct IrInstructionCheckRuntimeScope { IrInstruction *is_comptime; }; +struct IrInstructionResultErrorUnionPayload { + IrInstruction base; + + IrInstruction *prev_result_loc; +}; + +struct IrInstructionResultErrorUnionCode { + IrInstruction base; + + IrInstruction *prev_result_loc; +}; + +struct IrInstructionResultOptionalPayload { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *payload_type; + bool make_non_null; +}; + +struct IrInstructionResultBytesToSlice { + IrInstruction base; + + IrInstruction *prev_result_loc; +}; + +struct IrInstructionResultSliceToBytesSrc { + IrInstruction base; + + IrInstruction *elem_type; + IrInstruction *prev_result_loc; +}; + +struct IrInstructionResultSliceToBytesPlaceholder { + IrInstruction base; + + ZigType *elem_type; + IrInstruction *prev_result_loc; + IrInstruction *pass1_parent; +}; + +struct IrInstructionResultSliceToBytesGen { + IrInstruction base; + + IrInstruction *prev_result_loc; +}; + +struct IrInstructionResultSlicePtr { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *array_loc; + uint64_t len; +}; + +struct IrInstructionResultReturn { + IrInstruction base; +}; + +struct IrInstructionResultChild { + IrInstruction base; + + IrInstruction *prev_result_loc; + LVal lval; +}; + +struct IrInstructionResultPtrCast { + IrInstruction base; + + IrInstruction *elem_type; + IrInstruction *prev_result_loc; +}; + +struct IrInstructionResultCast { + IrInstruction base; + + IrInstruction *elem_type; + IrInstruction *prev_result_loc; +}; + +struct IrInstructionAllocaSrc { + IrInstruction base; + + // TODO: remove this field. use implicit casts instead. + IrInstruction *child_type; + IrInstruction *align; + IrInstruction *is_comptime; + const char *name_hint; +}; + +struct IrInstructionAllocaGen { + IrInstruction base; + + uint32_t align; + const char *name_hint; +}; + +struct IrInstructionAssertNonError { + IrInstruction base; + + IrInstruction *err_code; +}; + +// This is the safety check when using `.?`. +struct IrInstructionAssertNonNull { + IrInstruction base; + + IrInstruction *is_non_null; +}; + +struct IrInstructionFirstArgResultLoc { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *fn_ref; +}; + +struct IrInstructionInferArrayType { + IrInstruction base; + + IrInstruction *src_type; + size_t elem_count; +}; + +struct IrInstructionInferCompTime { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *new_result_loc; +}; + +struct IrInstructionSetNonNullBit { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *non_null_bit; + IrInstruction *new_result_loc; +}; + +// This is the instruction for the syntax: +// `error.Foo` +struct IrInstructionErrorLiteral { + IrInstruction base; + + Buf *name; +}; + +struct IrInstructionFromBytesLenSrc { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *new_result_loc; +}; + +struct IrInstructionFromBytesLenGen { + IrInstruction base; + + ZigType *elem_type; + IrInstruction *prev_result_loc; +}; + +struct IrInstructionToBytesLenSrc { + IrInstruction base; + + IrInstruction *prev_result_loc; + IrInstruction *new_result_loc; +}; + +struct IrInstructionToBytesLenGen { + IrInstruction base; + + ZigType *elem_type; + IrInstruction *prev_result_loc; +}; + struct IrInstructionBswap { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 00eb38de9e19..9f416a486c7b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -405,7 +405,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons uint32_t bit_offset_in_host, uint32_t host_int_bytes) { assert(!type_is_invalid(child_type)); - assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque); + assert(ptr_len == PtrLenSingle || + child_type->id != ZigTypeIdOpaque || + child_type == g->builtin_types.entry_infer); if (byte_alignment != 0) { uint32_t abi_alignment = get_abi_alignment(g, child_type); @@ -570,7 +572,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 @@ -1137,6 +1139,24 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { ZigType *gen_return_type; if (is_async) { gen_return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); + } else if (fn_type_id->return_type->id == ZigTypeIdErrorUnion) { + ZigType *payload_type = fn_type_id->return_type->data.error_union.payload_type; + if (type_has_bits(payload_type)) { + ZigType *gen_type = get_pointer_to_type(g, payload_type, false); + gen_param_types.append(gen_type->type_ref); + param_di_types.append(gen_type->di_type); + } + gen_return_type = g->builtin_types.entry_global_error_set; + } else if (fn_type_id->return_type->id == ZigTypeIdOptional) { + if (handle_is_ptr(fn_type_id->return_type)) { + ZigType *payload_type = fn_type_id->return_type->data.maybe.child_type; + ZigType *gen_type = get_pointer_to_type(g, payload_type, false); + gen_param_types.append(gen_type->type_ref); + param_di_types.append(gen_type->di_type); + gen_return_type = g->builtin_types.entry_bool; + } else { + gen_return_type = fn_type_id->return_type; + } } else if (!type_has_bits(fn_type_id->return_type)) { gen_return_type = g->builtin_types.entry_void; } else if (first_arg_return) { @@ -1278,7 +1298,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 +1308,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 +1364,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 +1386,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 +2526,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 +2966,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 (result->type->id == ZigTypeIdInvalid) { 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 +3441,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,13 +3536,14 @@ 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: case NodeTypeAwaitExpr: case NodeTypeSuspend: case NodeTypePromiseType: + case NodeTypeErrorLiteral: zig_unreachable(); } } @@ -3582,13 +3606,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 +3623,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 +3653,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 +3703,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 +3712,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 +3730,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 +3739,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 +4116,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; @@ -4132,11 +4158,11 @@ void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node) assert(!fn_type->data.fn.is_generic); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - ZigType *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable, - &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node); - fn_table_entry->src_implicit_return_type = block_return_type; + ConstExprValue *block_result = ir_analyze(g, &fn_table_entry->ir_executable, + &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node, nullptr); + fn_table_entry->src_implicit_return_type = block_result->type; - if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) { + if (type_is_invalid(block_result->type) || fn_table_entry->analyzed_executable.invalid) { assert(g->errors.length > 0); fn_table_entry->anal_state = FnAnalStateInvalid; return; @@ -4150,6 +4176,10 @@ void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node) inferred_err_set_type = fn_table_entry->src_implicit_return_type; } else if (fn_table_entry->src_implicit_return_type->id == ZigTypeIdErrorUnion) { inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.error_union.err_set_type; + } else if (fn_table_entry->src_implicit_return_type->id == ZigTypeIdOptional && + fn_table_entry->src_implicit_return_type->data.maybe.child_type->id == ZigTypeIdErrorSet) + { + inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.maybe.child_type; } else { add_node_error(g, return_type_node, buf_sprintf("function with inferred error set must return at least one possible error")); @@ -4228,18 +4258,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 (use_target_value->type->id == ZigTypeIdInvalid) { 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 +4331,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 +4515,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 +4762,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) { @@ -4744,6 +4779,8 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { case ConstPtrMutComptimeVar: hash_val += (uint32_t)1103195694; break; + case ConstPtrMutInfer: + zig_unreachable(); } switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: @@ -4763,6 +4800,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 +4823,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 +4924,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 +4938,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 +5040,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 +5101,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 +5121,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 +5145,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 +5172,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 +5180,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 +5693,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 +5728,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,15 +5898,22 @@ 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) { switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: 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)); + if (const_val->type->id == ZigTypeIdPointer) { + // 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)); + } else { + buf_appendf(buf, "(TODO print this pointer)"); + } return; case ConstPtrSpecialBaseArray: if (const_val->data.x_ptr.data.base_array.is_cstr) { @@ -5777,6 +5932,9 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy case ConstPtrSpecialDiscard: buf_append_str(buf, "*_"); return; + case ConstPtrSpecialNull: + buf_append_str(buf, "null"); + return; case ConstPtrSpecialFunction: { ZigFn *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; @@ -5787,6 +5945,14 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy 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: @@ -5914,6 +6080,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 { @@ -5951,11 +6119,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; @@ -5970,10 +6139,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)"); @@ -6165,6 +6331,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; @@ -6208,17 +6378,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[] = { @@ -6446,7 +6606,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 efbc065a633a..1bac15ebcce7 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 f7eb646e16d7..904018a66845 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: @@ -249,6 +249,8 @@ static const char *node_type_str(NodeType node_type) { return "PromiseType"; case NodeTypePointerType: return "PointerType"; + case NodeTypeErrorLiteral: + return "ErrorLiteral"; } zig_unreachable(); } @@ -387,7 +389,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 +976,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); @@ -1145,6 +1147,11 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } break; } + case NodeTypeErrorLiteral: + { + fprintf(ar->f, "error.%s", buf_ptr(node->data.error_literal.name)); + break; + } case NodeTypeParamDecl: case NodeTypeTestDecl: case NodeTypeStructField: diff --git a/src/codegen.cpp b/src/codegen.cpp index 47f2aa103fde..4731804b710a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -461,6 +461,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,10 +613,21 @@ 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) { + if (cc_want_sret_attr(cc)) { + addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias"); + } else { + ZigType *ret_type = fn_type->data.fn.fn_type_id.return_type; + if (ret_type->id == ZigTypeIdErrorUnion) { + uint64_t sz = type_size(g, ret_type->data.error_union.payload_type); + addLLVMArgAttrInt(fn_table_entry->llvm_value, 0, "dereferenceable", sz); + } else if (ret_type->id == ZigTypeIdOptional) { + uint64_t sz = type_size(g, ret_type->data.maybe.child_type); + addLLVMArgAttrInt(fn_table_entry->llvm_value, 0, "dereferenceable", sz); + } else { + addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); + } } init_gen_i = 1; } @@ -2200,10 +2226,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) { @@ -2239,20 +2265,19 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut } static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { - LLVMValueRef value = ir_llvm_value(g, return_instruction->value); ZigType *return_type = return_instruction->value->value.type; - - if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { - assert(g->cur_ret_ptr); - gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); + if (!type_has_bits(return_type)) { + LLVMBuildRetVoid(g->builder); + return nullptr; + } + LLVMValueRef value = ir_llvm_value(g, return_instruction->value); + if (handle_is_ptr(return_type)) { LLVMBuildRetVoid(g->builder); - } else if (handle_is_ptr(return_type)) { - LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); - LLVMBuildRet(g->builder, by_val_value); + return nullptr; } else { LLVMBuildRet(g->builder, value); + return nullptr; } - return nullptr; } static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, @@ -2817,69 +2842,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, zig_unreachable(); case CastOpNoop: return expr_val; - case CastOpResizeSlice: - { - assert(cast_instruction->tmp_ptr); - assert(wanted_type->id == ZigTypeIdStruct); - assert(wanted_type->data.structure.is_slice); - assert(actual_type->id == ZigTypeIdStruct); - assert(actual_type->data.structure.is_slice); - - ZigType *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; - ZigType *actual_child_type = actual_pointer_type->data.pointer.child_type; - ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; - ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; - - - size_t actual_ptr_index = actual_type->data.structure.fields[slice_ptr_index].gen_index; - size_t actual_len_index = actual_type->data.structure.fields[slice_len_index].gen_index; - size_t wanted_ptr_index = wanted_type->data.structure.fields[slice_ptr_index].gen_index; - size_t wanted_len_index = wanted_type->data.structure.fields[slice_len_index].gen_index; - - LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_ptr_index, ""); - LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, - wanted_type->data.structure.fields[0].type_entry->type_ref, ""); - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - (unsigned)wanted_ptr_index, ""); - gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); - - LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_len_index, ""); - LLVMValueRef src_len = gen_load_untyped(g, src_len_ptr, 0, false, ""); - uint64_t src_size = type_size(g, actual_child_type); - uint64_t dest_size = type_size(g, wanted_child_type); - - LLVMValueRef new_len; - if (dest_size == 1) { - LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false); - new_len = LLVMBuildMul(g->builder, src_len, src_size_val, ""); - } else if (src_size == 1) { - LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false); - if (ir_want_runtime_safety(g, &cast_instruction->base)) { - LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, ""); - LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdSliceWidenRemainder); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - new_len = LLVMBuildExactUDiv(g->builder, src_len, dest_size_val, ""); - } else { - zig_unreachable(); - } - - LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - (unsigned)wanted_len_index, ""); - gen_store_untyped(g, new_len, dest_len_ptr, 0, false); - - - return cast_instruction->tmp_ptr; - } case CastOpIntToFloat: assert(actual_type->id == ZigTypeIdInt); if (actual_type->data.integral.is_signed) { @@ -2933,35 +2895,12 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, return expr_val; case CastOpBitCast: return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); - case CastOpPtrOfArrayToSlice: { - assert(cast_instruction->tmp_ptr); - assert(actual_type->id == ZigTypeIdPointer); - ZigType *array_type = actual_type->data.pointer.child_type; - assert(array_type->id == ZigTypeIdArray); - - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - slice_ptr_index, ""); - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->type_ref), - LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), - }; - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - slice_len_index, ""); - LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, - array_type->data.array.len, false); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - - return cast_instruction->tmp_ptr; - } } zig_unreachable(); } 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)) { @@ -2971,14 +2910,6 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, ""); } -static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable, - IrInstructionBitCast *instruction) -{ - ZigType *wanted_type = instruction->base.value.type; - LLVMValueRef value = ir_llvm_value(g, instruction->value); - return LLVMBuildBitCast(g->builder, value, wanted_type->type_ref, ""); -} - static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable, IrInstructionWidenOrShorten *instruction) { @@ -3148,49 +3079,16 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildICmp(g->builder, LLVMIntEQ, value, zero, ""); } -static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, - IrInstructionDeclVar *decl_var_instruction) -{ - ZigVar *var = decl_var_instruction->var; +static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrInstructionDeclVarGen *instruction) { + ZigVar *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) return nullptr; - 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; - - 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, - 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); - } - } - + var->value_ref = ir_llvm_value(g, instruction->var_ptr); gen_var_debug_decl(g, var); return nullptr; } @@ -3225,21 +3123,77 @@ 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(); +} + +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); +} - assert(instruction->ptr->value.type->id == ZigTypeIdPointer); +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); + 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 { @@ -3415,12 +3369,20 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr CallingConvention cc = fn_type->data.fn.fn_type_id.cc; - bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id); + bool is_error_union = src_return_type->id == ZigTypeIdErrorUnion; + bool is_optional = src_return_type->id == ZigTypeIdOptional; + bool first_arg_ret = (is_error_union && type_has_bits(src_return_type->data.error_union.payload_type)) || + (ret_has_bits && want_first_arg_sret(g, fn_type_id)); bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); bool is_var_args = fn_type_id->is_var_args; ZigList gen_param_values = {}; + LLVMValueRef result_ptr = nullptr; + if (first_arg_ret || instruction->is_async) { + result_ptr = ir_llvm_value(g, instruction->result_loc); + } + if (first_arg_ret) { - gen_param_values.append(instruction->tmp_ptr); + gen_param_values.append(result_ptr); } if (prefix_arg_err_ret_stack) { gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope)); @@ -3428,7 +3390,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->is_async) { gen_param_values.append(ir_llvm_value(g, instruction->async_allocator)); - LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_err_index, ""); gen_param_values.append(err_val_ptr); } FnWalk fn_walk = {}; @@ -3471,9 +3433,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->is_async) { - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_payload_index, ""); LLVMBuildStore(g->builder, result, payload_ptr); - return instruction->tmp_ptr; + return result_ptr; } if (src_return_type->id == ZigTypeIdUnreachable) { @@ -3481,12 +3443,14 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } else if (!ret_has_bits) { return nullptr; } else if (first_arg_ret) { - set_call_instr_sret(g, result); - return instruction->tmp_ptr; + if (cc_want_sret_attr(cc) || (!is_error_union && !is_optional)) { + set_call_instr_sret(g, result); + } + return (is_error_union || is_optional) ? result : result_ptr; } else if (handle_is_ptr(src_return_type)) { - auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr); - LLVMSetAlignment(store_instr, LLVMGetAlignment(instruction->tmp_ptr)); - return instruction->tmp_ptr; + auto store_instr = LLVMBuildStore(g->builder, result, result_ptr); + LLVMSetAlignment(store_instr, LLVMGetAlignment(result_ptr)); + return result_ptr; } else { return result; } @@ -3535,7 +3499,10 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab return bitcasted_union_field_ptr; } - if (ir_want_runtime_safety(g, &instruction->base)) { + bool safety_check_on = instruction->id == IrInstructionUnionFieldPtrIdRef && + ir_want_runtime_safety(g, &instruction->base); + + if (safety_check_on) { LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, ""); LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, ""); @@ -3553,7 +3520,16 @@ 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, ""); + if (instruction->id == IrInstructionUnionFieldPtrIdResultPtr) { + LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_tag_index, ""); + LLVMValueRef tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, + &field->enum_field->value); + gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); + } + + 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 +3691,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 +3707,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 +3731,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); @@ -3765,6 +3741,31 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, } } +static LLVMValueRef ir_render_optional_unwrap_val(CodeGen *g, IrExecutable *executable, + IrInstructionOptionalUnwrapVal *instruction) +{ + ZigType *opt_type = instruction->opt->value.type; + assert(opt_type->id == ZigTypeIdOptional); + LLVMValueRef opt_handle = ir_llvm_value(g, instruction->opt); + if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { + LLVMValueRef non_null_bit = gen_non_null_bit(g, opt_type, opt_handle); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); + LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + ZigType *payload_type = opt_type->data.maybe.child_type; + if (!type_has_bits(payload_type)) + return nullptr; + if (!handle_is_ptr(opt_type)) + return opt_handle; + return LLVMBuildStructGEP(g->builder, opt_handle, maybe_child_index, ""); +} + static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *int_type, BuiltinFnId fn_id) { ZigLLVMFnKey key = {}; const char *fn_name; @@ -3891,9 +3892,10 @@ static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstru if (handle_is_ptr(instruction->value->value.type)) { return value; } else { - assert(instruction->tmp_ptr); - gen_store_untyped(g, value, instruction->tmp_ptr, 0, false); - return instruction->tmp_ptr; + assert(instruction->result_loc != nullptr); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + gen_store_untyped(g, value, result_ptr, 0, false); + return result_ptr; } } @@ -4174,39 +4176,52 @@ 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); + assert(type_has_bits(instruction->type)); + ZigType *ptr_type = instruction->result_loc->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = ptr_type->data.pointer.child_type; + bool is_scalar = !handle_is_ptr(child_type); + LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering(instruction->success_order); LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); - LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, + LLVMValueRef cmpxchg_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, success_order, failure_order, instruction->is_weak); - ZigType *maybe_type = instruction->base.value.type; - assert(maybe_type->id == ZigTypeIdOptional); - ZigType *child_type = maybe_type->data.maybe.child_type; + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, cmpxchg_val, 1, ""); + LLVMBasicBlockRef null_block = is_scalar ? LLVMAppendBasicBlock(g->cur_fn_val, "CmpXchgNull") : + LLVMGetInsertBlock(g->builder); + LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn_val, "CmpXchgNonNull"); + LLVMBasicBlockRef phi_block = LLVMAppendBasicBlock(g->cur_fn_val, "CmpXchgPhi"); - if (type_is_codegen_pointer(child_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, ""); - } + if (is_scalar) { + LLVMBuildCondBr(g->builder, success_bit, null_block, non_null_block); - assert(instruction->tmp_ptr != nullptr); - assert(type_has_bits(instruction->type)); + LLVMPositionBuilderAtEnd(g->builder, null_block); + LLVMValueRef zeroes = LLVMConstNull(child_type->type_ref); + gen_assign_raw(g, result_ptr, instruction->result_loc->value.type, zeroes); + LLVMBuildBr(g->builder, phi_block); + } else { + LLVMBuildCondBr(g->builder, success_bit, phi_block, non_null_block); + } - 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); + LLVMPositionBuilderAtEnd(g->builder, non_null_block); + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, cmpxchg_val, 0, ""); + gen_assign_raw(g, result_ptr, instruction->result_loc->value.type, payload_val); + LLVMBuildBr(g->builder, phi_block); - LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); - LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); - gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false); - return instruction->tmp_ptr; + LLVMPositionBuilderAtEnd(g->builder, phi_block); + LLVMValueRef non_null_bit = LLVMBuildPhi(g->builder, LLVMInt1Type(), ""); + LLVMValueRef incoming_values[] = {LLVMConstAllOnes(LLVMInt1Type()), LLVMConstNull(LLVMInt1Type())}; + LLVMBasicBlockRef incoming_blocks[] = {non_null_block, null_block}; + LLVMAddIncoming(non_null_bit, incoming_values, incoming_blocks, 2); + return non_null_bit; } static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) { @@ -4269,15 +4284,11 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutable *executable, IrIns } static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInstructionSlice *instruction) { - assert(instruction->tmp_ptr); - - LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); - ZigType *array_ptr_type = instruction->ptr->value.type; - assert(array_ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = array_ptr_type->data.pointer.child_type; - LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); + LLVMValueRef array_ptr = ir_llvm_value(g, instruction->ptr); + ZigType *array_type = instruction->ptr->value.type; - LLVMValueRef tmp_struct_ptr = instruction->tmp_ptr; + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + assert(LLVMGetTypeKind(LLVMTypeOf(tmp_struct_ptr)) == LLVMPointerTypeKind); bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); @@ -4351,6 +4362,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,13 +4552,13 @@ 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; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *err_union_type = ptr_type->data.pointer.child_type; +static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapErrCode *instruction) +{ + ZigType *err_union_type = instruction->err_union->value.type; + assert(err_union_type->id == ZigTypeIdErrorUnion); ZigType *payload_type = err_union_type->data.error_union.payload_type; - LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); - LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); + LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->err_union); if (type_has_bits(payload_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); @@ -4556,7 +4568,30 @@ 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_error_union_field_error_set(CodeGen *g, IrExecutable *executable, + IrInstructionErrorUnionFieldErrorSet *instruction) +{ + ZigType *ptr_type = instruction->ptr->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *err_union_type = ptr_type->data.pointer.child_type; + assert(err_union_type->id == ZigTypeIdErrorUnion); + ZigType *payload_type = err_union_type->data.error_union.payload_type; + LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->ptr); + + if (type_has_bits(payload_type)) { + return LLVMBuildStructGEP(g->builder, err_union_ptr, err_union_err_index, ""); + } else { + return err_union_ptr; + } +} + +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 +4603,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,19 +4642,19 @@ 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; } - assert(instruction->tmp_ptr); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_ptr, maybe_child_index, ""); // child_type and instruction->value->value.type may differ by constness gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_ptr, maybe_null_index, ""); gen_store_untyped(g, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr, 0, false); - return instruction->tmp_ptr; + return result_ptr; } static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapCode *instruction) { @@ -4635,12 +4670,12 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable if (!type_has_bits(payload_type) || !type_has_bits(err_set_type)) return err_val; - assert(instruction->tmp_ptr); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_err_index, ""); gen_store_untyped(g, err_val, err_tag_ptr, 0, false); - return instruction->tmp_ptr; + return result_ptr; } static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executable, IrInstructionErrWrapPayload *instruction) { @@ -4660,17 +4695,17 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa if (!type_has_bits(payload_type)) return ok_err_val; - assert(instruction->tmp_ptr); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_err_index, ""); gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false); - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, result_ptr, err_union_payload_index, ""); gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val); - return instruction->tmp_ptr; + return result_ptr; } static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, IrInstructionUnionTag *instruction) { @@ -4691,90 +4726,6 @@ static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, Ir return get_handle_value(g, tag_field_ptr, tag_type, ptr_type); } -static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, IrInstructionStructInit *instruction) { - for (size_t i = 0; i < instruction->field_count; i += 1) { - IrInstructionStructInitField *field = &instruction->fields[i]; - TypeStructField *type_struct_field = field->type_struct_field; - if (!type_has_bits(type_struct_field->type_entry)) - continue; - - LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, - (unsigned)type_struct_field->gen_index, ""); - LLVMValueRef value = ir_llvm_value(g, field->value); - - uint32_t field_align_bytes = get_abi_alignment(g, type_struct_field->type_entry); - uint32_t host_int_bytes = get_host_int_bytes(g, instruction->struct_type, type_struct_field); - - ZigType *ptr_type = get_pointer_to_type_extra(g, type_struct_field->type_entry, - false, false, PtrLenSingle, field_align_bytes, - (uint32_t)type_struct_field->bit_offset_in_host, host_int_bytes); - - gen_assign_raw(g, field_ptr, ptr_type, value); - } - return instruction->tmp_ptr; -} - -static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) { - TypeUnionField *type_union_field = instruction->field; - - if (!type_has_bits(type_union_field->type_entry)) - return nullptr; - - uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry); - ZigType *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry, - false, false, PtrLenSingle, field_align_bytes, - 0, 0); - - LLVMValueRef uncasted_union_ptr; - // Even if safety is off in this block, if the union type has the safety field, we have to populate it - // correctly. Otherwise safety code somewhere other than here could fail. - ZigType *union_type = instruction->union_type; - if (union_type->data.unionation.gen_tag_index != SIZE_MAX) { - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, - union_type->data.unionation.gen_tag_index, ""); - - LLVMValueRef tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, - &type_union_field->enum_field->value); - gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - - uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, - (unsigned)union_type->data.unionation.gen_union_index, ""); - } else { - uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, (unsigned)0, ""); - } - - LLVMValueRef field_ptr = LLVMBuildBitCast(g->builder, uncasted_union_ptr, ptr_type->type_ref, ""); - LLVMValueRef value = ir_llvm_value(g, instruction->init_value); - - gen_assign_raw(g, field_ptr, ptr_type, value); - - return instruction->tmp_ptr; -} - -static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *executable, - IrInstructionContainerInitList *instruction) -{ - ZigType *array_type = instruction->base.value.type; - assert(array_type->id == ZigTypeIdArray); - LLVMValueRef tmp_array_ptr = instruction->tmp_ptr; - assert(tmp_array_ptr); - - size_t field_count = instruction->item_count; - - ZigType *child_type = array_type->data.array.child_type; - for (size_t i = 0; i < field_count; i += 1) { - LLVMValueRef elem_val = ir_llvm_value(g, instruction->items[i]); - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->type_ref), - LLVMConstInt(g->builtin_types.entry_usize->type_ref, i, false), - }; - LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, tmp_array_ptr, indices, 2, ""); - gen_assign_raw(g, elem_ptr, get_pointer_to_type(g, child_type, false), elem_val); - } - - return tmp_array_ptr; -} - static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) { gen_panic(g, ir_llvm_value(g, instruction->msg), get_cur_err_ret_trace_val(g, instruction->base.scope)); return nullptr; @@ -4959,9 +4910,7 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f args.append(alignment_val); LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, alloc_fn_val, args.items, args.length, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); - set_call_instr_sret(g, call_instruction); - LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_err_index, ""); - LLVMValueRef err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); + LLVMValueRef err_val = call_instruction; LLVMBuildStore(g->builder, err_val, err_code_ptr); LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, LLVMConstNull(LLVMTypeOf(err_val)), ""); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(fn_val, "AllocOk"); @@ -4969,7 +4918,7 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, ok_block); - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_payload_index, ""); + LLVMValueRef payload_ptr = sret_ptr; ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false, PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); ZigType *slice_type = get_slice_type(g, u8_ptr_type); @@ -5078,6 +5027,188 @@ static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstr return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); } +static LLVMValueRef ir_render_result_return(CodeGen *g, IrExecutable *executable, + IrInstructionResultReturn *instruction) +{ + assert(g->cur_ret_ptr != nullptr || !type_has_bits(instruction->base.value.type)); + return g->cur_ret_ptr; +} + +static LLVMValueRef ir_render_result_optional_payload(CodeGen *g, IrExecutable *executable, + IrInstructionResultOptionalPayload *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + ZigType *ptr_type = instruction->base.value.type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = ptr_type->data.pointer.child_type; + if (!type_has_bits(child_type)) { + if (instruction->make_non_null) { + LLVMValueRef non_null_bit = LLVMConstInt(LLVMInt1Type(), 1, true); + gen_store_untyped(g, non_null_bit, prev_result_loc, 0, false); + } + return prev_result_loc; + } else if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { + return prev_result_loc; + } else { + if (instruction->make_non_null) { + LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_null_index, ""); + gen_store_untyped(g, LLVMConstInt(LLVMInt1Type(), 1, true), nonnull_ptr, 0, false); + } + return LLVMBuildStructGEP(g->builder, prev_result_loc, maybe_child_index, ""); + } +} + +static LLVMValueRef ir_render_result_slice_ptr(CodeGen *g, IrExecutable *executable, + IrInstructionResultSlicePtr *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + assert(instruction->prev_result_loc->value.type->id == ZigTypeIdPointer); + ZigType *slice_type = instruction->prev_result_loc->value.type->data.pointer.child_type; + assert(slice_type->id == ZigTypeIdStruct && slice_type->data.structure.is_slice); + size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index; + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, (unsigned)len_field_index, ""); + LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, instruction->len, false); + gen_store_untyped(g, len_val, len_field_ptr, 0, false); + + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, (unsigned)ptr_field_index, ""); + if (instruction->array_loc == nullptr) + return ptr_field_ptr; + + LLVMValueRef array_loc = ir_llvm_value(g, instruction->array_loc); + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->type_ref), + LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), + }; + LLVMValueRef casted_array_loc = LLVMBuildInBoundsGEP(g->builder, array_loc, indices, 2, ""); + gen_store_untyped(g, casted_array_loc, ptr_field_ptr, 0, false); + + return ptr_field_ptr; +} + +static LLVMValueRef ir_render_result_error_union_payload(CodeGen *g, IrExecutable *executable, + IrInstructionResultErrorUnionPayload *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, err_union_err_index, ""); + LLVMTypeRef err_type_ref = g->builtin_types.entry_global_error_set->type_ref; + gen_store_untyped(g, LLVMConstInt(err_type_ref, 0, false), err_val_ptr, 0, false); + return LLVMBuildStructGEP(g->builder, prev_result_loc, err_union_payload_index, ""); +} + +static LLVMValueRef ir_render_result_error_union_code(CodeGen *g, IrExecutable *executable, + IrInstructionResultErrorUnionCode *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + ZigType *ptr_to_error_union_type = instruction->prev_result_loc->value.type; + assert(ptr_to_error_union_type->id == ZigTypeIdPointer); + ZigType *error_union_type = ptr_to_error_union_type->data.pointer.child_type; + assert(error_union_type->id == ZigTypeIdErrorUnion); + if (type_has_bits(error_union_type->data.error_union.payload_type)) { + return LLVMBuildStructGEP(g->builder, prev_result_loc, err_union_err_index, ""); + } else { + return prev_result_loc; + } +} + +static LLVMValueRef ir_render_assert_non_error(CodeGen *g, IrExecutable *executable, + IrInstructionAssertNonError *instruction) +{ + if (!ir_want_runtime_safety(g, &instruction->base) || g->errors_by_index.length <= 1) { + return nullptr; + } + LLVMValueRef err_val = ir_llvm_value(g, instruction->err_code); + LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref); + LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); + LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrError"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrOk"); + LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); + + LLVMPositionBuilderAtEnd(g->builder, err_block); + gen_safety_crash_for_err(g, err_val, instruction->base.scope); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return nullptr; +} + +static LLVMValueRef ir_render_assert_non_null(CodeGen *g, IrExecutable *executable, + IrInstructionAssertNonNull *instruction) +{ + if (!ir_want_runtime_safety(g, &instruction->base)) { + return nullptr; + } + LLVMValueRef is_non_null = ir_llvm_value(g, instruction->is_non_null); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); + LLVMBuildCondBr(g->builder, is_non_null, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return nullptr; +} + +static LLVMValueRef ir_render_set_non_null_bit(CodeGen *g, IrExecutable *executable, + IrInstructionSetNonNullBit *instruction) +{ + LLVMValueRef ptr_to_optional = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef non_null_bit = ir_llvm_value(g, instruction->non_null_bit); + + LLVMValueRef nonnull_ptr = LLVMBuildStructGEP(g->builder, ptr_to_optional, maybe_null_index, ""); + gen_store_untyped(g, non_null_bit, nonnull_ptr, 0, false); + return nullptr; +} + +static LLVMValueRef ir_render_result_slice_to_bytes(CodeGen *g, IrExecutable *executable, + IrInstructionResultSliceToBytesGen *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef new_result_loc = LLVMBuildBitCast(g->builder, prev_result_loc, + instruction->base.value.type->type_ref, ""); + return new_result_loc; +} + +static LLVMValueRef ir_render_from_bytes_len(CodeGen *g, IrExecutable *executable, + IrInstructionFromBytesLenGen *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, slice_len_index, ""); + LLVMValueRef prev_len = LLVMBuildLoad(g->builder, len_ptr, ""); + uint64_t elem_size = type_size(g, instruction->elem_type); + LLVMValueRef elem_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, elem_size, false); + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef remainder_val = LLVMBuildURem(g->builder, prev_len, elem_size_val, ""); + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenOk"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdSliceWidenRemainder); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + LLVMValueRef new_len = LLVMBuildExactUDiv(g->builder, prev_len, elem_size_val, ""); + LLVMBuildStore(g->builder, new_len, len_ptr); + return nullptr; +} + +static LLVMValueRef ir_render_to_bytes_len(CodeGen *g, IrExecutable *executable, + IrInstructionToBytesLenGen *instruction) +{ + LLVMValueRef prev_result_loc = ir_llvm_value(g, instruction->prev_result_loc); + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, prev_result_loc, slice_len_index, ""); + LLVMValueRef prev_len = LLVMBuildLoad(g->builder, len_ptr, ""); + uint64_t elem_size = type_size(g, instruction->elem_type); + LLVMValueRef elem_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, elem_size, false); + LLVMValueRef new_len = LLVMBuildMul(g->builder, prev_len, elem_size_val, ""); + LLVMBuildStore(g->builder, new_len, len_ptr); + return nullptr; +} + static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) { LLVMValueRef op = ir_llvm_value(g, instruction->op); ZigType *int_type = instruction->base.value.type; @@ -5101,6 +5232,53 @@ static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInst return LLVMBuildTrunc(g->builder, shifted, int_type->type_ref, ""); } +static LLVMValueRef gen_array_to_slice(CodeGen *g, LLVMValueRef result_ptr, LLVMValueRef array_ptr, + ZigType *array_type) +{ + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, + slice_ptr_index, ""); + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->type_ref), + LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), + }; + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_ptr, slice_len_index, ""); + LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, + array_type->data.array.len, false); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + return result_ptr; +} + +static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutable *executable, + IrInstructionPtrOfArrayToSlice *instruction) +{ + ZigType *actual_type = instruction->value->value.type; + LLVMValueRef array_ptr = ir_llvm_value(g, instruction->value); + assert(array_ptr); + + assert(actual_type->id == ZigTypeIdPointer); + ZigType *array_type = actual_type->data.pointer.child_type; + assert(array_type->id == ZigTypeIdArray); + + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + return gen_array_to_slice(g, result_ptr, array_ptr, array_type); +} + +static LLVMValueRef ir_render_array_to_slice(CodeGen *g, IrExecutable *executable, + IrInstructionArrayToSlice *instruction) +{ + ZigType *array_type = instruction->array->value.type; + assert(array_type->id == ZigTypeIdArray); + assert(handle_is_ptr(array_type)); + LLVMValueRef result_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array); + + return gen_array_to_slice(g, result_ptr, array_ptr, array_type); +} + static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable, IrInstructionBitReverse *instruction) { LLVMValueRef op = ir_llvm_value(g, instruction->op); ZigType *int_type = instruction->base.value.type; @@ -5180,16 +5358,32 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: case IrInstructionIdErrSetCast: - case IrInstructionIdFromBytes: - case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: case IrInstructionIdCheckRuntimeScope: + case IrInstructionIdDeclVarSrc: + case IrInstructionIdAllocaSrc: + case IrInstructionIdAllocaGen: + case IrInstructionIdFirstArgResultLoc: + case IrInstructionIdResultCast: + case IrInstructionIdContainerInitList: + case IrInstructionIdInferArrayType: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdInferCompTime: + case IrInstructionIdResultPtrCast: + case IrInstructionIdCmpxchgSrc: + case IrInstructionIdErrorLiteral: + case IrInstructionIdResultSliceToBytesSrc: + case IrInstructionIdResultSliceToBytesPlaceholder: + case IrInstructionIdResultBytesToSlice: + case IrInstructionIdFromBytesLenSrc: + case IrInstructionIdToBytesLenSrc: + case IrInstructionIdResultChild: 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 +5414,10 @@ 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 IrInstructionIdOptionalUnwrapVal: + return ir_render_optional_unwrap_val(g, executable, (IrInstructionOptionalUnwrapVal *)instruction); case IrInstructionIdClz: return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -5236,8 +5432,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: @@ -5262,6 +5458,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErr: return ir_render_test_err(g, executable, (IrInstructionTestErr *)instruction); + case IrInstructionIdErrorUnionFieldErrorSet: + return ir_render_error_union_field_error_set(g, executable, (IrInstructionErrorUnionFieldErrorSet *)instruction); case IrInstructionIdUnwrapErrCode: return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction); case IrInstructionIdUnwrapErrPayload: @@ -5274,14 +5472,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_err_wrap_payload(g, executable, (IrInstructionErrWrapPayload *)instruction); case IrInstructionIdUnionTag: return ir_render_union_tag(g, executable, (IrInstructionUnionTag *)instruction); - case IrInstructionIdStructInit: - 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 IrInstructionIdBitCast: - return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction); + case IrInstructionIdPtrCastGen: + return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction); case IrInstructionIdWidenOrShorten: return ir_render_widen_or_shorten(g, executable, (IrInstructionWidenOrShorten *)instruction); case IrInstructionIdPtrToInt: @@ -5294,8 +5486,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_int_to_err(g, executable, (IrInstructionIntToErr *)instruction); case IrInstructionIdErrToInt: return ir_render_err_to_int(g, executable, (IrInstructionErrToInt *)instruction); - case IrInstructionIdContainerInitList: - return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction); case IrInstructionIdPanic: return ir_render_panic(g, executable, (IrInstructionPanic *)instruction); case IrInstructionIdTagName: @@ -5346,8 +5536,34 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); case IrInstructionIdSqrt: return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); + case IrInstructionIdResultReturn: + return ir_render_result_return(g, executable, (IrInstructionResultReturn *)instruction); + case IrInstructionIdResultOptionalPayload: + return ir_render_result_optional_payload(g, executable, (IrInstructionResultOptionalPayload *)instruction); + case IrInstructionIdResultSlicePtr: + return ir_render_result_slice_ptr(g, executable, (IrInstructionResultSlicePtr *)instruction); + case IrInstructionIdResultErrorUnionPayload: + return ir_render_result_error_union_payload(g, executable, (IrInstructionResultErrorUnionPayload *)instruction); + case IrInstructionIdResultErrorUnionCode: + return ir_render_result_error_union_code(g, executable, (IrInstructionResultErrorUnionCode *)instruction); + case IrInstructionIdAssertNonError: + return ir_render_assert_non_error(g, executable, (IrInstructionAssertNonError *)instruction); + case IrInstructionIdAssertNonNull: + return ir_render_assert_non_null(g, executable, (IrInstructionAssertNonNull *)instruction); + case IrInstructionIdSetNonNullBit: + return ir_render_set_non_null_bit(g, executable, (IrInstructionSetNonNullBit *)instruction); + case IrInstructionIdResultSliceToBytesGen: + return ir_render_result_slice_to_bytes(g, executable, (IrInstructionResultSliceToBytesGen *)instruction); + case IrInstructionIdFromBytesLenGen: + return ir_render_from_bytes_len(g, executable, (IrInstructionFromBytesLenGen *)instruction); + case IrInstructionIdToBytesLenGen: + return ir_render_to_bytes_len(g, executable, (IrInstructionToBytesLenGen *)instruction); case IrInstructionIdBswap: return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction); + case IrInstructionIdPtrOfArrayToSlice: + return ir_render_ptr_of_array_to_slice(g, executable, (IrInstructionPtrOfArrayToSlice *)instruction); + case IrInstructionIdArrayToSlice: + return ir_render_array_to_slice(g, executable, (IrInstructionArrayToSlice *)instruction); case IrInstructionIdBitReverse: return ir_render_bit_reverse(g, executable, (IrInstructionBitReverse *)instruction); } @@ -5366,8 +5582,25 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) { LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block); for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { IrInstruction *instruction = current_block->instruction_list.at(instr_i); - if (instruction->ref_count == 0 && !ir_has_side_effects(instruction)) - continue; + if (!ir_has_side_effects(instruction)) { + if (instruction->ref_count == 0) + continue; + if (instruction->value.special != ConstValSpecialRuntime) { + // If it's not a pointer, skip + if (get_codegen_ptr_type(instruction->value.type) == nullptr) + continue; + // If the const-ness isn't inferred, skip + if (instruction->value.data.x_ptr.mut != ConstPtrMutInfer) + continue; + + // Follow the const ptr reference, see if the underlying value is runtime + if (const_ptr_pointee(nullptr, g, &instruction->value, nullptr)->special != + ConstValSpecialRuntime) + { + continue; + } + } + } instruction->llvm_value = ir_render_instruction(g, executable, instruction); } current_block->llvm_exit_block = LLVMGetInsertBlock(g->builder); @@ -5377,6 +5610,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 +5623,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 +5644,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 +5669,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 +5680,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 +5887,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 +5956,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 +5986,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 +6020,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 +6256,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 +6266,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 +6474,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 +6491,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 +6508,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 +6517,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 +6530,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 +6544,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); @@ -6246,48 +6598,25 @@ static void do_code_gen(CodeGen *g) { // allocate temporary stack data for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_list.length; alloca_i += 1) { - IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i); - LLVMValueRef *slot; - ZigType *slot_type = instruction->value.type; - if (instruction->id == IrInstructionIdCast) { - IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction; - slot = &cast_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdRef) { - IrInstructionRef *ref_instruction = (IrInstructionRef *)instruction; - slot = &ref_instruction->tmp_ptr; - assert(instruction->value.type->id == ZigTypeIdPointer); - slot_type = instruction->value.type->data.pointer.child_type; - } else if (instruction->id == IrInstructionIdContainerInitList) { - IrInstructionContainerInitList *container_init_list_instruction = (IrInstructionContainerInitList *)instruction; - slot = &container_init_list_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdStructInit) { - IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction; - slot = &struct_init_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdUnionInit) { - IrInstructionUnionInit *union_init_instruction = (IrInstructionUnionInit *)instruction; - slot = &union_init_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdCall) { - IrInstructionCall *call_instruction = (IrInstructionCall *)instruction; - slot = &call_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdSlice) { - IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction; - slot = &slice_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdOptionalWrap) { - IrInstructionOptionalWrap *maybe_wrap_instruction = (IrInstructionOptionalWrap *)instruction; - slot = &maybe_wrap_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdErrWrapPayload) { - IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction; - slot = &err_wrap_payload_instruction->tmp_ptr; - } 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; - slot = &cmpxchg_instruction->tmp_ptr; - } else { - zig_unreachable(); + IrInstructionAllocaGen *instruction = fn_table_entry->alloca_list.at(alloca_i); + ZigType *ptr_type = instruction->base.value.type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = ptr_type->data.pointer.child_type; + if (!type_has_bits(child_type)) + continue; + if (child_type == g->builtin_types.entry_infer) + continue; + if (instruction->base.ref_count == 0) + continue; + if (instruction->base.value.special != ConstValSpecialRuntime) { + if (const_ptr_pointee(nullptr, g, &instruction->base.value, nullptr)->special != + ConstValSpecialRuntime) + { + continue; + } } - *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type)); + instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, + get_ptr_align(g, ptr_type)); } ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); @@ -6304,12 +6633,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 +6648,9 @@ 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->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 +6660,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), @@ -6656,6 +6983,9 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_c_void = get_opaque_type(g, nullptr, nullptr, "c_void"); g->primitive_type_table.put(&g->builtin_types.entry_c_void->name, g->builtin_types.entry_c_void); } + { + g->builtin_types.entry_infer = get_opaque_type(g, nullptr, nullptr, "(infer)"); + } { ZigType *entry = new_type_table_entry(ZigTypeIdErrorSet); @@ -7458,9 +7788,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 c651f30dd54f..85df4a1af694 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -35,8 +35,11 @@ struct IrAnalyze { size_t instruction_index; ZigType *explicit_return_type; AstNode *explicit_return_type_source_node; + ZigType *scalar_return_type; + ZigType *payload_return_type; ZigList src_implicit_return_type_list; IrBasicBlock *const_predecessor_bb; + ConstExprValue *result_value; }; enum ConstCastResultId { @@ -146,17 +149,16 @@ enum UndefAllowed { UndefBad, }; -static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); -static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval); +static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope, + LVal lval, IrInstruction *result_loc); static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction); static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type); static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr); static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg); static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, - IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type); + IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type, bool initialize); static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var); static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); -static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align); static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align); static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val); @@ -165,8 +167,24 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source ConstExprValue *out_val, ConstExprValue *ptr_val); static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, ZigType *dest_type, IrInstruction *dest_type_src); +static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *uncasted_ptr, IrInstruction *uncasted_value); static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed); +static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type); +static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, bool safety_check_on); +static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, IrInstruction *result_loc, bool safety_check_on); +static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, + IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on); +static ZigType *adjust_ptr_child(CodeGen *g, ZigType *ptr_type, ZigType *new_child); +static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, + IrInstruction *source_inst, IrInstruction *prev_result_loc, ZigType *elem_type, ZigType *child_type); +static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *child_type_inst, + uint32_t align, const char *name_hint, bool is_comptime); static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); +static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value); +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 +196,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,25 +229,30 @@ 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; - if (a->id == b->id) - return true; + if (a->id == b->id) { + if (a->id == ZigTypeIdInt) { + return a->data.integral.is_signed == b->data.integral.is_signed; + } else { + return true; + } + } 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 +341,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 +381,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; } @@ -389,6 +429,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) { return IrInstructionIdCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrOfArrayToSlice *) { + return IrInstructionIdPtrOfArrayToSlice; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToSlice *) { + return IrInstructionIdArrayToSlice; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) { return IrInstructionIdContainerInitList; } @@ -449,8 +497,12 @@ 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(IrInstructionOptionalUnwrapVal *) { + return IrInstructionIdOptionalUnwrapVal; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { @@ -493,14 +545,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) { return IrInstructionIdRef; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) { - return IrInstructionIdStructInit; -} - -static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) { - return IrInstructionIdUnionInit; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) { return IrInstructionIdCompileErr; } @@ -517,8 +561,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 *) { @@ -541,14 +589,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrSetCast *) { return IrInstructionIdErrSetCast; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytes *) { - return IrInstructionIdToBytes; -} - -static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytes *) { - return IrInstructionIdFromBytes; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) { return IrInstructionIdIntToFloat; } @@ -649,12 +689,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(IrInstructionBitCast *) { - return IrInstructionIdBitCast; +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastGen *) { + return IrInstructionIdPtrCastGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) { @@ -869,6 +909,110 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScop return IrInstructionIdCheckRuntimeScope; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultErrorUnionPayload *) { + return IrInstructionIdResultErrorUnionPayload; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultErrorUnionCode *) { + return IrInstructionIdResultErrorUnionCode; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultOptionalPayload *) { + return IrInstructionIdResultOptionalPayload; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSlicePtr *) { + return IrInstructionIdResultSlicePtr; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultReturn *) { + return IrInstructionIdResultReturn; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultChild *) { + return IrInstructionIdResultChild; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultPtrCast *) { + return IrInstructionIdResultPtrCast; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultCast *) { + return IrInstructionIdResultCast; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSliceToBytesSrc *) { + return IrInstructionIdResultSliceToBytesSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSliceToBytesPlaceholder *) { + return IrInstructionIdResultSliceToBytesPlaceholder; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultSliceToBytesGen *) { + return IrInstructionIdResultSliceToBytesGen; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionResultBytesToSlice *) { + return IrInstructionIdResultBytesToSlice; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionAllocaSrc *) { + return IrInstructionIdAllocaSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionAllocaGen *) { + return IrInstructionIdAllocaGen; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonError *) { + return IrInstructionIdAssertNonError; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *) { + return IrInstructionIdAssertNonNull; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnionFieldErrorSet *) { + return IrInstructionIdErrorUnionFieldErrorSet; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFirstArgResultLoc *) { + return IrInstructionIdFirstArgResultLoc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionInferArrayType *) { + return IrInstructionIdInferArrayType; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionInferCompTime *) { + return IrInstructionIdInferCompTime; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytesLenSrc *) { + return IrInstructionIdFromBytesLenSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytesLenGen *) { + return IrInstructionIdFromBytesLenGen; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytesLenSrc *) { + return IrInstructionIdToBytesLenSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytesLenGen *) { + return IrInstructionIdToBytesLenGen; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionSetNonNullBit *) { + return IrInstructionIdSetNonNullBit; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorLiteral *) { + return IrInstructionIdErrorLiteral; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -901,8 +1045,39 @@ static IrInstruction *ir_build_cast(IrBuilder *irb, Scope *scope, AstNode *sourc return &cast_instruction->base; } +static IrInstruction *ir_build_ptr_of_array_to_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *value, IrInstruction *result_loc) +{ + IrInstructionPtrOfArrayToSlice *instruction = ir_build_instruction( + irb, scope, source_node); + instruction->value = value; + instruction->result_loc = result_loc; + + ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, ZigType *slice_type, + IrInstruction *array, IrInstruction *result_loc) +{ + IrInstructionArrayToSlice *instruction = ir_build_instruction( + &ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->base.value.special = ConstValSpecialRuntime; + instruction->base.value.type = slice_type; + + instruction->array = array; + instruction->result_loc = result_loc; + + ir_ref_instruction(array, ira->new_irb.current_basic_block); + ir_ref_instruction(result_loc, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *condition, - IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime) + IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime, IrInstruction *result_loc) { IrInstructionCondBr *cond_br_instruction = ir_build_instruction(irb, scope, source_node); cond_br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; @@ -911,36 +1086,32 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *so cond_br_instruction->then_block = then_block; cond_br_instruction->else_block = else_block; cond_br_instruction->is_comptime = is_comptime; + cond_br_instruction->result_loc = result_loc; 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); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &cond_br_instruction->base; } -static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *return_value) { +static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *return_value, + IrInstruction *result_loc) +{ IrInstructionReturn *return_instruction = ir_build_instruction(irb, scope, source_node); return_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; return_instruction->base.value.special = ConstValSpecialStatic; return_instruction->value = return_value; + return_instruction->result_loc = result_loc; ir_ref_instruction(return_value, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); 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; @@ -1136,14 +1307,17 @@ static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scop } static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_ptr, Buf *field_name) + IrInstruction *container_ptr, IrInstruction *container_type, Buf *field_name, bool initialize) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; + instruction->container_type = container_type; instruction->field_name_buffer = field_name; instruction->field_name_expr = nullptr; + instruction->initialize = initialize; ir_ref_instruction(container_ptr, irb->current_basic_block); + if (container_type != nullptr) ir_ref_instruction(container_type, irb->current_basic_block); return &instruction->base; } @@ -1161,11 +1335,12 @@ static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, As } static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *union_ptr, TypeUnionField *field) + IrInstruction *union_ptr, TypeUnionField *field, IrInstructionUnionFieldPtrId id) { IrInstructionUnionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->union_ptr = union_ptr; instruction->field = field; + instruction->id = id; ir_ref_instruction(union_ptr, irb->current_basic_block); @@ -1175,7 +1350,7 @@ static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, Ast static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, - IrInstruction *new_stack) + IrInstruction *new_stack, IrInstruction *result_loc, IrInstruction *first_arg_result_loc, LVal lval) { IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; @@ -1187,15 +1362,17 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->is_async = is_async; call_instruction->async_allocator = async_allocator; call_instruction->new_stack = new_stack; + call_instruction->result_loc = result_loc; + call_instruction->first_arg_result_loc = first_arg_result_loc; + call_instruction->lval = lval; - if (fn_ref) - ir_ref_instruction(fn_ref, irb->current_basic_block); + if (fn_ref) 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); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); + if (first_arg_result_loc != nullptr) ir_ref_instruction(first_arg_result_loc, irb->current_basic_block); return &call_instruction->base; } @@ -1272,66 +1449,45 @@ static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *sour } static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_type, size_t item_count, IrInstruction **items) + IrInstruction *container_type, size_t elem_count, IrInstruction **elem_result_loc_list, + IrInstruction *result_loc) { IrInstructionContainerInitList *container_init_list_instruction = ir_build_instruction(irb, scope, source_node); container_init_list_instruction->container_type = container_type; - container_init_list_instruction->item_count = item_count; - container_init_list_instruction->items = items; + container_init_list_instruction->elem_count = elem_count; + container_init_list_instruction->elem_result_loc_list = elem_result_loc_list; + container_init_list_instruction->result_loc = result_loc; + ir_ref_instruction(result_loc, irb->current_basic_block); 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); + for (size_t i = 0; i < elem_count; i += 1) { + ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block); } return &container_init_list_instruction->base; } static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields) + IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields, + IrInstruction *result_loc) { IrInstructionContainerInitFields *container_init_fields_instruction = ir_build_instruction(irb, scope, source_node); container_init_fields_instruction->container_type = container_type; container_init_fields_instruction->field_count = field_count; container_init_fields_instruction->fields = fields; + container_init_fields_instruction->result_loc = result_loc; ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < field_count; i += 1) { - ir_ref_instruction(fields[i].value, irb->current_basic_block); + ir_ref_instruction(fields[i].result_loc, irb->current_basic_block); } + ir_ref_instruction(result_loc, irb->current_basic_block); return &container_init_fields_instruction->base; } -static IrInstruction *ir_build_struct_init(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigType *struct_type, size_t field_count, IrInstructionStructInitField *fields) -{ - IrInstructionStructInit *struct_init_instruction = ir_build_instruction(irb, scope, source_node); - struct_init_instruction->struct_type = struct_type; - struct_init_instruction->field_count = field_count; - struct_init_instruction->fields = fields; - - for (size_t i = 0; i < field_count; i += 1) - ir_ref_instruction(fields[i].value, irb->current_basic_block); - - return &struct_init_instruction->base; -} - -static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigType *union_type, TypeUnionField *field, IrInstruction *init_value) -{ - IrInstructionUnionInit *union_init_instruction = ir_build_instruction(irb, scope, source_node); - union_init_instruction->union_type = union_type; - union_init_instruction->field = field; - union_init_instruction->init_value = init_value; - - ir_ref_instruction(init_value, irb->current_basic_block); - - return &union_init_instruction->base; -} - static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionUnreachable *unreachable_instruction = ir_build_instruction(irb, scope, source_node); @@ -1355,20 +1511,34 @@ 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, - ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) +static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *ptr) { - 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; decl_var_instruction->var_type = var_type; decl_var_instruction->align_value = align_value; - decl_var_instruction->init_value = init_value; + decl_var_instruction->ptr = ptr; if (var_type) ir_ref_instruction(var_type, irb->current_basic_block); if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); - ir_ref_instruction(init_value, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + + return &decl_var_instruction->base; +} + +static IrInstruction *ir_build_var_decl_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, + IrInstruction *var_ptr) +{ + IrInstructionDeclVarGen *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; + decl_var_instruction->var_ptr = var_ptr; + + ir_ref_instruction(var_ptr, irb->current_basic_block); return &decl_var_instruction->base; } @@ -1391,11 +1561,15 @@ static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *sou return &export_instruction->base; } -static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) { +static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, + IrInstruction *result_loc) +{ IrInstructionLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; + instruction->result_loc = result_loc; ir_ref_instruction(ptr, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -1542,23 +1716,39 @@ 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; +} + +static IrInstruction *ir_build_optional_unwrap_val(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *opt, bool safety_check_on) +{ + IrInstructionOptionalUnwrapVal *instruction = ir_build_instruction(irb, scope, source_node); + instruction->opt = opt; + instruction->safety_check_on = safety_check_on; + + ir_ref_instruction(opt, irb->current_basic_block); return &instruction->base; } -static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { +static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *value, IrInstruction *result_loc) +{ IrInstructionOptionalWrap *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; + instruction->result_loc = result_loc; ir_ref_instruction(value, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -1608,9 +1798,10 @@ static IrInstruction *ir_build_pop_count(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, - IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, - IrInstruction *switch_prongs_void) +static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target_value, IrBasicBlock *else_block, + size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, + IrInstruction *switch_prongs_void, IrInstruction *result_loc) { IrInstructionSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; @@ -1621,11 +1812,13 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode * instruction->cases = cases; instruction->is_comptime = is_comptime; instruction->switch_prongs_void = switch_prongs_void; + instruction->result_loc = result_loc; ir_ref_instruction(target_value, irb->current_basic_block); if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); ir_ref_bb(else_block); if (switch_prongs_void) ir_ref_instruction(switch_prongs_void, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); for (size_t i = 0; i < case_count; i += 1) { ir_ref_instruction(cases[i].value, irb->current_basic_block); @@ -1678,14 +1871,16 @@ static IrInstruction *ir_build_import(IrBuilder *irb, Scope *scope, AstNode *sou } static IrInstruction *ir_build_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, - bool is_const, bool is_volatile) + bool is_const, bool is_volatile, IrInstruction *result_loc) { IrInstructionRef *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; instruction->is_const = is_const; instruction->is_volatile = is_volatile; + instruction->result_loc = result_loc; ir_ref_instruction(value, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -1765,30 +1960,49 @@ 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, - IrInstruction *success_order_value, IrInstruction *failure_order_value, - bool is_weak, - ZigType *type, AtomicOrder success_order, AtomicOrder failure_order) +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, IrInstruction *result_loc, 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; instruction->new_value = new_value; instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; + instruction->result_loc = result_loc; instruction->is_weak = is_weak; + + 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); + ir_ref_instruction(success_order_value, irb->current_basic_block); + ir_ref_instruction(failure_order_value, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_cmpxchg_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, + ZigType *type, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + AtomicOrder success_order, AtomicOrder failure_order, IrInstruction *result_loc, bool is_weak) +{ + IrInstructionCmpxchgGen *instruction = ir_build_instruction(irb, scope, source_node); instruction->type = type; + instruction->ptr = ptr; + instruction->cmp_value = cmp_value; + instruction->new_value = new_value; instruction->success_order = success_order; instruction->failure_order = failure_order; + instruction->result_loc = result_loc; + instruction->is_weak = is_weak; - if (type_value != nullptr) 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); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -1847,26 +2061,6 @@ static IrInstruction *ir_build_err_set_cast(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } -static IrInstruction *ir_build_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { - IrInstructionToBytes *instruction = ir_build_instruction(irb, scope, source_node); - instruction->target = target; - - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstruction *ir_build_from_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_child_type, IrInstruction *target) { - IrInstructionFromBytes *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_child_type = dest_child_type; - instruction->target = target; - - ir_ref_instruction(dest_child_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { IrInstructionIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_type = dest_type; @@ -1949,16 +2143,18 @@ static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *sou } static IrInstruction *ir_build_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on) + IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, IrInstruction *result_loc) { IrInstructionSlice *instruction = ir_build_instruction(irb, scope, source_node); instruction->ptr = ptr; instruction->start = start; instruction->end = end; + instruction->result_loc = result_loc; instruction->safety_check_on = safety_check_on; ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(start, irb->current_basic_block); + ir_ref_instruction(result_loc, irb->current_basic_block); if (end) ir_ref_instruction(end, irb->current_basic_block); return &instruction->base; @@ -2060,24 +2256,26 @@ 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; } static IrInstruction *ir_build_unwrap_err_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *value, bool safety_check_on) + IrInstruction *value, IrInstruction *result_loc, bool safety_check_on) { IrInstructionUnwrapErrPayload *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; + instruction->result_loc = result_loc; instruction->safety_check_on = safety_check_on; ir_ref_instruction(value, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); return &instruction->base; } @@ -2115,30 +2313,29 @@ 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_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *dest_type, IrInstruction *value) +static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instr, + ZigType *ptr_type, IrInstruction *ptr) { - IrInstructionBitCast *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->value = value; + IrInstructionPtrCastGen *instruction = ir_build_instruction( + &ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->ptr = ptr; + instruction->base.value.type = ptr_type; - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); return &instruction->base; } @@ -2277,13 +2474,10 @@ static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, - Tld *tld, LVal lval) -{ +static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, Tld *tld) { IrInstructionDeclRef *instruction = ir_build_instruction( irb, scope, source_node); instruction->tld = tld; - instruction->lval = lval; return &instruction->base; } @@ -2747,71 +2941,401 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, return &instruction->base; } -static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { - results[ReturnKindUnconditional] = 0; - results[ReturnKindError] = 0; +static IrInstruction *ir_build_result_optional_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *payload_type, bool make_non_null) +{ + IrInstructionResultOptionalPayload *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->payload_type = payload_type; + instruction->make_non_null = make_non_null; - Scope *scope = inner_scope; + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block); - while (scope != outer_scope) { - assert(scope); - switch (scope->id) { - case ScopeIdDefer: { - AstNode *defer_node = scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - results[defer_kind] += 1; - scope = scope->parent; - continue; - } - case ScopeIdDecls: - case ScopeIdFnDef: - return; - case ScopeIdBlock: - case ScopeIdVarDecl: - case ScopeIdLoop: - case ScopeIdSuspend: - case ScopeIdCompTime: - case ScopeIdRuntime: - scope = scope->parent; - continue; - case ScopeIdDeferExpr: - case ScopeIdCImport: - case ScopeIdCoroPrelude: - zig_unreachable(); - } - } + return &instruction->base; } -static IrInstruction *ir_mark_gen(IrInstruction *instruction) { - instruction->is_gen = true; - return instruction; +static IrInstruction *ir_build_result_slice_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, uint64_t len, IrInstruction *array_loc) +{ + IrInstructionResultSlicePtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->len = len; + instruction->array_loc = array_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + if (array_loc != nullptr) ir_ref_instruction(array_loc, irb->current_basic_block); + + return &instruction->base; } -static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) { - Scope *scope = inner_scope; - bool is_noreturn = false; - while (scope != outer_scope) { - if (!scope) - return is_noreturn; +static IrInstruction *ir_build_result_error_union_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc) +{ + IrInstructionResultErrorUnionPayload *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; - switch (scope->id) { - case ScopeIdDefer: { - AstNode *defer_node = scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - if (defer_kind == ReturnKindUnconditional || - (gen_error_defers && defer_kind == ReturnKindError)) - { - AstNode *defer_expr_node = defer_node->data.defer.expr; - 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) { - is_noreturn = true; - } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); - } + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_error_union_code(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc) +{ + IrInstructionResultErrorUnionCode *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_return(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionResultReturn *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstruction *ir_build_result_child(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, LVal lval) +{ + IrInstructionResultChild *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->lval = lval; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *elem_type, IrInstruction *prev_result_loc) +{ + IrInstructionResultPtrCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->elem_type = elem_type; + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(elem_type, irb->current_basic_block); + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *elem_type, IrInstruction *prev_result_loc) +{ + IrInstructionResultCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->elem_type = elem_type; + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(elem_type, irb->current_basic_block); + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_slice_to_bytes_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *elem_type, IrInstruction *prev_result_loc) +{ + IrInstructionResultSliceToBytesSrc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->elem_type = elem_type; + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(elem_type, irb->current_basic_block); + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_slice_to_bytes_placeholder(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *prev_result_loc, ZigType *elem_type) +{ + IrInstructionResultSliceToBytesPlaceholder *instruction = + ir_build_instruction( + &ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->base.value.type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_infer, false); + instruction->prev_result_loc = prev_result_loc; + instruction->elem_type = elem_type; + instruction->pass1_parent = source_instr; + + ir_ref_instruction(prev_result_loc, ira->new_irb.current_basic_block); + // no ref for pass1_parent + + return &instruction->base; +} + +static IrInstruction *ir_build_result_slice_to_bytes_gen(IrAnalyze *ira, IrInstruction *source_instr, + ZigType *result_type, IrInstruction *prev_result_loc) +{ + IrInstructionResultSliceToBytesGen *instruction = ir_build_instruction( + &ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->base.value.type = result_type; + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(prev_result_loc, ira->new_irb.current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_result_bytes_to_slice(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc) +{ + IrInstructionResultBytesToSlice *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_alloca_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *child_type, IrInstruction *align, const char *name_hint, IrInstruction *is_comptime) +{ + IrInstructionAllocaSrc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base.is_gen = true; + instruction->child_type = child_type; + instruction->align = align; + instruction->name_hint = name_hint; + instruction->is_comptime = is_comptime; + + if (child_type != nullptr) ir_ref_instruction(child_type, irb->current_basic_block); + if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block); + if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstructionAllocaGen *ir_create_alloca_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, + uint32_t align, const char *name_hint) +{ + IrInstructionAllocaGen *instruction = ir_create_instruction(irb, scope, source_node); + instruction->align = align; + instruction->name_hint = name_hint; + + return instruction; +} + +static IrInstruction *ir_build_assert_non_error(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *err_code) +{ + IrInstructionAssertNonError *instruction = ir_build_instruction(irb, scope, source_node); + instruction->err_code = err_code; + + ir_ref_instruction(err_code, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_assert_non_null(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *is_non_null) +{ + IrInstructionAssertNonNull *instruction = ir_build_instruction(irb, scope, source_node); + instruction->is_non_null = is_non_null; + + ir_ref_instruction(is_non_null, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_error_union_field_error_set(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *ptr, bool safety_check_on) +{ + IrInstructionErrorUnionFieldErrorSet *instruction = ir_build_instruction(irb, scope, source_node); + instruction->ptr = ptr; + instruction->safety_check_on = safety_check_on; + + ir_ref_instruction(ptr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_first_arg_result_loc(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *fn_ref) +{ + IrInstructionFirstArgResultLoc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->fn_ref = fn_ref; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(fn_ref, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_infer_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *src_type, size_t elem_count) +{ + IrInstructionInferArrayType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->src_type = src_type; + instruction->elem_count = elem_count; + + ir_ref_instruction(src_type, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_infer_comptime(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *new_result_loc) +{ + IrInstructionInferCompTime *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->new_result_loc = new_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(new_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_from_bytes_len_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *new_result_loc) +{ + IrInstructionFromBytesLenSrc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->new_result_loc = new_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(new_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_from_bytes_len_gen(IrAnalyze *ira, IrInstruction *source_inst, + IrInstruction *prev_result_loc, ZigType *elem_type) +{ + IrInstructionFromBytesLenGen *instruction = ir_build_instruction( + &ira->new_irb, source_inst->scope, source_inst->source_node); + instruction->base.value.special = ConstValSpecialStatic; + instruction->base.value.type = ira->codegen->builtin_types.entry_void; + + instruction->prev_result_loc = prev_result_loc; + instruction->elem_type = elem_type; + + ir_ref_instruction(prev_result_loc, ira->new_irb.current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_to_bytes_len_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *new_result_loc) +{ + IrInstructionToBytesLenSrc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->new_result_loc = new_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(new_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_to_bytes_len_gen(IrAnalyze *ira, IrInstruction *source_inst, + IrInstruction *prev_result_loc, ZigType *elem_type) +{ + IrInstructionToBytesLenGen *instruction = ir_build_instruction( + &ira->new_irb, source_inst->scope, source_inst->source_node); + instruction->base.value.special = ConstValSpecialStatic; + instruction->base.value.type = ira->codegen->builtin_types.entry_void; + + instruction->prev_result_loc = prev_result_loc; + instruction->elem_type = elem_type; + + ir_ref_instruction(prev_result_loc, ira->new_irb.current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_set_nonnull_bit(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *prev_result_loc, IrInstruction *non_null_bit, IrInstruction *new_result_loc) +{ + IrInstructionSetNonNullBit *instruction = ir_build_instruction(irb, scope, source_node); + instruction->prev_result_loc = prev_result_loc; + instruction->non_null_bit = non_null_bit; + instruction->new_result_loc = new_result_loc; + + ir_ref_instruction(prev_result_loc, irb->current_basic_block); + ir_ref_instruction(non_null_bit, irb->current_basic_block); + if (new_result_loc != nullptr) ir_ref_instruction(new_result_loc, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_error_literal(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *name) { + IrInstructionErrorLiteral *instruction = ir_build_instruction(irb, scope, source_node); + instruction->name = name; + + return &instruction->base; +} + +static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { + results[ReturnKindUnconditional] = 0; + results[ReturnKindError] = 0; + + Scope *scope = inner_scope; + + while (scope != outer_scope) { + assert(scope); + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + results[defer_kind] += 1; + scope = scope->parent; + continue; + } + case ScopeIdDecls: + case ScopeIdFnDef: + return; + case ScopeIdBlock: + case ScopeIdVarDecl: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + case ScopeIdRuntime: + scope = scope->parent; + continue; + case ScopeIdDeferExpr: + case ScopeIdCImport: + case ScopeIdCoroPrelude: + zig_unreachable(); + } + } +} + +static IrInstruction *ir_mark_gen(IrInstruction *instruction) { + instruction->is_gen = true; + return instruction; +} + +static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) { + Scope *scope = inner_scope; + bool is_noreturn = false; + while (scope != outer_scope) { + if (!scope) + return is_noreturn; + + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + if (defer_kind == ReturnKindUnconditional || + (gen_error_defers && defer_kind == ReturnKindError)) + { + AstNode *defer_expr_node = defer_node->data.defer.expr; + Scope *defer_expr_scope = defer_node->data.defer.expr_scope; + IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope, + LValNone, nullptr); + if (defer_expr_value != irb->codegen->invalid_instruction) { + 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)); + } } } scope = scope->parent; @@ -2878,13 +3402,13 @@ static bool exec_is_async(IrExecutable *exec) { } static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value, - bool is_generated_code) + bool is_generated_code, IrInstruction *return_result_loc) { ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value)); bool is_async = exec_is_async(irb->exec); if (!is_async) { - IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value); + IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value, return_result_loc); return_inst->is_gen = is_generated_code; return return_inst; } @@ -2910,7 +3434,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, suspended_block); ir_build_unreachable(irb, scope, node); @@ -2919,7 +3443,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); - ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime); + ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); @@ -2929,10 +3453,176 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode ir_set_cursor_at_end_and_append_block(irb, check_canceled_block); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime); + return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime, nullptr); +} + +static IrInstruction *ir_gen_result(IrBuilder *irb, Scope *scope, AstNode *node, LVal parent_lval, + IrInstruction *parent_result_loc, LVal child_lval, IrInstruction *child_result_loc) +{ + assert(child_result_loc != nullptr); + bool need_copy = (parent_result_loc != nullptr && + parent_result_loc != child_result_loc && child_result_loc->id == IrInstructionIdAllocaSrc); + switch (parent_lval) { + case LValNone: + if (need_copy) { + IrInstruction *result = ir_build_load_ptr(irb, scope, node, child_result_loc, parent_result_loc); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + return result; + } else { + return ir_build_load_ptr(irb, scope, node, child_result_loc, nullptr); + } + case LValPtr: + if (need_copy) { + ir_build_ref(irb, scope, node, child_result_loc, true, false, parent_result_loc); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + return ir_build_const_void(irb, scope, node); + } else { + return child_result_loc; + } + case LValOptional: { + IrInstruction *loaded_ptr = ir_build_load_ptr(irb, scope, node, child_result_loc, nullptr); + if (need_copy) { + IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, child_result_loc, false); + ir_build_load_ptr(irb, scope, node, payload_ptr, parent_result_loc); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + } + return ir_build_test_nonnull(irb, scope, node, loaded_ptr); + } + case LValErrorUnionVal: { + if (need_copy) { + ir_build_unwrap_err_payload(irb, scope, node, child_result_loc, parent_result_loc, false); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + } + IrInstruction *err_ptr = ir_build_error_union_field_error_set(irb, scope, node, child_result_loc, false); + return ir_build_load_ptr(irb, scope, node, err_ptr, nullptr); + } + case LValErrorUnionPtr: + if (need_copy) { + ir_build_unwrap_err_payload(irb, scope, node, child_result_loc, parent_result_loc, false); + ir_build_infer_comptime(irb, scope, node, parent_result_loc, child_result_loc); + } + return ir_build_error_union_field_error_set(irb, scope, node, child_result_loc, false); + } + zig_unreachable(); +} + +static IrInstruction *ensure_result_loc(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ + if (result_loc != nullptr && + (result_loc->value.special != ConstValSpecialStatic || + result_loc->value.data.x_ptr.special != ConstPtrSpecialDiscard)) + { + switch (lval) { + case LValNone: + case LValPtr: + return result_loc; + case LValErrorUnionVal: + case LValErrorUnionPtr: + case LValOptional: + if (result_loc->id == IrInstructionIdAllocaSrc || + result_loc->id == IrInstructionIdFirstArgResultLoc || + result_loc->id == IrInstructionIdFieldPtr) + { + break; + } + if (result_loc->id != IrInstructionIdResultChild) { + return ir_build_result_child(irb, scope, node, result_loc, lval); + } + break; + } + } + return ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "", nullptr); +} + +static IrInstruction *ir_gen_value(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc, IrInstruction *value) +{ + if (value == irb->codegen->invalid_instruction) + return value; + if (instr_is_unreachable(value)) + return value; + if (result_loc != nullptr) { + IrInstruction *store_inst = ir_build_store_ptr(irb, scope, node, result_loc, value); + store_inst->is_gen = value->is_gen || result_loc->is_gen; + switch (lval) { + case LValPtr: + zig_unreachable(); + case LValNone: + return value; + case LValErrorUnionVal: + return ir_build_const_null(irb, scope, node); + case LValErrorUnionPtr: { + IrInstruction *null = ir_build_const_null(irb, scope, node); + return ir_build_ref(irb, scope, node, null, true, false, nullptr); + } + case LValOptional: { + return ir_build_const_bool(irb, scope, node, true); + } + } + zig_unreachable(); + } + switch (lval) { + case LValPtr: + // We needed a pointer to a value, but we got a value. So we create + // an instruction which just makes a pointer of it. + return ir_build_ref(irb, scope, value->source_node, value, false, false, result_loc); + case LValNone: + return value; + case LValOptional: + case LValErrorUnionVal: + case LValErrorUnionPtr: + zig_panic("TODO"); + } + zig_unreachable(); +} + +static IrInstruction *ir_gen_ptr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc, IrInstruction *ptr) +{ + switch (lval) { + case LValPtr: + if (result_loc != nullptr) { + IrInstruction *store_inst = ir_build_store_ptr(irb, scope, node, result_loc, ptr); + store_inst->is_gen = result_loc->is_gen || ptr->is_gen; + } + return ptr; + case LValNone: { + IrInstruction *load_inst = ir_build_load_ptr(irb, scope, node, ptr, result_loc); + load_inst->is_gen = (result_loc != nullptr && result_loc->is_gen) || ptr->is_gen; + return load_inst; + } + case LValErrorUnionPtr: { + // ptr points to an error union; + // result_loc points to the result payload + // must return pointer to the error code + ir_build_unwrap_err_payload(irb, scope, node, ptr, result_loc, false); + return ir_build_error_union_field_error_set(irb, scope, node, ptr, false); + } + case LValErrorUnionVal: { + // ptr points to an error union; + // result_loc points to the result payload + // must return the error code + ir_build_unwrap_err_payload(irb, scope, node, ptr, result_loc, false); + IrInstruction *err_ptr = ir_build_error_union_field_error_set(irb, scope, node, ptr, false); + return ir_build_load_ptr(irb, scope, node, err_ptr, nullptr); + } + case LValOptional: { + // ptr points to an optional; + // result_loc points to the result payload + // must return is_non_null bool + IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, ptr, false); + ir_build_load_ptr(irb, scope, node, payload_ptr, result_loc); + IrInstruction *loaded_ptr = ir_build_load_ptr(irb, scope, node, ptr, nullptr); + return ir_build_test_nonnull(irb, scope, node, loaded_ptr); + } + } + zig_unreachable(); } -static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeReturnExpr); ZigFn *fn_entry = exec_fn_entry(irb->exec); @@ -2957,14 +3647,32 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindUnconditional: { IrInstruction *return_value; + ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + IrInstruction *return_result_loc = nullptr; + LVal expr_lval = LValNone; if (expr_node) { + if (return_type != nullptr) { + if (return_type->id == ZigTypeIdErrorUnion) { + return_result_loc = ir_build_result_return(irb, scope, node); + expr_lval = LValErrorUnionVal; + } else if (return_type->id == ZigTypeIdOptional && handle_is_ptr(return_type)) { + return_result_loc = ir_build_result_return(irb, scope, node); + expr_lval = LValOptional; + } else if (type_has_bits(return_type) && handle_is_ptr(return_type)) { + return_result_loc = ir_build_result_return(irb, scope, node); + } + } // Temporarily set this so that if we return a type it gets the name of the function ZigFn *prev_name_fn = irb->exec->name_fn; irb->exec->name_fn = exec_fn_entry(irb->exec); - return_value = ir_gen_node(irb, expr_node, scope); + return_value = ir_gen_node(irb, expr_node, scope, expr_lval, return_result_loc); irb->exec->name_fn = prev_name_fn; if (return_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; + } else if (return_type->id == ZigTypeIdErrorUnion && + return_type->data.error_union.payload_type == irb->codegen->builtin_types.entry_void) + { + return_value = ir_build_const_null(irb, scope, node); } else { return_value = ir_build_const_void(irb, scope, node); } @@ -2972,14 +3680,23 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, size_t defer_counts[2]; ir_count_defers(irb, scope, outer_scope, defer_counts); bool have_err_defers = defer_counts[ReturnKindError] > 0; - if (have_err_defers || irb->codegen->have_err_ret_tracing) { + if (expr_node != nullptr && expr_lval != LValOptional && + (have_err_defers || irb->codegen->have_err_ret_tracing)) + { IrBasicBlock *err_block = ir_create_basic_block(irb, scope, "ErrRetErr"); IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk"); if (!have_err_defers) { ir_gen_defers_for_block(irb, scope, outer_scope, false); } - IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value); + IrInstruction *is_err; + if (expr_lval == LValNone) { + is_err = ir_build_test_err(irb, scope, node, return_value); + } else if (expr_lval == LValErrorUnionVal) { + is_err = ir_build_test_nonnull(irb, scope, node, return_value); + } else { + zig_unreachable(); + } bool should_inline = ir_should_inline(irb->exec, scope); IrInstruction *is_comptime; @@ -2989,7 +3706,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, is_comptime = ir_build_test_comptime(irb, scope, node, is_err); } - ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime)); + ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime, nullptr)); IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt"); ir_set_cursor_at_end_and_append_block(irb, err_block); @@ -3008,21 +3725,25 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block); - return ir_gen_async_return(irb, scope, node, return_value, false); + return ir_gen_async_return(irb, scope, node, return_value, false, return_result_loc); } else { // generate unconditional defers ir_gen_defers_for_block(irb, scope, outer_scope, false); - return ir_gen_async_return(irb, scope, node, return_value, false); + return ir_gen_async_return(irb, scope, node, return_value, false, return_result_loc); } } case ReturnKindError: { assert(expr_node); - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); - if (err_union_ptr == irb->codegen->invalid_instruction) + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, expr_node, lval, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); - IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_val); + IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, + LValErrorUnionVal, ensured_result_loc); + if (opt_err_code == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *is_err_val = ir_build_test_nonnull(irb, scope, node, opt_err_code); IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); @@ -3033,23 +3754,20 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, } else { is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val); } - ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime)); + ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime, nullptr)); ir_set_cursor_at_end_and_append_block(irb, return_block); + IrInstruction *err_val = ir_build_optional_unwrap_val(irb, scope, node, opt_err_code, false); if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) { - IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr); if (irb->codegen->have_err_ret_tracing && !should_inline) { ir_build_save_err_ret_addr(irb, scope, node); } - ir_gen_async_return(irb, scope, node, err_val, false); + ir_gen_async_return(irb, scope, node, err_val, false, nullptr); } ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false); - if (lval == LValPtr) - return unwrapped_ptr; - else - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + return ir_gen_result(irb, scope, node, lval, result_loc, LValErrorUnionVal, + ensured_result_loc); } } zig_unreachable(); @@ -3065,7 +3783,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 +3798,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; } } } @@ -3131,7 +3849,9 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n return var; } -static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) { +static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node, LVal lval, + IrInstruction *result_loc) +{ assert(block_node->type == NodeTypeBlock); ZigList incoming_values = {0}; @@ -3149,14 +3869,18 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode if (block_node->data.block.statements.length == 0) { // {} - return ir_build_const_void(irb, child_scope, block_node); + return ir_gen_value(irb, parent_scope, block_node, lval, result_loc, + ir_build_const_void(irb, child_scope, block_node)); } if (block_node->data.block.name != nullptr) { + scope_block->result_loc = result_loc; + scope_block->lval = lval; 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; @@ -3164,7 +3888,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { AstNode *statement_node = block_node->data.block.statements.at(i); - IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope); + IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope, LValNone, nullptr); is_continuation_unreachable = instr_is_unreachable(statement_value); if (is_continuation_unreachable) { // keep the last noreturn statement value around in case we need to return it @@ -3174,9 +3898,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 @@ -3214,8 +3938,10 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no inner_scope = create_comptime_scope(irb->codegen, node, scope); } - IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope); - IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_scope); + // All the binary ops operate either on scalar values or comptime values, so + // we do not need a result location. + IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope, LValNone, nullptr); + IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_scope, LValNone, nullptr); if (op1 == irb->codegen->invalid_instruction || op2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -3224,22 +3950,23 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no } static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); - IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + IrInstruction *lvalue = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); + IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, lvalue); if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - ir_build_store_ptr(irb, scope, node, lvalue, rvalue); return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); + // All the assign ops operate on scalar values or comptime values, so + // we do not need a result location. + IrInstruction *lvalue = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; - IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); - IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue, nullptr); + IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (op2 == irb->codegen->invalid_instruction) return op2; IrInstruction *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); @@ -3250,7 +3977,7 @@ static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *no static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); - IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); + IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValNone, nullptr); if (val1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val1_block = irb->current_basic_block; @@ -3267,10 +3994,10 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node // block for when val1 == true (don't even evaluate the second part) IrBasicBlock *true_block = ir_create_basic_block(irb, scope, "BoolOrTrue"); - ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); + ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, false_block); - IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (val2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val2_block = irb->current_basic_block; @@ -3292,7 +4019,7 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); - IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); + IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope, LValNone, nullptr); if (val1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val1_block = irb->current_basic_block; @@ -3309,10 +4036,10 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod // block for when val1 == false (don't even evaluate the second part) IrBasicBlock *false_block = ir_create_basic_block(irb, scope, "BoolAndFalse"); - ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); + ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, true_block); - IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope, LValNone, nullptr); if (val2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrBasicBlock *post_val2_block = irb->current_basic_block; @@ -3331,19 +4058,22 @@ 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, LVal lval, + IrInstruction *old_result_loc) +{ assert(node->type == NodeTypeBinOpExpr); + IrInstruction *new_result_loc = ensure_result_loc(irb, parent_scope, node, lval, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); - if (maybe_ptr == irb->codegen->invalid_instruction) + IrInstruction *is_non_null = ir_gen_node(irb, op1_node, parent_scope, LValOptional, new_result_loc); + if (is_non_null == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr); - IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_val); - IrInstruction *is_comptime; if (ir_should_inline(irb->exec, parent_scope)) { is_comptime = ir_build_const_bool(irb, parent_scope, node, true); @@ -3351,33 +4081,19 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null); } - IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull"); - IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull"); - IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd"); - ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime); + IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OrElseNull"); + IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OrElseEnd"); + ir_build_cond_br(irb, parent_scope, node, is_non_null, end_block, null_block, is_comptime, new_result_loc); ir_set_cursor_at_end_and_append_block(irb, null_block); - IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope); + IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope, LValNone, new_result_loc); if (null_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrBasicBlock *after_null_block = irb->current_basic_block; if (!instr_is_unreachable(null_result)) 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_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); - ir_set_cursor_at_end_and_append_block(irb, end_block); - IrInstruction **incoming_values = allocate(2); - incoming_values[0] = null_result; - incoming_values[1] = unwrapped_payload; - IrBasicBlock **incoming_blocks = allocate(2); - incoming_blocks[0] = after_null_block; - incoming_blocks[1] = after_ok_block; - return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); + return ir_gen_result(irb, parent_scope, node, lval, old_result_loc, LValOptional, new_result_loc); } static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -3386,18 +4102,20 @@ static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, As AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; - IrInstruction *err_set = ir_gen_node(irb, op1_node, parent_scope); + IrInstruction *err_set = ir_gen_node(irb, op1_node, parent_scope, LValNone, nullptr); if (err_set == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope); + IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope, LValNone, nullptr); if (payload == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_build_error_union(irb, parent_scope, node, err_set, payload); } -static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeBinOpExpr); BinOpType bin_op_type = node->data.bin_op_expr.bin_op; @@ -3405,87 +4123,125 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) case BinOpTypeInvalid: zig_unreachable(); case BinOpTypeAssign: - return ir_gen_assign(irb, scope, node); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_assign(irb, scope, node)); case BinOpTypeAssignTimes: - return ir_gen_assign_op(irb, scope, node, IrBinOpMult); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpMult)); case BinOpTypeAssignTimesWrap: - return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap)); case BinOpTypeAssignDiv: - return ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified)); case BinOpTypeAssignMod: - return ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified)); case BinOpTypeAssignPlus: - return ir_gen_assign_op(irb, scope, node, IrBinOpAdd); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpAdd)); case BinOpTypeAssignPlusWrap: - return ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap)); case BinOpTypeAssignMinus: - return ir_gen_assign_op(irb, scope, node, IrBinOpSub); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpSub)); case BinOpTypeAssignMinusWrap: - return ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap)); case BinOpTypeAssignBitShiftLeft: - return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy)); case BinOpTypeAssignBitShiftRight: - return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy)); case BinOpTypeAssignBitAnd: - return ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd)); case BinOpTypeAssignBitXor: - return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBinXor)); case BinOpTypeAssignBitOr: - return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpBinOr)); case BinOpTypeAssignMergeErrorSets: - return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets)); case BinOpTypeBoolOr: - return ir_gen_bool_or(irb, scope, node); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_or(irb, scope, node)); case BinOpTypeBoolAnd: - return ir_gen_bool_and(irb, scope, node); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_and(irb, scope, node)); case BinOpTypeCmpEq: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq)); case BinOpTypeCmpNotEq: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq)); case BinOpTypeCmpLessThan: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan)); case BinOpTypeCmpGreaterThan: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan)); case BinOpTypeCmpLessOrEq: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq)); case BinOpTypeCmpGreaterOrEq: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq)); case BinOpTypeBinOr: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr)); case BinOpTypeBinXor: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor)); case BinOpTypeBinAnd: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd)); case BinOpTypeBitShiftLeft: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy)); case BinOpTypeBitShiftRight: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy)); case BinOpTypeAdd: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd)); case BinOpTypeAddWrap: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap)); case BinOpTypeSub: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpSub); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpSub)); case BinOpTypeSubWrap: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap)); case BinOpTypeMult: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpMult); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpMult)); case BinOpTypeMultWrap: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap)); case BinOpTypeDiv: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified)); case BinOpTypeMod: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified)); case BinOpTypeArrayCat: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat)); case BinOpTypeArrayMult: - return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult)); 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_value(irb, scope, node, lval, result_loc, + ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets)); case BinOpTypeErrorUnion: - return ir_gen_error_union(irb, scope, node); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_error_union(irb, scope, node)); + + case BinOpTypeUnwrapOptional: + return ir_gen_orelse(irb, scope, node, lval, result_loc); } zig_unreachable(); } @@ -3513,13 +4269,29 @@ static IrInstruction *ir_gen_char_lit(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_const_uint(irb, scope, node, node->data.char_literal.value); } -static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeNullLiteral); - return ir_build_const_null(irb, scope, node); + if (result_loc == nullptr) { + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_const_null(irb, scope, node)); + } + switch (lval) { + case LValNone: + case LValPtr: + case LValErrorUnionVal: + case LValErrorUnionPtr: + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_const_null(irb, scope, node)); + case LValOptional: + return ir_build_const_bool(irb, scope, node, false); + } + zig_unreachable(); } -static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ Error err; assert(node->type == NodeTypeSymbol); @@ -3542,28 +4314,36 @@ 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) { - return ir_build_ref(irb, scope, node, value, false, false); - } else { - return value; - } + return ir_gen_value(irb, scope, node, lval, result_loc, value); } ScopeFnDef *crossed_fndef_scope; ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope); if (var) { IrInstruction *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope); - if (lval == LValPtr) - return var_ptr; - else - return ir_build_load_ptr(irb, scope, node, var_ptr); + return ir_gen_ptr(irb, scope, node, lval, result_loc, var_ptr); } Tld *tld = find_decl(irb->codegen, scope, variable_name); - if (tld) - return ir_build_decl_ref(irb, scope, node, tld, lval); + if (tld) { + switch (tld->id) { + case TldIdContainer: + case TldIdCompTime: + zig_unreachable(); + case TldIdVar: { + IrInstruction *var_ptr = ir_build_decl_ref(irb, scope, node, tld); + return ir_gen_ptr(irb, scope, node, lval, result_loc, var_ptr); + } + case TldIdFn: { + IrInstruction *fn_inst = ir_build_decl_ref(irb, scope, node, tld); + return ir_gen_value(irb, scope, node, lval, result_loc, fn_inst); + } + } + zig_unreachable(); + } if (node->owner->any_imports_failed) { // skip the error message since we had a failing import in this file @@ -3577,38 +4357,42 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, return irb->codegen->invalid_instruction; } -static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeArrayAccessExpr); AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; - IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr); + IrInstruction *array_ref_instruction = ir_gen_node(irb, array_ref_node, scope, LValPtr, nullptr); if (array_ref_instruction == irb->codegen->invalid_instruction) return array_ref_instruction; AstNode *subscript_node = node->data.array_access_expr.subscript; - IrInstruction *subscript_instruction = ir_gen_node(irb, subscript_node, scope); + IrInstruction *subscript_instruction = ir_gen_node(irb, subscript_node, scope, LValNone, nullptr); if (subscript_instruction == irb->codegen->invalid_instruction) return subscript_instruction; IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, subscript_instruction, true, PtrLenSingle); - if (lval == LValPtr) - return ptr_instruction; - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_instruction); } -static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeFieldAccessExpr); AstNode *container_ref_node = node->data.field_access_expr.struct_expr; Buf *field_name = node->data.field_access_expr.field_name; - IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr); + IrInstruction *container_ref_instruction = ir_gen_node(irb, container_ref_node, scope, LValPtr, nullptr); if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; - return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name); + IrInstruction *field_ptr = ir_build_field_ptr(irb, scope, node, container_ref_instruction, nullptr, + field_name, false); + return ir_gen_ptr(irb, scope, node, lval, result_loc, field_ptr); } static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) { @@ -3619,20 +4403,19 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * AstNode *op2_node = node->data.fn_call_expr.params.at(2); AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3); - - IrInstruction *type_value = ir_gen_node(irb, type_node, scope); + IrInstruction *type_value = ir_gen_node(irb, type_node, scope, LValNone, nullptr); if (type_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *op1 = ir_gen_node(irb, op1_node, scope); + IrInstruction *op1 = ir_gen_node(irb, op1_node, scope, LValNone, nullptr); if (op1 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *op2 = ir_gen_node(irb, op2_node, scope); + IrInstruction *op2 = ir_gen_node(irb, op2_node, scope, LValNone, nullptr); if (op2 == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *result_ptr = ir_gen_node(irb, result_ptr_node, scope); + IrInstruction *result_ptr = ir_gen_node(irb, result_ptr_node, scope, LValNone, nullptr); if (result_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -3654,7 +4437,9 @@ static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *no zig_unreachable(); } -static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; @@ -3685,102 +4470,102 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdTypeof: { AstNode *arg_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg = ir_gen_node(irb, arg_node, scope); + IrInstruction *arg = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (arg == irb->codegen->invalid_instruction) return arg; IrInstruction *type_of = ir_build_typeof(irb, scope, node, arg); - return ir_lval_wrap(irb, scope, type_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, type_of); } case BuiltinFnIdSetCold: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_cold = ir_build_set_cold(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_cold, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_cold); } case BuiltinFnIdSetRuntimeSafety: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_safety, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_safety); } case BuiltinFnIdSetFloatMode: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_float_mode, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_float_mode); } case BuiltinFnIdSizeof: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, size_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, size_of); } case BuiltinFnIdCtz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, ctz, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ctz); } case BuiltinFnIdPopCount: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *instr = ir_build_pop_count(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, instr, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, instr); } case BuiltinFnIdClz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *clz = ir_build_clz(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, clz, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, clz); } case BuiltinFnIdImport: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *import = ir_build_import(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, import, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, import); } case BuiltinFnIdCImport: { IrInstruction *c_import = ir_build_c_import(irb, scope, node); - return ir_lval_wrap(irb, scope, c_import, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, c_import); } case BuiltinFnIdCInclude: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3790,17 +4575,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *c_include = ir_build_c_include(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, c_include, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, c_include); } case BuiltinFnIdCDefine: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; @@ -3810,12 +4595,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, c_define, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, c_define); } case BuiltinFnIdCUndef: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -3825,17 +4610,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *c_undef = ir_build_c_undef(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, c_undef, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, c_undef); } case BuiltinFnIdCompileErr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *compile_err = ir_build_compile_err(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, compile_err, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, compile_err); } case BuiltinFnIdCompileLog: { @@ -3843,449 +4628,472 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo for (size_t i = 0; i < actual_param_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i); - args[i] = ir_gen_node(irb, arg_node, scope); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (args[i] == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } IrInstruction *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args); - return ir_lval_wrap(irb, scope, compile_log, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, compile_log); } case BuiltinFnIdErrName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *err_name = ir_build_err_name(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, err_name, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, err_name); } case BuiltinFnIdEmbedFile: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *embed_file = ir_build_embed_file(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, embed_file, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, embed_file); } case BuiltinFnIdCmpxchgWeak: case BuiltinFnIdCmpxchgStrong: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); - IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); + IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope, LValNone, nullptr); if (arg3_value == irb->codegen->invalid_instruction) return arg3_value; AstNode *arg4_node = node->data.fn_call_expr.params.at(4); - IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope); + IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope, LValNone, nullptr); if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; AstNode *arg5_node = node->data.fn_call_expr.params.at(5); - IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope); + IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope, LValNone, nullptr); 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); - return ir_lval_wrap(irb, scope, cmpxchg, lval); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, LValNone, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *payload_ptr = (lval == LValOptional) ? ensured_result_loc : + ir_build_result_optional_payload(irb, scope, node, ensured_result_loc, arg0_value, false); + + IrInstruction *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value, arg4_value, arg5_value, payload_ptr, + (builtin_fn->id == BuiltinFnIdCmpxchgWeak)); + + if (lval == LValOptional) + return cmpxchg; + + ir_build_set_nonnull_bit(irb, scope, node, ensured_result_loc, cmpxchg, payload_ptr); + return ir_gen_result(irb, scope, node, lval, result_loc, LValNone, ensured_result_loc); } case BuiltinFnIdFence: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *fence = ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered); - return ir_lval_wrap(irb, scope, fence, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, fence); } case BuiltinFnIdDivExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdDivTrunc: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdDivFloor: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdRem: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdMod: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdSqrt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, ir_sqrt, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_sqrt); } case BuiltinFnIdTruncate: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, truncate, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, truncate); } case BuiltinFnIdIntCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdFloatCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdErrSetCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdFromBytes: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *new_result_loc = ir_build_result_slice_to_bytes_src(irb, scope, node, arg0_value, + ensured_result_loc); + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, new_result_loc); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *result = ir_build_from_bytes(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + ir_build_from_bytes_len_src(irb, scope, node, ensured_result_loc, new_result_loc); + + return ir_gen_result(irb, scope, node, lval, result_loc, LValNone, ensured_result_loc); } case BuiltinFnIdToBytes: { + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *new_result_loc = ir_build_result_bytes_to_slice(irb, scope, node, ensured_result_loc); + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, new_result_loc); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *result = ir_build_to_bytes(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + ir_build_to_bytes_len_src(irb, scope, node, ensured_result_loc, new_result_loc); + + return ir_gen_result(irb, scope, node, lval, result_loc, LValNone, ensured_result_loc); } case BuiltinFnIdIntToFloat: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdFloatToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdErrToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *result = ir_build_err_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdIntToErr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *result = ir_build_int_to_err(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdBoolToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *result = ir_build_bool_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdIntType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, int_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, int_type); } case BuiltinFnIdMemcpy: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; IrInstruction *ir_memcpy = ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, ir_memcpy, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_memcpy); } case BuiltinFnIdMemset: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; IrInstruction *ir_memset = ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, ir_memset, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_memset); } case BuiltinFnIdMemberCount: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *member_count = ir_build_member_count(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, member_count, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, member_count); } case BuiltinFnIdMemberType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *member_type = ir_build_member_type(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, member_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, member_type); } case BuiltinFnIdMemberName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *member_name = ir_build_member_name(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, member_name, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, member_name); } case BuiltinFnIdField: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValPtr, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value); - - if (lval == LValPtr) - return ptr_instruction; - - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, + arg0_value, arg1_value); + return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_instruction); } case BuiltinFnIdTypeInfo: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *type_info = ir_build_type_info(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, type_info, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, type_info); } case BuiltinFnIdBreakpoint: - return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_breakpoint(irb, scope, node)); case BuiltinFnIdReturnAddress: - return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_return_address(irb, scope, node)); case BuiltinFnIdFrameAddress: - return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_frame_address(irb, scope, node)); case BuiltinFnIdHandle: if (!irb->exec->fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition")); @@ -4295,170 +5103,183 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); return irb->codegen->invalid_instruction; } - return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_handle(irb, scope, node)); case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *align_of = ir_build_align_of(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, align_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, align_of); } case BuiltinFnIdAddWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd)); case BuiltinFnIdSubWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub)); case BuiltinFnIdMulWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul)); case BuiltinFnIdShlWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl)); case BuiltinFnIdTypeName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, type_name, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, type_name); } case BuiltinFnIdPanic: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *panic = ir_build_panic(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, panic, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, panic); } case BuiltinFnIdPtrCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, ptr_cast, lval); + IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value); + return ir_gen_value(irb, scope, node, lval, result_loc, ptr_cast); } case BuiltinFnIdBitCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *new_result_loc = ir_build_result_ptr_cast(irb, scope, node, arg0_value, + ensured_result_loc); + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, new_result_loc); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *bit_cast = ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, bit_cast, lval); + ir_build_infer_comptime(irb, scope, node, ensured_result_loc, new_result_loc); + + return ir_gen_result(irb, scope, node, lval, result_loc, LValNone, ensured_result_loc); } case BuiltinFnIdIntToPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *int_to_ptr = ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, int_to_ptr, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, int_to_ptr); } case BuiltinFnIdPtrToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *ptr_to_int = ir_build_ptr_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, ptr_to_int, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ptr_to_int); } case BuiltinFnIdTagName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value); IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag); - return ir_lval_wrap(irb, scope, tag_name, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, tag_name); } case BuiltinFnIdTagType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *tag_type = ir_build_tag_type(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, tag_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, tag_type); } case BuiltinFnIdFieldParentPtr: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr); - return ir_lval_wrap(irb, scope, field_parent_ptr, lval); + IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, + arg0_value, arg1_value, arg2_value, nullptr); + return ir_gen_value(irb, scope, node, lval, result_loc, field_parent_ptr); } case BuiltinFnIdByteOffsetOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, offset_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, offset_of); } case BuiltinFnIdBitOffsetOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, offset_of, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, offset_of); } case BuiltinFnIdInlineCall: case BuiltinFnIdNoInlineCall: @@ -4469,7 +5290,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } AstNode *fn_ref_node = node->data.fn_call_expr.params.at(0); - IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope, LValNone, nullptr); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; @@ -4478,14 +5299,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i + 1); - args[i] = ir_gen_node(irb, arg_node, scope); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr); - return ir_lval_wrap(irb, scope, call, lval); + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, + fn_inline, false, nullptr, nullptr, result_loc, nullptr, lval); } case BuiltinFnIdNewStackCall: { @@ -4495,12 +5316,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } AstNode *new_stack_node = node->data.fn_call_expr.params.at(0); - IrInstruction *new_stack = ir_gen_node(irb, new_stack_node, scope); + IrInstruction *new_stack = ir_gen_node(irb, new_stack_node, scope, LValNone, nullptr); if (new_stack == irb->codegen->invalid_instruction) return new_stack; AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1); - IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope, LValNone, nullptr); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; @@ -4509,291 +5330,314 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction **args = allocate(arg_count); for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i + 2); - args[i] = ir_gen_node(irb, arg_node, scope); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack); - return ir_lval_wrap(irb, scope, call, lval); + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, + false, FnInlineAuto, false, nullptr, new_stack, result_loc, nullptr, lval); } case BuiltinFnIdTypeId: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *type_id = ir_build_type_id(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, type_id, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, type_id); } case BuiltinFnIdShlExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, + arg0_value, arg1_value, true); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdShrExact: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, + arg0_value, arg1_value, true); + return ir_gen_value(irb, scope, node, lval, result_loc, bin_op); } case BuiltinFnIdSetEvalBranchQuota: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_eval_branch_quota); } case BuiltinFnIdAlignCast: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *align_cast = ir_build_align_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, align_cast, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, align_cast); } case BuiltinFnIdOpaqueType: { IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node); - return ir_lval_wrap(irb, scope, opaque_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, opaque_type); } case BuiltinFnIdThis: { IrInstruction *this_inst = ir_gen_this(irb, scope, node); - return ir_lval_wrap(irb, scope, this_inst, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, this_inst); } case BuiltinFnIdSetAlignStack: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_align_stack, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, set_align_stack); } case BuiltinFnIdArgType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, arg_type, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, arg_type); } case BuiltinFnIdExport: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, ir_export, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_export); } case BuiltinFnIdErrorReturnTrace: { - IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null); - return ir_lval_wrap(irb, scope, error_return_trace, lval); + IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, + IrInstructionErrorReturnTrace::Null); + return ir_gen_value(irb, scope, node, lval, result_loc, error_return_trace); } case BuiltinFnIdAtomicRmw: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; AstNode *arg3_node = node->data.fn_call_expr.params.at(3); - IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope); + IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope, LValNone, nullptr); if (arg3_value == irb->codegen->invalid_instruction) return arg3_value; AstNode *arg4_node = node->data.fn_call_expr.params.at(4); - IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope); + IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope, LValNone, nullptr); if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; - return ir_build_atomic_rmw(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, - arg4_value, + IrInstruction *result = ir_build_atomic_rmw(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value, arg4_value, // these 2 values don't mean anything since we passed non-null values for other args AtomicRmwOp_xchg, AtomicOrderMonotonic); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdAtomicLoad: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope, LValNone, nullptr); if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value, + IrInstruction *result = ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value, // this value does not mean anything since we passed non-null values for other arg AtomicOrderMonotonic); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdIntToEnum: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_int_to_enum(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdEnumToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; IrInstruction *result = ir_build_enum_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdBswap: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } case BuiltinFnIdBitReverse: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope, LValNone, nullptr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope, LValNone, nullptr); if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; IrInstruction *result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval); + return ir_gen_value(irb, scope, node, lval, result_loc, result); } } zig_unreachable(); } -static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.is_builtin) - return ir_gen_builtin_fn_call(irb, scope, node, lval); + return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc); AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; - IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope, LValNone, nullptr); if (fn_ref == irb->codegen->invalid_instruction) return fn_ref; + // In pass 1, we can't tell the difference between a function call with a single argument + // and an implicit cast. So if there is only one argument, we emit a special instruction. size_t arg_count = node->data.fn_call_expr.params.length; IrInstruction **args = allocate(arg_count); + bool is_async = node->data.fn_call_expr.is_async; + if (arg_count == 1 && !is_async && result_loc != nullptr) { + AstNode *arg_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg_result_loc = ir_build_first_arg_result_loc(irb, scope, arg_node, result_loc, fn_ref); + args[0] = ir_gen_node(irb, arg_node, scope, LValNone, arg_result_loc); + if (args[0] == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + // In the analysis, this call instruction will be a simple LoadPtr instruction if + // it turns out to be an implicit cast. + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, + FnInlineAuto, false, nullptr, nullptr, result_loc, arg_result_loc, lval); + } + for (size_t i = 0; i < arg_count; i += 1) { AstNode *arg_node = node->data.fn_call_expr.params.at(i); - args[i] = ir_gen_node(irb, arg_node, scope); + args[i] = ir_gen_node(irb, arg_node, scope, LValNone, nullptr); if (args[i] == irb->codegen->invalid_instruction) return args[i]; } - bool is_async = node->data.fn_call_expr.is_async; IrInstruction *async_allocator = nullptr; if (is_async) { if (node->data.fn_call_expr.async_allocator) { - async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope); + async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope, LValNone, nullptr); if (async_allocator == irb->codegen->invalid_instruction) return async_allocator; } } - 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); + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, + is_async, async_allocator, nullptr, result_loc, nullptr, lval); } -static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeIfBoolExpr); - IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); + IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope, LValNone, nullptr); if (condition == irb->codegen->invalid_instruction) - return condition; + return irb->codegen->invalid_instruction; IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { @@ -4809,14 +5653,14 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "Else"); IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "EndIf"); - ir_build_cond_br(irb, scope, condition->source_node, condition, then_block, else_block, is_comptime); + ir_build_cond_br(irb, scope, condition->source_node, condition, then_block, else_block, is_comptime, result_loc); ir_set_cursor_at_end_and_append_block(irb, then_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope, lval, result_loc); 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)); @@ -4824,9 +5668,9 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { - else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); + else_expr_result = ir_gen_node(irb, else_node, subexpr_scope, lval, result_loc); 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); } @@ -4845,32 +5689,17 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { +static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); + IrInstruction *value = ir_gen_node(irb, expr_node, scope, LValNone, nullptr); if (value == irb->codegen->invalid_instruction) return value; return ir_build_un_op(irb, scope, node, op_id, value); } -static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { - return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone); -} - -static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) { - if (lval != LValPtr) - return value; - if (value == irb->codegen->invalid_instruction) - return value; - - // We needed a pointer to a value, but we got a value. So we create - // an instruction which just makes a const pointer of it. - return ir_build_ref(irb, scope, value->source_node, value, false, false); -} - 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 || @@ -4882,14 +5711,14 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode IrInstruction *align_value; if (align_expr != nullptr) { - align_value = ir_gen_node(irb, align_expr, scope); + align_value = ir_gen_node(irb, align_expr, scope, LValNone, nullptr); if (align_value == irb->codegen->invalid_instruction) return align_value; } else { align_value = nullptr; } - IrInstruction *child_type = ir_gen_node(irb, expr_node, scope); + IrInstruction *child_type = ir_gen_node(irb, expr_node, scope, LValNone, nullptr); if (child_type == irb->codegen->invalid_instruction) return child_type; @@ -4927,35 +5756,36 @@ 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, - LVal lval) +static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, + AstNode *expr_node, LVal lval, IrInstruction *old_result_loc) { - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); - if (err_union_ptr == irb->codegen->invalid_instruction) + IrInstruction *new_result_loc = ensure_result_loc(irb, scope, source_node, lval, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, source_node, err_union_ptr, true); - if (payload_ptr == irb->codegen->invalid_instruction) + IrInstruction *opt_err_code = ir_gen_node(irb, expr_node, scope, LValErrorUnionVal, new_result_loc); + if (opt_err_code == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - if (lval == LValPtr) - return payload_ptr; + ir_build_assert_non_error(irb, scope, source_node, opt_err_code); - return ir_build_load_ptr(irb, scope, source_node, payload_ptr); + return ir_gen_result(irb, scope, source_node, lval, old_result_loc, LValErrorUnionVal, new_result_loc); } static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - IrInstruction *value = ir_gen_node(irb, expr_node, scope); + IrInstruction *value = ir_gen_node(irb, expr_node, scope, LValNone, nullptr); if (value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; return ir_build_bool_not(irb, scope, node, value); } -static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypePrefixOpExpr); PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; @@ -4964,34 +5794,53 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod case PrefixOpInvalid: zig_unreachable(); case PrefixOpBoolNot: - return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_not(irb, scope, node)); case PrefixOpBinNot: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot)); case PrefixOpNegation: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation)); case PrefixOpNegationWrap: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap)); case PrefixOpOptional: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, + ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional)); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr), lval); + IrInstruction *inst = ir_gen_node(irb, expr_node, scope, LValPtr, result_loc); + if (inst == irb->codegen->invalid_instruction) + return inst; + if (result_loc == nullptr) { + return ir_gen_value(irb, scope, expr_node, lval, nullptr, inst); + } else { + return ir_gen_result(irb, scope, expr_node, lval, result_loc, LValNone, result_loc); + } } } zig_unreachable(); } -static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *old_result_loc) +{ assert(node->type == NodeTypeContainerInitExpr); + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, old_result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; ContainerInitKind kind = container_init_expr->kind; - IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, scope); + IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, scope, LValNone, nullptr); if (container_type == irb->codegen->invalid_instruction) return container_type; if (kind == ContainerInitKindStruct) { + IrInstruction *new_result_loc = ir_build_result_cast(irb, scope, node, container_type, ensured_result_loc); + size_t field_count = container_init_expr->entries.length; IrInstructionContainerInitFieldsField *fields = allocate(field_count); for (size_t i = 0; i < field_count; i += 1) { @@ -4999,28 +5848,39 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A assert(entry_node->type == NodeTypeStructValueField); Buf *name = entry_node->data.struct_val_field.name; + IrInstruction *field_result_loc = ir_build_field_ptr(irb, scope, entry_node, new_result_loc, + container_type, name, true); + AstNode *expr_node = entry_node->data.struct_val_field.expr; - IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope); + IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope, LValNone, field_result_loc); if (expr_value == irb->codegen->invalid_instruction) return expr_value; fields[i].name = name; - fields[i].value = expr_value; + fields[i].result_loc = field_result_loc; fields[i].source_node = entry_node; } - return ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields); + ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields, new_result_loc); + return ir_gen_result(irb, scope, node, lval, old_result_loc, LValNone, ensured_result_loc); } else if (kind == ContainerInitKindArray) { - size_t item_count = container_init_expr->entries.length; - IrInstruction **values = allocate(item_count); - for (size_t i = 0; i < item_count; i += 1) { + size_t elem_count = container_init_expr->entries.length; + IrInstruction *array_type = ir_build_infer_array_type(irb, scope, node, container_type, elem_count); + IrInstruction *new_result_loc = ir_build_result_cast(irb, scope, node, array_type, ensured_result_loc); + + IrInstruction **result_locs = allocate(elem_count); + for (size_t i = 0; i < elem_count; i += 1) { AstNode *expr_node = container_init_expr->entries.at(i); - IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope); + IrInstruction *index_value = ir_build_const_usize(irb, scope, expr_node, i); + IrInstruction *elem_result_loc = ir_mark_gen(ir_build_elem_ptr(irb, scope, expr_node, new_result_loc, + index_value, true, PtrLenSingle)); + IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope, LValNone, elem_result_loc); if (expr_value == irb->codegen->invalid_instruction) return expr_value; - values[i] = expr_value; + result_locs[i] = elem_result_loc; } - return ir_build_container_init_list(irb, scope, node, container_type, item_count, values); + ir_build_container_init_list(irb, scope, node, array_type, elem_count, result_locs, new_result_loc); + return ir_gen_result(irb, scope, node, lval, old_result_loc, LValNone, ensured_result_loc); } else { zig_unreachable(); } @@ -5038,7 +5898,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod IrInstruction *type_instruction; if (variable_declaration->type != nullptr) { - type_instruction = ir_gen_node(irb, variable_declaration->type, scope); + type_instruction = ir_gen_node(irb, variable_declaration->type, scope, LValNone, nullptr); if (type_instruction == irb->codegen->invalid_instruction) return type_instruction; } else { @@ -5053,18 +5913,18 @@ 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; } IrInstruction *align_value = nullptr; if (variable_declaration->align_expr != nullptr) { - align_value = ir_gen_node(irb, variable_declaration->align_expr, scope); + align_value = ir_gen_node(irb, variable_declaration->align_expr, scope, LValNone, nullptr); if (align_value == irb->codegen->invalid_instruction) return align_value; } @@ -5074,20 +5934,39 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); } + IrInstruction *alloca = ir_build_alloca_src(irb, scope, node, nullptr, align_value, + buf_ptr(variable_declaration->symbol), is_comptime); + + IrInstruction *cast_result_loc = nullptr; + if (type_instruction != nullptr) { + cast_result_loc = ir_build_first_arg_result_loc(irb, scope, node, alloca, type_instruction); + } else { + cast_result_loc = alloca; + } + // Temporarily set the name of the IrExecutable to the VariableDeclaration // so that the struct or enum from the init expression inherits the name. Buf *old_exec_name = irb->exec->name; irb->exec->name = variable_declaration->symbol; - IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope); + IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope, LValNone, cast_result_loc); irb->exec->name = old_exec_name; 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); + if (type_instruction != nullptr) { + IrInstruction **args = allocate(1); + args[0] = ir_gen_result(irb, scope, node, LValNone, alloca, LValNone, cast_result_loc); + ir_build_call(irb, scope, node, nullptr, type_instruction, 1, args, false, + FnInlineAuto, false, nullptr, nullptr, alloca, cast_result_loc, LValNone); + } + + return ir_build_var_decl_src(irb, scope, node, var, nullptr, align_value, alloca); } -static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeWhileExpr); AstNode *continue_expr_node = node->data.while_expr.continue_expr; @@ -5122,25 +6001,32 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } else { payload_scope = subexpr_scope; } - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); - if (err_val_ptr == irb->codegen->invalid_instruction) - return err_val_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); - IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val); + IrInstruction *err_union_ptr = ir_gen_node(irb, node->data.while_expr.condition, subexpr_scope, + LValPtr, nullptr); + if (err_union_ptr == irb->codegen->invalid_instruction) + return err_union_ptr; + + IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, + node->data.while_expr.condition, err_union_ptr, false); + IrInstruction *opt_err_code = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, + ptr_opt_err_code, nullptr); + IrInstruction *is_err = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, opt_err_code); IrBasicBlock *after_cond_block = irb->current_basic_block; - IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); + assert(else_node != nullptr); + IrInstruction *void_else_result = ir_mark_gen(ir_build_const_void(irb, scope, node)); + if (!instr_is_unreachable(is_err)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err, - else_block, body_block, is_comptime)); + else_block, body_block, is_comptime, result_loc)); } ir_set_cursor_at_end_and_append_block(irb, body_block); if (var_symbol) { - IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node, - 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); + IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node, + err_union_ptr, nullptr, false); + IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ? + ir_build_ref(irb, payload_scope, symbol_node, payload_ptr, true, false, nullptr) : payload_ptr; + ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_ptr); } ZigList incoming_values = {0}; @@ -5150,10 +6036,12 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; + loop_scope->result_loc = result_loc; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; - IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); + IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, + lval, result_loc); if (body_result == irb->codegen->invalid_instruction) return body_result; @@ -5164,7 +6052,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope); + IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope, LValNone, nullptr); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) @@ -5172,21 +6060,22 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } ir_set_cursor_at_end_and_append_block(irb, else_block); - assert(else_node != nullptr); // TODO make it an error to write to error variable AstNode *err_symbol_node = else_node; // TODO make more accurate ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, - true, false, false, is_comptime); + true, true, 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); + IrInstruction *unwrapped_err_code_ptr = ir_build_optional_unwrap_ptr(irb, err_scope, err_symbol_node, + ptr_opt_err_code, false); + ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, unwrapped_err_code_ptr); - IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope); + IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope, lval, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + IrBasicBlock *after_else_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { @@ -5207,23 +6096,24 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); Scope *child_scope = payload_var->child_scope; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); + IrInstruction *maybe_val_ptr = ir_gen_node(irb, node->data.while_expr.condition, subexpr_scope, + LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(is_non_null)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null, - body_block, else_block, is_comptime)); + body_block, else_block, is_comptime, result_loc)); } 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_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); + IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false); + IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ? + ir_build_ref(irb, child_scope, symbol_node, payload_ptr, true, false, nullptr) : payload_ptr; + ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_ptr); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5232,10 +6122,12 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; + loop_scope->result_loc = result_loc; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; - IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); + IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, + lval, result_loc); if (body_result == irb->codegen->invalid_instruction) return body_result; @@ -5246,7 +6138,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, child_scope); + IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, child_scope, LValNone, nullptr); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) @@ -5257,13 +6149,14 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, scope); + else_result = ir_gen_node(irb, else_node, scope, lval, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } IrBasicBlock *after_else_block = irb->current_basic_block; + ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); @@ -5276,14 +6169,14 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else { ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope); + IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope, LValNone, nullptr); if (cond_val == irb->codegen->invalid_instruction) return cond_val; IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); if (!instr_is_unreachable(cond_val)) { ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val, - body_block, else_block, is_comptime)); + body_block, else_block, is_comptime, result_loc)); } ir_set_cursor_at_end_and_append_block(irb, body_block); @@ -5297,10 +6190,12 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; + loop_scope->result_loc = result_loc; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; - IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); + IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base, + lval, result_loc); if (body_result == irb->codegen->invalid_instruction) return body_result; @@ -5311,7 +6206,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope); + IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope, LValNone, nullptr); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) @@ -5322,13 +6217,14 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, subexpr_scope); + else_result = ir_gen_node(irb, else_node, subexpr_scope, lval, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); } IrBasicBlock *after_else_block = irb->current_basic_block; + ir_set_cursor_at_end_and_append_block(irb, end_block); if (else_result) { incoming_blocks.append(after_else_block); @@ -5337,12 +6233,13 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n incoming_blocks.append(after_cond_block); incoming_values.append(void_else_result); } - return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } } -static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeForExpr); AstNode *array_node = node->data.for_expr.array_expr; @@ -5357,11 +6254,11 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo } assert(elem_node->type == NodeTypeSymbol); - IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPtr); + IrInstruction *array_val_ptr = ir_gen_node(irb, array_node, parent_scope, LValPtr, nullptr); if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; - IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr); + IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr, nullptr); IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_val); IrInstruction *elem_var_type; @@ -5379,10 +6276,6 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime); 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); - IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); - AstNode *index_var_source_node; ZigVar *index_var; if (index_node) { @@ -5396,10 +6289,12 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo child_scope = index_var->child_scope; IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *index_alloca = ir_build_alloca_src(irb, child_scope, node, usize, nullptr, "for_index", + is_comptime); 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); - IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); + ir_build_store_ptr(irb, child_scope, index_var_source_node, index_alloca, zero); + ir_build_var_decl_src(irb, child_scope, index_var_source_node, index_var, usize, nullptr, index_alloca); IrBasicBlock *cond_block = ir_create_basic_block(irb, child_scope, "ForCond"); @@ -5409,26 +6304,23 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrBasicBlock *continue_block = ir_create_basic_block(irb, child_scope, "ForContinue"); Buf *len_field_name = buf_create_from_str("len"); - IrInstruction *len_ref = ir_build_field_ptr(irb, child_scope, node, array_val_ptr, len_field_name); - IrInstruction *len_val = ir_build_load_ptr(irb, child_scope, node, len_ref); + IrInstruction *len_ref = ir_build_field_ptr(irb, child_scope, node, array_val_ptr, nullptr, + len_field_name, false); + IrInstruction *len_val = ir_build_load_ptr(irb, child_scope, node, len_ref, nullptr); ir_build_br(irb, child_scope, node, cond_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr); + IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_alloca, nullptr); IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); IrBasicBlock *after_cond_block = irb->current_basic_block; IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); - ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime)); + ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime, result_loc)); ir_set_cursor_at_end_and_append_block(irb, body_block); IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false, PtrLenSingle); - IrInstruction *elem_val; - if (node->data.for_expr.elem_is_ptr) { - elem_val = elem_ptr; - } else { - elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr); - } - ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val)); + IrInstruction *elem_var_ptr = node->data.for_expr.elem_is_ptr ? + ir_build_ref(irb, child_scope, elem_node, elem_ptr, true, false, nullptr) : elem_ptr; + ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, elem_var_ptr); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5436,24 +6328,25 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; + loop_scope->result_loc = result_loc; loop_scope->incoming_blocks = &incoming_blocks; loop_scope->incoming_values = &incoming_values; - IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base); + IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base, lval, result_loc); if (!instr_is_unreachable(body_result)) ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false); - ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)); + ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_alloca, new_index_val)); ir_build_br(irb, child_scope, node, cond_block, is_comptime); IrInstruction *else_result = nullptr; if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, parent_scope); + else_result = ir_gen_node(irb, else_node, parent_scope, lval, result_loc); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) @@ -5469,7 +6362,6 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo incoming_blocks.append(after_cond_block); incoming_values.append(void_else_value); } - return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } @@ -5512,11 +6404,11 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n return irb->codegen->invalid_instruction; } - IrInstruction *size_value = ir_gen_node(irb, size_node, comptime_scope); + IrInstruction *size_value = ir_gen_node(irb, size_node, comptime_scope, LValNone, nullptr); if (size_value == irb->codegen->invalid_instruction) return size_value; - IrInstruction *child_type = ir_gen_node(irb, child_type_node, comptime_scope); + IrInstruction *child_type = ir_gen_node(irb, child_type_node, comptime_scope, LValNone, nullptr); if (child_type == irb->codegen->invalid_instruction) return child_type; @@ -5524,14 +6416,14 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n } else { IrInstruction *align_value; if (align_expr != nullptr) { - align_value = ir_gen_node(irb, align_expr, comptime_scope); + align_value = ir_gen_node(irb, align_expr, comptime_scope, LValNone, nullptr); if (align_value == irb->codegen->invalid_instruction) return align_value; } else { align_value = nullptr; } - IrInstruction *child_type = ir_gen_node(irb, child_type_node, comptime_scope); + IrInstruction *child_type = ir_gen_node(irb, child_type_node, comptime_scope, LValNone, nullptr); if (child_type == irb->codegen->invalid_instruction) return child_type; @@ -5546,7 +6438,7 @@ static IrInstruction *ir_gen_promise_type(IrBuilder *irb, Scope *scope, AstNode IrInstruction *payload_type_value = nullptr; if (payload_type_node != nullptr) { - payload_type_value = ir_gen_node(irb, payload_type_node, scope); + payload_type_value = ir_gen_node(irb, payload_type_node, scope, LValNone, nullptr); if (payload_type_value == irb->codegen->invalid_instruction) return payload_type_value; @@ -5578,7 +6470,7 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod if (asm_output->return_type) { return_count += 1; - IrInstruction *return_type = ir_gen_node(irb, asm_output->return_type, scope); + IrInstruction *return_type = ir_gen_node(irb, asm_output->return_type, scope, LValNone, nullptr); if (return_type == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; if (return_count > 1) { @@ -5612,7 +6504,7 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod } for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) { AsmInput *asm_input = node->data.asm_expr.input_list.at(i); - IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope); + IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope, LValNone, nullptr); if (input_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5622,8 +6514,10 @@ 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, LVal lval, + IrInstruction *result_loc) +{ + assert(node->type == NodeTypeIfOptional); Buf *var_symbol = node->data.test_expr.var_symbol; AstNode *expr_node = node->data.test_expr.target_node; @@ -5631,11 +6525,11 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no AstNode *else_node = node->data.test_expr.else_node; bool var_is_ptr = node->data.test_expr.var_is_ptr; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); + IrInstruction *maybe_val_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, nullptr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; - IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); + IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr, nullptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val); IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); @@ -5648,7 +6542,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no } else { is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null); } - ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, is_comptime, result_loc); ir_set_cursor_at_end_and_append_block(irb, then_block); @@ -5661,14 +6555,15 @@ 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_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); + IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false); + IrInstruction *var_ptr = var_is_ptr ? + ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false, nullptr) : payload_ptr; + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_ptr); var_scope = var->child_scope; } else { var_scope = subexpr_scope; } - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, lval, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; @@ -5678,7 +6573,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { - else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); + else_expr_result = ir_gen_node(irb, else_node, subexpr_scope, lval, result_loc); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { @@ -5699,7 +6594,9 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeIfErrorExpr); AstNode *target_node = node->data.if_err_expr.target_node; @@ -5710,40 +6607,47 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Buf *var_symbol = node->data.if_err_expr.var_symbol; Buf *err_symbol = node->data.if_err_expr.err_symbol; - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); - if (err_val_ptr == irb->codegen->invalid_instruction) - return err_val_ptr; + IrInstruction *err_union_ptr = ir_gen_node(irb, target_node, scope, LValPtr, nullptr); + if (err_union_ptr == irb->codegen->invalid_instruction) + return err_union_ptr; - IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); - IrInstruction *is_err = ir_build_test_err(irb, scope, node, err_val); + IrInstruction *ptr_opt_err_code = ir_build_error_union_field_error_set(irb, scope, node, err_union_ptr, false); + IrInstruction *opt_err_code = ir_build_load_ptr(irb, scope, node, ptr_opt_err_code, nullptr); + IrInstruction *is_err = ir_build_test_nonnull(irb, scope, node, opt_err_code); - IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "TryOk"); - IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse"); - IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd"); + IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "IfErrThen"); + IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "IfErrElse"); + IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "IfErrEnd"); bool force_comptime = ir_should_inline(irb->exec, scope); - IrInstruction *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); - ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime); + IrInstruction *is_comptime = force_comptime ? + ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); + ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime, result_loc); ir_set_cursor_at_end_and_append_block(irb, ok_block); Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); Scope *var_scope; if (var_symbol) { + IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_union_ptr, + nullptr, false); + IrInstruction *var_type = nullptr; bool is_shadowable = false; - IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val); + IrInstruction *var_is_comptime = force_comptime ? + ir_build_const_bool(irb, subexpr_scope, node, true) : + ir_build_test_comptime(irb, subexpr_scope, node, payload_ptr); ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); - 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); + IrInstruction *var_ptr = var_is_ptr ? + ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false, nullptr) : payload_ptr; + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_ptr); var_scope = var->child_scope; } else { var_scope = subexpr_scope; } - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope, lval, result_loc); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; @@ -5756,19 +6660,19 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * if (else_node) { Scope *err_var_scope; if (err_symbol) { - IrInstruction *var_type = nullptr; bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, node, subexpr_scope, 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); + IrInstruction *unwrapped_err_code_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, + ptr_opt_err_code, false); + ir_build_var_decl_src(irb, subexpr_scope, node, var, nullptr, nullptr, unwrapped_err_code_ptr); err_var_scope = var->child_scope; } else { err_var_scope = subexpr_scope; } - else_expr_result = ir_gen_node(irb, else_node, err_var_scope); + else_expr_result = ir_gen_node(irb, else_node, err_var_scope, lval, result_loc); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { @@ -5792,7 +6696,8 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *var_is_comptime, IrInstruction *target_value_ptr, IrInstruction *prong_value, - ZigList *incoming_blocks, ZigList *incoming_values) + ZigList *incoming_blocks, ZigList *incoming_values, + LVal lval, IrInstruction *result_loc) { assert(switch_node->type == NodeTypeSwitchExpr); assert(prong_node->type == NodeTypeSwitchProng); @@ -5805,25 +6710,23 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit Buf *var_name = var_symbol_node->data.symbol_expr.symbol; bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr; + IrInstruction *var_type = nullptr; // infer the type bool is_shadowable = false; bool is_const = true; ZigVar *var = ir_create_var(irb, var_symbol_node, scope, var_name, is_const, is_const, is_shadowable, var_is_comptime); + + IrInstruction *payload_ptr = (prong_value == nullptr) ? target_value_ptr : + ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); + IrInstruction *var_ptr = var_is_ptr ? + ir_build_ref(irb, scope, var_symbol_node, payload_ptr, true, false, nullptr) : payload_ptr; + ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, var_ptr); child_scope = var->child_scope; - IrInstruction *var_value; - if (prong_value) { - IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value); - var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, var_symbol_node, var_ptr_value); - } else { - 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); } else { child_scope = scope; } - IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope); + IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope, lval, result_loc); if (expr_result == irb->codegen->invalid_instruction) return false; if (!instr_is_unreachable(expr_result)) @@ -5833,11 +6736,13 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit return true; } -static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *result_loc) +{ assert(node->type == NodeTypeSwitchExpr); AstNode *target_node = node->data.switch_expr.expr; - IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); + IrInstruction *target_value_ptr = ir_gen_node(irb, target_node, scope, LValPtr, nullptr); if (target_value_ptr == irb->codegen->invalid_instruction) return target_value_ptr; IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); @@ -5882,7 +6787,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * IrBasicBlock *prev_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, else_block); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) + is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values, + lval, result_loc)) { return irb->codegen->invalid_instruction; } @@ -5897,11 +6803,11 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * AstNode *start_node = item_node->data.switch_range.start; AstNode *end_node = item_node->data.switch_range.end; - IrInstruction *start_value = ir_gen_node(irb, start_node, comptime_scope); + IrInstruction *start_value = ir_gen_node(irb, start_node, comptime_scope, LValNone, nullptr); if (start_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *end_value = ir_gen_node(irb, end_node, comptime_scope); + IrInstruction *end_value = ir_gen_node(irb, end_node, comptime_scope, LValNone, nullptr); if (end_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5921,7 +6827,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ok_bit = both_ok; } } else { - IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope); + IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope, LValNone, nullptr); if (item_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5945,11 +6851,12 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * assert(ok_bit); assert(last_item_node); ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit, range_block_yes, - range_block_no, is_comptime)); + range_block_no, is_comptime, result_loc)); ir_set_cursor_at_end_and_append_block(irb, range_block_yes); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) + is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values, + lval, result_loc)) { return irb->codegen->invalid_instruction; } @@ -5974,7 +6881,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); assert(item_node->type != NodeTypeSwitchRange); - IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope); + IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope, LValNone, nullptr); if (item_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5993,7 +6900,8 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * IrBasicBlock *prev_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, prong_block); if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values)) + is_comptime, var_is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values, + lval, result_loc)) { return irb->codegen->invalid_instruction; } @@ -6002,13 +6910,14 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * } - IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length, - else_prong != nullptr); + IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, + check_ranges.items, check_ranges.length, else_prong != nullptr); if (cases.length == 0) { ir_build_br(irb, scope, node, else_block, is_comptime); } else { - ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime, switch_prongs_void); + ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, + is_comptime, switch_prongs_void, result_loc); } if (!else_prong) { @@ -6025,14 +6934,18 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * } } -static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, + LVal lval, IrInstruction *result_loc) +{ assert(node->type == NodeTypeCompTime); Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope); - return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval); + return ir_gen_node(irb, node->data.comptime_expr.expr, child_scope, lval, result_loc); } -static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { +static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, + ScopeBlock *block_scope) +{ IrInstruction *is_comptime; if (ir_should_inline(irb->exec, break_scope)) { is_comptime = ir_build_const_bool(irb, break_scope, node, true); @@ -6042,7 +6955,7 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop IrInstruction *result_value; if (node->data.break_expr.expr) { - result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope); + result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope, block_scope->lval, block_scope->result_loc); if (result_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { @@ -6112,7 +7025,7 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode * IrInstruction *result_value; if (node->data.break_expr.expr) { - result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope); + result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope, LValNone, loop_scope->result_loc); if (result_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { @@ -6200,35 +7113,44 @@ static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode return ir_build_const_void(irb, parent_scope, node); } -static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, + IrInstruction *old_result_loc) +{ assert(node->type == NodeTypeSliceExpr); + IrInstruction *new_result_loc = ensure_result_loc(irb, scope, node, lval, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + AstNodeSliceExpr *slice_expr = &node->data.slice_expr; AstNode *array_node = slice_expr->array_ref_expr; AstNode *start_node = slice_expr->start; AstNode *end_node = slice_expr->end; - IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr); + IrInstruction *ptr_value = ir_gen_node(irb, array_node, scope, LValPtr, nullptr); if (ptr_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *start_value = ir_gen_node(irb, start_node, scope); + IrInstruction *start_value = ir_gen_node(irb, start_node, scope, LValNone, nullptr); if (start_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *end_value; if (end_node) { - end_value = ir_gen_node(irb, end_node, scope); + end_value = ir_gen_node(irb, end_node, scope, LValNone, nullptr); if (end_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } else { end_value = nullptr; } - return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true); + ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true, new_result_loc); + return ir_gen_result(irb, scope, node, lval, old_result_loc, LValNone, new_result_loc); } -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, LVal lval, + IrInstruction *old_result_loc) +{ assert(node->type == NodeTypeUnwrapErrorExpr); AstNode *op1_node = node->data.unwrap_err_expr.op1; @@ -6242,16 +7164,31 @@ 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, lval, old_result_loc); } + IrInstruction *new_result_loc; + IrInstruction *ptr_opt_err_code; + IrInstruction *opt_err_code; + if (var_node) { + new_result_loc = ensure_result_loc(irb, parent_scope, node, lval, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); - if (err_union_ptr == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; + ptr_opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnionPtr, new_result_loc); + if (ptr_opt_err_code == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + opt_err_code = ir_build_load_ptr(irb, parent_scope, node, ptr_opt_err_code, nullptr); + } else { + new_result_loc = ensure_result_loc(irb, parent_scope, node, lval, old_result_loc); + if (new_result_loc == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; - IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr); - IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_val); + opt_err_code = ir_gen_node(irb, op1_node, parent_scope, LValErrorUnionVal, new_result_loc); + if (opt_err_code == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + } + IrInstruction *is_err = ir_build_test_nonnull(irb, parent_scope, node, opt_err_code); IrInstruction *is_comptime; if (ir_should_inline(irb->exec, parent_scope)) { @@ -6260,10 +7197,9 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err); } - IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk"); - IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError"); - IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd"); - ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime); + IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "CatchError"); + IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "CatchEnd"); + ir_build_cond_br(irb, parent_scope, node, is_err, err_block, end_block, is_comptime, new_result_loc); ir_set_cursor_at_end_and_append_block(irb, err_block); Scope *err_scope; @@ -6275,32 +7211,20 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN ZigVar *var = ir_create_var(irb, node, parent_scope, var_name, 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); + IrInstruction *unwrapped_err_code_ptr = ir_build_optional_unwrap_ptr(irb, err_scope, var_node, + ptr_opt_err_code, false); + ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, unwrapped_err_code_ptr); } else { err_scope = parent_scope; } - IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope); + IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope, LValNone, new_result_loc); if (err_result == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrBasicBlock *after_err_block = irb->current_basic_block; if (!instr_is_unreachable(err_result)) ir_mark_gen(ir_build_br(irb, err_scope, node, end_block, is_comptime)); - ir_set_cursor_at_end_and_append_block(irb, ok_block); - IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, parent_scope, node, err_union_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); - ir_set_cursor_at_end_and_append_block(irb, end_block); - IrInstruction **incoming_values = allocate(2); - incoming_values[0] = err_result; - incoming_values[1] = unwrapped_payload; - IrBasicBlock **incoming_blocks = allocate(2); - incoming_blocks[0] = after_err_block; - incoming_blocks[1] = after_ok_block; - return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); + return ir_gen_result(irb, parent_scope, node, lval, old_result_loc, LValErrorUnionVal, new_result_loc); } static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) { @@ -6312,7 +7236,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; } @@ -6498,7 +7423,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo } if (param_node->data.param_decl.var_token == nullptr) { AstNode *type_node = param_node->data.param_decl.type; - IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope); + IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope, LValNone, nullptr); if (type_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; param_types[i] = type_value; @@ -6509,7 +7434,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *align_value = nullptr; if (node->data.fn_proto.align_expr != nullptr) { - align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope); + align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope, LValNone, nullptr); if (align_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } @@ -6519,7 +7444,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo if (node->data.fn_proto.return_type == nullptr) { return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void); } else { - return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); + return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope, LValNone, nullptr); if (return_type == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } @@ -6532,7 +7457,8 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *async_allocator_type_value = nullptr; if (node->data.fn_proto.async_allocator_type != nullptr) { - async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope); + async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope, + LValNone, nullptr); if (async_allocator_type_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; } @@ -6562,11 +7488,11 @@ 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, - atomic_state_field_name); + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, + atomic_state_field_name, false); // set the is_canceled bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, @@ -6575,12 +7501,12 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false); - ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, post_return_block); if (cancel_awaited) { @@ -6588,7 +7514,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode } else { IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); - ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime, nullptr); } ir_set_cursor_at_end_and_append_block(irb, pre_return_block); @@ -6598,7 +7524,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode } else { IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime, nullptr); } } else { ir_build_br(irb, scope, node, done_block, is_comptime); @@ -6615,7 +7541,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeCancel); - IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope, LValNone, nullptr); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6640,11 +7566,11 @@ 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, - atomic_state_field_name); + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, + atomic_state_field_name, false); // clear the is_suspended bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, @@ -6653,12 +7579,12 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime, nullptr); ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); ir_build_unreachable(irb, scope, node); @@ -6674,7 +7600,7 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeResume); - IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope, LValNone, nullptr); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6684,7 +7610,7 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAwaitExpr); - IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope, LValNone, nullptr); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6711,12 +7637,14 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst); Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); - IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); + IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, + result_ptr_field_name, false); if (irb->codegen->have_err_ret_tracing) { IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); - IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); + IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, err_ret_trace_ptr_field_name, false); ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } @@ -6738,11 +7666,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n 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, - atomic_state_field_name); + nullptr, atomic_state_field_name, false); IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); - IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 @@ -6756,9 +7683,10 @@ 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); - 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 *result_var_alloca = ir_build_alloca_src(irb, scope, node, promise_result_type, nullptr, "", + nullptr); + ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, result_var_alloca); + ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, result_var_alloca); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle); @@ -6769,7 +7697,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); - ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false); + ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, already_awaited_block); ir_build_unreachable(irb, scope, node); @@ -6779,10 +7707,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); - ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); + ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, cancel_target_block); ir_build_cancel(irb, scope, node, target_inst); @@ -6791,16 +7719,18 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); - IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name); + IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, err_ret_trace_field_name, false); IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr); } Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); - IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); + IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, + result_field_name, false); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. - IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr); - ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result); + IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr, nullptr); + ir_build_store_ptr(irb, scope, node, result_var_alloca, no_suspend_result); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, merge_block, const_bool_false); @@ -6811,7 +7741,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false); IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false); + ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, my_suspended_block); ir_build_unreachable(irb, scope, node); @@ -6819,7 +7749,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block); IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false); IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false); + ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, do_suspend_block); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); @@ -6830,7 +7760,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = destroy_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false, nullptr); + 2, cases, const_bool_false, nullptr, nullptr); ir_set_cursor_at_end_and_append_block(irb, destroy_block); ir_gen_cancel_target(irb, scope, node, target_inst, false, true); @@ -6844,7 +7774,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false); IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false); IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false); - ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false); + ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr); @@ -6853,13 +7783,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, do_defers_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); - ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false)); + ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false, nullptr)); ir_set_cursor_at_end_and_append_block(irb, resume_block); ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - return ir_build_load_ptr(irb, scope, node, my_result_var_ptr); + return ir_build_load_ptr(irb, scope, node, result_var_alloca, nullptr); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -6920,13 +7850,13 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false); + ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, canceled_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrBasicBlock *post_canceled_block = irb->current_basic_block; - ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false); + ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); @@ -6937,7 +7867,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false); + ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, suspended_block); ir_build_unreachable(irb, parent_scope, node); @@ -6952,7 +7882,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod suspend_scope->resume_block = resume_block; child_scope = &suspend_scope->base; IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle); - ir_gen_node(irb, node->data.suspend.block, child_scope); + ir_gen_node(irb, node->data.suspend.block, child_scope, LValNone, nullptr); suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false)); } @@ -6962,7 +7892,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); cases[1].block = canceled_block; ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false, nullptr)); + 2, cases, const_bool_false, nullptr, nullptr)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); IrBasicBlock **incoming_blocks = allocate(2); @@ -6973,14 +7903,14 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod incoming_values[1] = const_bool_false; IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); - ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false)); + ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false, nullptr)); ir_set_cursor_at_end_and_append_block(irb, resume_block); return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); } -static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, - LVal lval) +static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, + IrInstruction *result_loc) { assert(scope); switch (node->type) { @@ -6994,134 +7924,155 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeTestDecl: zig_unreachable(); case NodeTypeBlock: - return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval); + return ir_gen_block(irb, scope, node, lval, result_loc); case NodeTypeGroupedExpr: - return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval); + return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc); case NodeTypeBinOpExpr: - return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval); + return ir_gen_bin_op(irb, scope, node, lval, result_loc); case NodeTypeIntLiteral: - return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_int_lit(irb, scope, node)); case NodeTypeFloatLiteral: - return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_float_lit(irb, scope, node)); case NodeTypeCharLiteral: - return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_char_lit(irb, scope, node)); case NodeTypeSymbol: - return ir_gen_symbol(irb, scope, node, lval); + return ir_gen_symbol(irb, scope, node, lval, result_loc); case NodeTypeFnCallExpr: - return ir_gen_fn_call(irb, scope, node, lval); + return ir_gen_fn_call(irb, scope, node, lval, result_loc); case NodeTypeIfBoolExpr: - return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); + return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc); case NodeTypePrefixOpExpr: - return ir_gen_prefix_op_expr(irb, scope, node, lval); + return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc); case NodeTypeContainerInitExpr: - return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval); + return ir_gen_container_init_expr(irb, scope, node, lval, result_loc); case NodeTypeVariableDeclaration: - return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_var_decl(irb, scope, node)); case NodeTypeWhileExpr: - return ir_lval_wrap(irb, scope, ir_gen_while_expr(irb, scope, node), lval); + return ir_gen_while_expr(irb, scope, node, lval, result_loc); case NodeTypeForExpr: - return ir_lval_wrap(irb, scope, ir_gen_for_expr(irb, scope, node), lval); + return ir_gen_for_expr(irb, scope, node, lval, result_loc); case NodeTypeArrayAccessExpr: - return ir_gen_array_access(irb, scope, node, lval); + return ir_gen_array_access(irb, scope, node, lval, result_loc); case NodeTypeReturnExpr: - return ir_gen_return(irb, scope, node, lval); + return ir_gen_return(irb, scope, node, lval, result_loc); case NodeTypeFieldAccessExpr: - { - IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); - if (ptr_instruction == irb->codegen->invalid_instruction) - return ptr_instruction; - if (lval == LValPtr) - return ptr_instruction; - - return ir_build_load_ptr(irb, scope, node, ptr_instruction); - } + return ir_gen_field_access(irb, scope, node, lval, result_loc); case NodeTypePtrDeref: { AstNode *expr_node = node->data.ptr_deref_expr.target; - IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); - if (value == irb->codegen->invalid_instruction) - return value; - - return ir_build_un_op(irb, scope, node, IrUnOpDereference, value); + IrInstruction *ptr_inst = ir_gen_node(irb, expr_node, scope, LValNone, nullptr); + if (ptr_inst == irb->codegen->invalid_instruction) + return ptr_inst; + return ir_gen_ptr(irb, scope, node, lval, result_loc, ptr_inst); } case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; + if (lval == LValPtr) { + IrInstruction *maybe_ptr = ir_gen_node(irb, expr_node, scope, LValPtr, nullptr); + if (maybe_ptr == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, expr_node, maybe_ptr, true); + if (result_loc == nullptr) + return unwrapped_ptr; + ir_build_store_ptr(irb, scope, node, result_loc, unwrapped_ptr); + return result_loc; + } - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); - if (maybe_ptr == irb->codegen->invalid_instruction) + IrInstruction *ensured_result_loc = ensure_result_loc(irb, scope, node, lval, result_loc); + if (ensured_result_loc == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); - if (lval == LValPtr) - return unwrapped_ptr; - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + IrInstruction *is_non_null = ir_gen_node(irb, expr_node, scope, LValOptional, ensured_result_loc); + if (is_non_null == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + ir_build_assert_non_null(irb, scope, expr_node, is_non_null); + return ir_gen_result(irb, scope, node, lval, result_loc, LValOptional, ensured_result_loc); } case NodeTypeBoolLiteral: - return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_bool_literal(irb, scope, node)); case NodeTypeArrayType: - return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_array_type(irb, scope, node)); case NodeTypePointerType: - return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_pointer_type(irb, scope, node)); case NodeTypePromiseType: - return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_promise_type(irb, scope, node)); case NodeTypeStringLiteral: - return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_string_literal(irb, scope, node)); case NodeTypeUndefinedLiteral: - return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_undefined_literal(irb, scope, node)); case NodeTypeAsmExpr: - return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_asm_expr(irb, scope, node)); case NodeTypeNullLiteral: - return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); + return ir_gen_null_literal(irb, scope, node, lval, result_loc); 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); + return ir_gen_if_err_expr(irb, scope, node, lval, result_loc); + case NodeTypeIfOptional: + return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc); case NodeTypeSwitchExpr: - return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); + return ir_gen_switch_expr(irb, scope, node, lval, result_loc); case NodeTypeCompTime: - return ir_gen_comptime(irb, scope, node, lval); + return ir_gen_comptime(irb, scope, node, lval, result_loc); case NodeTypeErrorType: - return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_error_type(irb, scope, node)); case NodeTypeBreak: - return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_break(irb, scope, node)); case NodeTypeContinue: - return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_continue(irb, scope, node)); case NodeTypeUnreachable: - return ir_lval_wrap(irb, scope, ir_build_unreachable(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_build_unreachable(irb, scope, node)); case NodeTypeDefer: - return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_defer(irb, scope, node)); case NodeTypeSliceExpr: - return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval); + return ir_gen_slice(irb, scope, node, lval, result_loc); case NodeTypeUnwrapErrorExpr: - return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval); + return ir_gen_catch(irb, scope, node, lval, result_loc); case NodeTypeContainerDecl: - return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_container_decl(irb, scope, node)); case NodeTypeFnProto: - return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_fn_proto(irb, scope, node)); case NodeTypeErrorSetDecl: - return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_err_set_decl(irb, scope, node)); case NodeTypeCancel: - return ir_lval_wrap(irb, scope, ir_gen_cancel(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_cancel(irb, scope, node)); case NodeTypeResume: - return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_resume(irb, scope, node)); case NodeTypeAwaitExpr: - return ir_lval_wrap(irb, scope, ir_gen_await_expr(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_await_expr(irb, scope, node)); case NodeTypeSuspend: - return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval); + return ir_gen_value(irb, scope, node, lval, result_loc, ir_gen_suspend(irb, scope, node)); + case NodeTypeErrorLiteral: { + IrInstruction *err_code = ir_build_error_literal(irb, scope, node, node->data.error_literal.name); + switch (lval) { + case LValErrorUnionVal: + if (result_loc != nullptr) { + IrInstruction *undef = ir_build_const_undefined(irb, scope, node); + ir_build_store_ptr(irb, scope, node, result_loc, undef); + } + return err_code; + case LValErrorUnionPtr: + if (result_loc != nullptr) { + IrInstruction *undef = ir_build_const_undefined(irb, scope, node); + ir_build_store_ptr(irb, scope, node, result_loc, undef); + } + return ir_build_ref(irb, scope, node, err_code, true, false, nullptr); + default: + return ir_gen_value(irb, scope, node, lval, result_loc, err_code); + } + } } zig_unreachable(); } -static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval) { - IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval); +static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, + IrInstruction *result_loc) +{ + IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval, result_loc); irb->exec->invalid = irb->exec->invalid || (result == irb->codegen->invalid_instruction); return result; } -static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) { - return ir_gen_node_extra(irb, node, scope, LValNone); -} - static void invalidate_exec(IrExecutable *exec) { if (exec->invalid) return; @@ -7152,6 +8103,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,73 +8124,88 @@ 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); - coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var); + coro_promise_ptr = ir_build_alloca_src(irb, coro_scope, node, coro_frame_type_value, nullptr, "", nullptr); + ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, coro_promise_ptr); 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); - irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); + irb->exec->await_handle_var_ptr = ir_build_alloca_src(irb, coro_scope, node, await_handle_type_val, nullptr, "", nullptr); + ir_build_store_ptr(irb, coro_scope, node, irb->exec->await_handle_var_ptr, null_value); + ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, + irb->exec->await_handle_var_ptr); 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); + IrInstruction *coro_size_var_alloca = ir_build_alloca_src(irb, coro_scope, node, nullptr, nullptr, "", nullptr); + ir_build_store_ptr(irb, coro_scope, node, coro_size_var_alloca, coro_size); + ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size_var_alloca); 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); + IrInstruction *coro_allocator_var_alloca = ir_build_alloca_src(irb, coro_scope, node, nullptr, nullptr, "", nullptr); + ir_build_store_ptr(irb, coro_scope, node, coro_allocator_var_alloca, implicit_allocator_ptr); + ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, + coro_allocator_var_alloca); 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); + IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, + nullptr, alloc_field_name, false); + IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr, nullptr); IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size); IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError"); IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, coro_scope, "AllocOk"); - ir_build_cond_br(irb, coro_scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false); + ir_build_cond_br(irb, coro_scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, alloc_err_block); // we can return undefined here, because the caller passes a pointer to the error struct field // in the error union result, and we populate it in case of allocation failure. - ir_build_return(irb, coro_scope, node, undef); + ir_build_return(irb, coro_scope, node, undef, nullptr); 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); irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - atomic_state_field_name); + nullptr, atomic_state_field_name, false); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero); Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); - irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); + irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, result_field_name, false); result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); - irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); + irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, result_ptr_field_name, false); ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr); if (irb->codegen->have_err_ret_tracing) { // initialize the error return trace Buf *return_addresses_field_name = buf_create_from_str(RETURN_ADDRESSES_FIELD_NAME); - IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, return_addresses_field_name); + IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, return_addresses_field_name, false); Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); - err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name); + err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, nullptr, + err_ret_trace_field_name, false); ir_build_mark_err_ret_trace_ptr(irb, scope, node, err_ret_trace_ptr); // coordinate with builtin.zig Buf *index_name = buf_create_from_str("index"); - IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name); + IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, nullptr, + index_name, false); ir_build_store_ptr(irb, scope, node, index_ptr, zero); Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses"); - IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, instruction_addresses_name); + IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, nullptr, + instruction_addresses_name, false); - IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false); + IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, + nullptr, false, addrs_slice_ptr); ir_build_store_ptr(irb, scope, node, addrs_slice_ptr, slice_value); } @@ -7249,14 +8216,22 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup"); } - IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone); + IrInstruction *result = ir_gen_node(irb, node, scope, LValNone, nullptr); assert(result); if (irb->exec->invalid) return false; if (!instr_is_unreachable(result)) { // no need for save_err_ret_addr because this cannot return error - ir_gen_async_return(irb, scope, result->source_node, result, true); + IrInstruction *implicit_return_result = result; + if (fn_entry != nullptr) { + ZigType *return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + // return_type can be null when this function is being executed at comptime + if (return_type != nullptr && return_type->id == ZigTypeIdErrorUnion) { + implicit_return_result = ir_build_const_null(irb, scope, result->source_node); + } + } + ir_gen_async_return(irb, scope, result->source_node, implicit_return_result, true, nullptr); } if (is_async) { @@ -7271,11 +8246,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec cases[0].block = invalid_resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = irb->exec->coro_final_cleanup_block; - ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); + ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr, nullptr); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block); ir_build_coro_end(irb, scope, node); - ir_build_return(irb, scope, node, irb->exec->coro_handle); + ir_build_return(irb, scope, node, irb->exec->coro_handle, nullptr); ir_set_cursor_at_end_and_append_block(irb, invalid_resume_block); ir_build_unreachable(irb, scope, node); @@ -7285,9 +8260,9 @@ 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 *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 = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, nullptr); + 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); @@ -7296,16 +8271,17 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec } if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); - IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); - IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr); + IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + nullptr, err_ret_trace_ptr_field_name, false); + IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, nullptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } // 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); + IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr, nullptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block); @@ -7331,26 +8307,30 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); - IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, free_field_name); - IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); + IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, nullptr, + free_field_name, false); + IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr, nullptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); 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_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); + 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, nullptr); 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); - IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false); + IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr, nullptr); + IrInstruction *mem_slice_alloca = ir_build_alloca_src(irb, scope, node, nullptr, nullptr, "", nullptr); + IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false, + mem_slice_alloca); size_t arg_count = 2; IrInstruction **args = allocate(arg_count); args[0] = implicit_allocator_ptr; // self args[1] = mem_slice; // old_mem - ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr); + ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, + nullptr, nullptr, nullptr, nullptr, LValNone); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); - ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); + ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, resume_block); ir_gen_resume_target(irb, scope, node, awaiter_handle); @@ -7435,7 +8415,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 +8425,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 +9748,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) { @@ -8807,7 +9787,13 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (prev_type->id == ZigTypeIdErrorSet) { assert(err_set_type != nullptr); - if (cur_type->id == ZigTypeIdErrorSet) { + if (cur_type->id == ZigTypeIdErrorSet || + (cur_type->id == ZigTypeIdOptional && cur_type->data.maybe.child_type->id == ZigTypeIdErrorSet)) + { + if (cur_type->id == ZigTypeIdOptional) { + cur_type = cur_type->data.maybe.child_type; + any_are_null = true; + } if (type_is_global_error_set(err_set_type)) { continue; } @@ -8933,6 +9919,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 +9929,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); @@ -9264,8 +10250,15 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT return slice_type; } } else if (err_set_type != nullptr) { - if (prev_inst->value.type->id == ZigTypeIdErrorSet) { - return err_set_type; + if (prev_inst->value.type->id == ZigTypeIdErrorSet || + (prev_inst->value.type->id == ZigTypeIdOptional && + prev_inst->value.type->data.maybe.child_type->id == ZigTypeIdErrorSet)) + { + if (any_are_null) { + return get_optional_type(ira->codegen, err_set_type); + } else { + return err_set_type; + } } else if (prev_inst->value.type->id == ZigTypeIdErrorUnion) { ZigType *payload_type = prev_inst->value.type->data.error_union.payload_type; if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) @@ -9284,9 +10277,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT buf_sprintf("unable to make error union out of number literal")); return ira->codegen->builtin_types.entry_invalid; } else if (prev_inst->value.type->id == ZigTypeIdNull) { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to make error union out of null literal")); - return ira->codegen->builtin_types.entry_invalid; + return get_optional_type(ira->codegen, err_set_type); } else { if ((err = type_resolve(ira->codegen, prev_inst->value.type, ResolveStatusSizeKnown))) return ira->codegen->builtin_types.entry_invalid; @@ -9310,19 +10301,10 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT } } -static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry) { - if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) { - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - if (fn_entry != nullptr) { - fn_entry->alloca_list.append(instruction); - } - } -} - static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) { ConstGlobalRefs *global_refs = dest->global_refs; *dest = *src; - if (!same_global_refs) { + if (!same_global_refs && src->special != ConstValSpecialUndef) { dest->global_refs = global_refs; if (dest->type->id == ZigTypeIdStruct) { dest->data.x_struct.fields = allocate_nonzero(dest->type->data.structure.src_field_count); @@ -9344,7 +10326,6 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ zig_unreachable(); case CastOpErrSet: case CastOpBitCast: - case CastOpPtrOfArrayToSlice: zig_panic("TODO"); case CastOpNoop: { @@ -9379,9 +10360,6 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ } const_val->type = new_type; break; - case CastOpResizeSlice: - // can't do it - zig_unreachable(); case CastOpIntToFloat: { assert(new_type->id == ZigTypeIdFloat); @@ -9432,14 +10410,27 @@ 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_const_undef(IrAnalyze *ira, IrInstruction *source_instr) { + IrInstruction *undef = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_undef); + undef->value.special = ConstValSpecialUndef; + return undef; +} + 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); + if (instr_is_comptime(value) || !type_has_bits(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)) { @@ -9449,9 +10440,6 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst } else { IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op); result->value.type = wanted_type; - if (need_alloca) { - ir_add_alloca(ira, result, wanted_type); - } return result; } } @@ -9471,14 +10459,16 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type)); + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, + wanted_type, value, CastOpBitCast); + result->value.type = wanted_type; + if (instr_is_comptime(value)) { ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, &value->value, source_instr->source_node); 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; + result->value.special = ConstValSpecialStatic; 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; @@ -9488,9 +10478,6 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, } } - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, - wanted_type, value, CastOpBitCast); - result->value.type = wanted_type; return result; } @@ -9517,8 +10504,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; @@ -9527,10 +10513,11 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc } } - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, - wanted_type, value, CastOpPtrOfArrayToSlice); + IrInstruction *result_loc = ir_analyze_alloca(ira, source_instr, wanted_type, 0, "slice", false); + result_loc->value.special = ConstValSpecialRuntime; + IrInstruction *result = ir_build_ptr_of_array_to_slice(&ira->new_irb, source_instr->scope, + source_instr->source_node, value, result_loc); result->value.type = wanted_type; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9653,15 +10640,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 +10697,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 +10716,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); @@ -9758,17 +10736,20 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node analyzed_executable->backward_branch_count = backward_branch_count; analyzed_executable->backward_branch_quota = backward_branch_quota; 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; + + ConstExprValue *payload_result = create_const_vals(1); + ConstExprValue *analyze_result = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node, + payload_result); + if (type_is_invalid(analyze_result->type)) + 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"); } - return ir_exec_const_result(codegen, analyzed_executable); + return analyze_result; } static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { @@ -9810,11 +10791,324 @@ 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_result_optional_payload(IrAnalyze *ira, IrInstruction *result_loc, + ZigType *needed_child_type, bool make_non_null) +{ + ZigType *opt_type = get_optional_type(ira->codegen, needed_child_type); + result_loc = resolve_possible_alloca_inference(ira, result_loc, opt_type); + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + + ZigType *old_ptr_type = result_loc->value.type; + assert(old_ptr_type->id == ZigTypeIdPointer); + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, + false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, result_loc->source_node); + assert(optional_val->type->id == ZigTypeIdOptional); + bool same_comptime_repr = types_have_same_zig_comptime_repr(optional_val->type, + optional_val->type->data.maybe.child_type); + if (optional_val->special == ConstValSpecialUndef) { + switch (type_has_one_possible_value(ira->codegen, needed_child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + if (!same_comptime_repr) { + ConstExprValue *payload_val = create_const_vals(1); + payload_val->type = needed_child_type; + payload_val->special = ConstValSpecialUndef; + payload_val->parent.id = ConstParentIdOptionalPayload; + payload_val->parent.data.p_optional_payload.optional_val = optional_val; + + optional_val->data.x_optional = payload_val; + optional_val->special = ConstValSpecialStatic; + } + break; + case OnePossibleValueYes: { + ConstExprValue *pointee = create_const_vals(1); + pointee->special = ConstValSpecialStatic; + pointee->type = needed_child_type; + + optional_val->special = ConstValSpecialStatic; + optional_val->data.x_optional = pointee; + break; + } + } + } + + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_result_optional_payload(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc, nullptr, make_non_null); + result->value.type = new_ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, result_loc, new_ptr_type); + } + ConstExprValue *result_val = &result->value; + result_val->data.x_ptr.special = ConstPtrSpecialRef; + result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + switch (type_has_one_possible_value(ira->codegen, needed_child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + if (same_comptime_repr) { + result_val->data.x_ptr.data.ref.pointee = optional_val; + } else { + assert(optional_val->data.x_optional != nullptr); + result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; + } + break; + case OnePossibleValueYes: + assert(optional_val->data.x_optional != nullptr); + result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; + break; + } + + return result; + } + } + + IrInstruction *result = ir_build_result_optional_payload(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc, nullptr, make_non_null); + result->value.type = new_ptr_type; + return result; +} + +static IrInstruction *ir_analyze_instruction_result_optional_payload(IrAnalyze *ira, + IrInstructionResultOptionalPayload *instruction) +{ + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child); + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_instruction; + return ir_analyze_result_optional_payload(ira, prev_result_loc, payload_type, instruction->make_non_null); +} + +static IrInstruction *ir_analyze_result_slice_ptr(IrAnalyze *ira, IrInstruction *result_loc, uint64_t len, + bool init_array) +{ + ZigType *slice_type = result_loc->value.type->data.pointer.child_type; + assert(is_slice(slice_type)); + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, slice_ptr_type, + false, result_loc->value.type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *slice_val = const_ptr_pointee(ira, ira->codegen, ptr_val, result_loc->source_node); + assert(is_slice(slice_val->type)); + if (slice_val->special == ConstValSpecialUndef) { + slice_val->data.x_struct.fields = create_const_vals(2); + ConstExprValue *slice_ptr_val = &slice_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *slice_len_val = &slice_val->data.x_struct.fields[slice_len_index]; + + slice_ptr_val->type = slice_ptr_type; + slice_ptr_val->special = ConstValSpecialUndef; + slice_ptr_val->parent.id = ConstParentIdStruct; + slice_ptr_val->parent.data.p_struct.struct_val = slice_val; + slice_ptr_val->parent.data.p_struct.field_index = slice_ptr_index; + + slice_len_val->type = ira->codegen->builtin_types.entry_usize; + slice_len_val->special = ConstValSpecialUndef; + slice_len_val->parent.id = ConstParentIdStruct; + slice_len_val->parent.data.p_struct.struct_val = slice_val; + slice_len_val->parent.data.p_struct.field_index = slice_len_index; + + slice_val->special = ConstValSpecialStatic; + } + assert(slice_val->data.x_struct.fields != nullptr); + ConstExprValue *slice_len_val = &slice_val->data.x_struct.fields[slice_len_index]; + if (slice_len_val->special == ConstValSpecialUndef) { + slice_len_val->special = ConstValSpecialStatic; + bigint_init_unsigned(&slice_len_val->data.x_bigint, len); + } + IrInstruction *array_loc = nullptr; + if (init_array) { + ConstExprValue *slice_ptr_val = &slice_val->data.x_struct.fields[slice_ptr_index]; + if (slice_ptr_val->special == ConstValSpecialUndef) { + ZigType *array_type = get_array_type(ira->codegen, slice_ptr_type->data.pointer.child_type, len); + array_loc = ir_analyze_alloca(ira, result_loc, array_type, 0, "", false); + assert(array_loc->value.data.x_ptr.special == ConstPtrSpecialRef); + ConstExprValue *array_val = array_loc->value.data.x_ptr.data.ref.pointee; + array_val->special = ConstValSpecialStatic; + array_val->data.x_array.special = ConstArraySpecialUndef; + + slice_ptr_val->special = ConstValSpecialStatic; + slice_ptr_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + slice_ptr_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + slice_ptr_val->data.x_ptr.data.base_array.array_val = array_val; + } + } + + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_result_slice_ptr(&ira->new_irb, result_loc->scope, result_loc->source_node, + result_loc, len, array_loc); + result->value.type = new_ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, result_loc, new_ptr_type); + } + ConstExprValue *result_val = &result->value; + result_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; + result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + result_val->data.x_ptr.data.base_struct.struct_val = slice_val; + result_val->data.x_ptr.data.base_struct.field_index = slice_ptr_index; + return result; + } + } + IrInstruction *array_loc = nullptr; + if (init_array) { + ZigType *array_type = get_array_type(ira->codegen, slice_ptr_type->data.pointer.child_type, len); + array_loc = ir_analyze_alloca(ira, result_loc, array_type, 0, "", false); + array_loc->value.special = ConstValSpecialRuntime; + } + IrInstruction *result = ir_build_result_slice_ptr(&ira->new_irb, result_loc->scope, result_loc->source_node, + result_loc, len, array_loc); + result->value.type = new_ptr_type; + return result; +} + +static void init_if_undef_err_union(IrAnalyze *ira, ConstExprValue *error_union_val) { + if (error_union_val->special != ConstValSpecialUndef) + return; + + ConstExprValue *vals = create_const_vals(2); + ConstExprValue *err_set_val = &vals[0]; + ConstExprValue *payload_val = &vals[1]; + + err_set_val->type = get_optional_type(ira->codegen, + error_union_val->type->data.error_union.err_set_type); + err_set_val->special = ConstValSpecialUndef; + err_set_val->parent.id = ConstParentIdErrUnionCode; + err_set_val->parent.data.p_err_union_code.err_union_val = error_union_val; + + payload_val->type = error_union_val->type->data.error_union.payload_type; + payload_val->special = ConstValSpecialUndef; + payload_val->parent.id = ConstParentIdErrUnionPayload; + payload_val->parent.data.p_err_union_payload.err_union_val = error_union_val; + + error_union_val->data.x_err_union.error_set = err_set_val; + error_union_val->data.x_err_union.payload = payload_val; + error_union_val->special = ConstValSpecialStatic; +} + +static IrInstruction *ir_analyze_result_error_union_payload(IrAnalyze *ira, IrInstruction *result_loc, + ZigType *needed_child_type) +{ + ZigType *old_ptr_type = result_loc->value.type; + assert(old_ptr_type->id == ZigTypeIdPointer); + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, + false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *error_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, result_loc->source_node); + assert(error_union_val->type->id == ZigTypeIdErrorUnion); + + init_if_undef_err_union(ira, error_union_val); + assert(error_union_val->data.x_err_union.payload != nullptr); + + // Make the error set null + error_union_val->data.x_err_union.error_set->special = ConstValSpecialStatic; + error_union_val->data.x_err_union.error_set->data.x_err_set = nullptr; + + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_result_error_union_payload(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc); + result->value.type = new_ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, result_loc, new_ptr_type); + } + ConstExprValue *result_val = &result->value; + result_val->data.x_ptr.special = ConstPtrSpecialRef; + result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + result_val->data.x_ptr.data.ref.pointee = error_union_val->data.x_err_union.payload; + + return result; + } + } + + IrInstruction *result = ir_build_result_error_union_payload(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc); + result->value.type = new_ptr_type; + return result; +} + +static IrInstruction *ir_analyze_result_error_union_code(IrAnalyze *ira, IrInstruction *result_loc, + ZigType *needed_child_type) +{ + ZigType *old_ptr_type = result_loc->value.type; + assert(old_ptr_type->id == ZigTypeIdPointer); + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, needed_child_type, + false, old_ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); + + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *error_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, result_loc->source_node); + assert(error_union_val->type->id == ZigTypeIdErrorUnion); + + init_if_undef_err_union(ira, error_union_val); + assert(error_union_val->data.x_err_union.error_set != nullptr); + + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_result_error_union_code(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc); + result->value.type = new_ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, result_loc, new_ptr_type); + } + ConstExprValue *result_val = &result->value; + result_val->data.x_ptr.special = ConstPtrSpecialRef; + result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + result_val->data.x_ptr.data.ref.pointee = error_union_val->data.x_err_union.error_set; + + return result; + } + } + + IrInstruction *result = ir_build_result_error_union_code(&ira->new_irb, result_loc->scope, + result_loc->source_node, result_loc); + result->value.type = new_ptr_type; + return result; +} + +static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *wanted_type) +{ assert(wanted_type->id == ZigTypeIdOptional); + ZigType *payload_type = wanted_type->data.maybe.child_type; if (instr_is_comptime(value)) { - ZigType *payload_type = wanted_type->data.maybe.child_type; IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type); if (type_is_invalid(casted_payload->value.type)) return ira->codegen->invalid_instruction; @@ -9826,7 +11120,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; @@ -9835,10 +11129,16 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc return &const_instruction->base; } - IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value); + IrInstruction *result_loc = nullptr; + if (type_has_bits(payload_type) && handle_is_ptr(wanted_type)) { + result_loc = ir_analyze_alloca(ira, source_instr, wanted_type, 0, "", false); + result_loc->value.special = ConstValSpecialRuntime; + } + + IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, + value, result_loc); result->value.type = wanted_type; result->value.data.rh_maybe = RuntimeHintOptionalNonNull; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9857,11 +11157,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; } @@ -9869,7 +11174,6 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction IrInstruction *result = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionNonError; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9926,11 +11230,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; } @@ -9938,7 +11247,6 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so IrInstruction *result = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; result->value.data.rh_error_union = RuntimeHintErrorUnionError; - ir_add_alloca(ira, result, wanted_type); return result; } @@ -9952,8 +11260,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; } @@ -9962,7 +11271,7 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so } static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value, - bool is_const, bool is_volatile) + bool is_const, bool is_volatile, IrInstruction *result_loc) { Error err; @@ -9980,17 +11289,16 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi ConstPtrMutComptimeConst, is_const, is_volatile, 0); } + if (result_loc == nullptr) { + result_loc = ir_analyze_alloca(ira, source_instruction, value->value.type, 0, "", false); + result_loc->value.special = ConstValSpecialRuntime; + } ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, is_const, is_volatile, PtrLenSingle, 0, 0, 0); IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, value, is_const, is_volatile); + source_instruction->source_node, value, is_const, is_volatile, result_loc); new_instruction->value.type = ptr_type; new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; - if (type_has_bits(ptr_type)) { - ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - assert(fn_entry); - fn_entry->alloca_list.append(new_instruction); - } return new_instruction; } @@ -10001,43 +11309,27 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s // In this function we honor the const-ness of wanted_type, because // we may be casting [0]T to []const T which is perfectly valid. - IrInstruction *array_ptr = nullptr; IrInstruction *array; if (array_arg->value.type->id == ZigTypeIdPointer) { array = ir_get_deref(ira, source_instr, array_arg); - array_ptr = array_arg; } else { array = array_arg; } 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); - 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); - 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); - - IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope, - source_instr->source_node, array_ptr, start, end, false); - result->value.type = wanted_type; - result->value.data.rh_slice.id = RuntimeHintSliceIdLen; - result->value.data.rh_slice.len = array_type->data.array.len; - ir_add_alloca(ira, result, result->value.type); - - return result; + // TODO once https://github.com/ziglang/zig/issues/265 + // lands this entire function will be deleted. + IrInstruction *slice_alloca = ir_analyze_alloca(ira, source_instr, wanted_type, 0, "", false); + slice_alloca->value.special = ConstValSpecialRuntime; + return ir_build_array_to_slice(ira, source_instr, wanted_type, array, slice_alloca); } static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, @@ -10064,8 +11356,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; } @@ -10075,8 +11366,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; @@ -10099,8 +11389,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); @@ -10111,8 +11400,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; @@ -10129,8 +11417,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; } @@ -10162,8 +11449,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); @@ -10218,8 +11504,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); @@ -10273,8 +11558,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; } @@ -10292,8 +11576,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) { @@ -10316,8 +11599,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; @@ -10381,12 +11663,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 { @@ -10421,14 +11702,12 @@ 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); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = 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); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = wanted_type; ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; bigint_init_unsigned(&result->value.data.x_bigint, err->value); @@ -10476,8 +11755,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); @@ -10625,12 +11904,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; } @@ -10654,7 +11933,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); } } } @@ -10707,6 +11986,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); @@ -10760,6 +12044,8 @@ 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 + // only the comptime branch here is used 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); @@ -10771,7 +12057,23 @@ 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 + // []const T in this situation gets turned into [*]const T with the result location cast + if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdPointer && + wanted_type->data.pointer.ptr_len == PtrLenUnknown) + { + if ((wanted_type->data.pointer.is_const || actual_type->data.array.len == 0) && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) + { + IrInstruction *array_ref = ir_get_ref(ira, source_instr, value, true, false, nullptr); + return ir_analyze_cast(ira, source_instr, wanted_type, array_ref); + } + } + // 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) @@ -10866,7 +12168,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) { @@ -10966,49 +12268,298 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from T to *T where T is zero bits - if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + // cast from T to *T where T is zero bits + if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) { + return ira->codegen->invalid_instruction; + } + if (!type_has_bits(actual_type)) { + return ir_get_ref(ira, source_instr, value, false, false, nullptr); + } + } + + + // cast from undefined to anything + if (actual_type->id == ZigTypeIdUndefined) { + return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); + } + + ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, + 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); + return ira->codegen->invalid_instruction; +} + +static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type) { + assert(value); + assert(value != ira->codegen->invalid_instruction); + assert(!expected_type || !type_is_invalid(expected_type)); + assert(value->value.type); + assert(!type_is_invalid(value->value.type)); + if (expected_type == nullptr) + return value; // anything will do + if (expected_type == value->value.type) + return value; // match + if (value->value.type->id == ZigTypeIdUnreachable) + return value; + + return ir_analyze_cast(ira, value, expected_type, value); +} + +static Error resolve_alloca_inference(IrAnalyze *ira, IrInstructionAllocaGen *alloca, + ZigType *child_type) +{ + Error err; + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) + return err; + assert(alloca->base.value.data.x_ptr.special != ConstPtrSpecialInvalid); + alloca->base.value.data.x_ptr.data.ref.pointee->type = child_type; + alloca->base.value.type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, + PtrLenSingle, alloca->align, 0, 0); + return ErrorNone; +} + +static IrInstruction *resolve_possible_alloca_inference(IrAnalyze *ira, IrInstruction *base, ZigType *child_type) { + Error err; + assert(base->value.type->id == ZigTypeIdPointer); + ZigType *infer_child = base->value.type->data.pointer.child_type; + if (base->id == IrInstructionIdResultSliceToBytesPlaceholder) { + IrInstructionResultSliceToBytesPlaceholder *res_slice_to_bytes = + reinterpret_cast(base); + // When the instruction was made it didn't have enough information to do analysis, + // so it emitted a placeholder. Now we have the information, so we do + // the analysis, and rely on the ref_count being 0 so that it is not emitted. + assert(res_slice_to_bytes->base.ref_count == 0); + assert(res_slice_to_bytes->pass1_parent != nullptr); + IrInstruction *new_res_slice_to_bytes = ir_analyze_result_slice_to_bytes(ira, base, + res_slice_to_bytes->prev_result_loc, res_slice_to_bytes->elem_type, child_type); + res_slice_to_bytes->pass1_parent->child = new_res_slice_to_bytes; + return new_res_slice_to_bytes; + } + if (infer_child == ira->codegen->builtin_types.entry_infer) { + if (base->id == IrInstructionIdAllocaGen) { + if ((err = resolve_alloca_inference(ira, reinterpret_cast(base), child_type))) + return ira->codegen->invalid_instruction; + } else if (base->id == IrInstructionIdPtrCastGen) { + IrInstructionPtrCastGen *ptr_cast = reinterpret_cast(base); + // When the ptr cast instruction was made it didn't have the destination type, so + // we had to set it to "infer". Now we have the destination type, so we re-do + // analysis, and rely on the ref_count being 0 so that it is not emitted. + assert(ptr_cast->base.ref_count == 0); + ZigType *prev_ptr_type = ptr_cast->ptr->value.type; + // TODO should this be adjust_ptr_child? + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, false, false, + PtrLenSingle, prev_ptr_type->data.pointer.explicit_alignment, 0, 0); + IrInstruction *new_ptr_cast = ir_analyze_ptr_cast(ira, base, ptr_cast->ptr, new_ptr_type, base); + ptr_cast->pass1_parent->child = new_ptr_cast; + return new_ptr_cast; + } + } else if (is_slice(infer_child)) { + ZigType *slice_ptr_type = infer_child->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + if (slice_ptr_type->data.pointer.child_type == ira->codegen->builtin_types.entry_infer) { + assert(base->id == IrInstructionIdPtrCastGen); + IrInstructionPtrCastGen *ptr_cast = reinterpret_cast(base); + // See above comment + assert(ptr_cast->base.ref_count == 0); + assert(ptr_cast->ptr->ref_count == 1); // The ref is this PtrCast instruction + ZigType *prev_ptr_type = ptr_cast->ptr->value.type; + assert(is_slice(child_type)); + ZigType *good_slice_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry; + ZigType *good_u8_ptr_type = adjust_ptr_child(ira->codegen, good_slice_ptr_type, + ira->codegen->builtin_types.entry_u8); + ZigType *good_u8_slice_type = get_slice_type(ira->codegen, good_u8_ptr_type); + ptr_cast->ptr->value.type = adjust_ptr_child(ira->codegen, prev_ptr_type, good_u8_slice_type); + ZigType *new_ptr_type = adjust_ptr_child(ira->codegen, prev_ptr_type, child_type); + ptr_cast->base.value.type = new_ptr_type; + } + } + return base; +} + +static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { + if (ptr_type->id == ZigTypeIdPointer) { + return get_pointer_to_type_extra(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + new_align, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); + } else if (ptr_type->id == ZigTypeIdOptional) { + ZigType *actual_ptr_type = ptr_type->data.pointer.child_type; + return get_optional_type(g, adjust_ptr_align(g, actual_ptr_type, new_align)); + } + zig_unreachable(); +} + +// TODO: audit callsites. The purpose of this function is to change the element type of the pointer +// while leaving the other properties the same. But if the original pointer has ABI alignment and +// the original element type has different ABI alignment than the new element type, the resulting +// pointer type will have different alignment, which is probably incorrect. Probably this function +// will have to be removed and the logic in the callsites reworked. +static ZigType *adjust_ptr_child(CodeGen *g, ZigType *ptr_type, ZigType *new_child) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra(g, + new_child, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); +} + +static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { + assert(is_slice(slice_type)); + ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry, + new_align); + return get_slice_type(g, ptr_type); +} + +static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); +} + +static IrInstruction *ir_implicit_cast_result(IrAnalyze *ira, AstNode *source_node, + IrInstruction *unresolved_result_loc, ZigType *needed_child_type, bool allow_failure) +{ + if (type_is_invalid(unresolved_result_loc->value.type) || type_is_invalid(needed_child_type)) + return ira->codegen->invalid_instruction; + + assert(unresolved_result_loc->value.type->id == ZigTypeIdPointer); + if (unresolved_result_loc->value.special == ConstValSpecialStatic && + unresolved_result_loc->value.data.x_ptr.special == ConstPtrSpecialDiscard) + { + return unresolved_result_loc; + } + + IrInstruction *result_loc = resolve_possible_alloca_inference(ira, unresolved_result_loc, needed_child_type); + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + + if (needed_child_type == nullptr) + return result_loc; + + assert(result_loc->value.type->id == ZigTypeIdPointer); + ZigType *have_child_type = result_loc->value.type->data.pointer.child_type; + if (have_child_type == needed_child_type) + return result_loc; + + // perfect match or non-const to const + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, have_child_type, needed_child_type, + source_node, false); + if (const_cast_result.id == ConstCastResultIdInvalid) + return ira->codegen->invalid_instruction; + if (const_cast_result.id == ConstCastResultIdOk) { + ZigType *new_ptr_type = adjust_ptr_child(ira->codegen, result_loc->value.type, needed_child_type); + return ir_analyze_ptr_cast(ira, result_loc, result_loc, new_ptr_type, result_loc); + } + + // cast from *[N]T to []T + if (is_slice(have_child_type) && + needed_child_type->id == ZigTypeIdPointer && + needed_child_type->data.pointer.ptr_len == PtrLenSingle && + needed_child_type->data.pointer.child_type->id == ZigTypeIdArray) + { + ZigType *slice_ptr_type = have_child_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = needed_child_type->data.pointer.child_type; + bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 + || !needed_child_type->data.pointer.is_const); + if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + array_type->data.array.child_type, source_node, + !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + return ir_analyze_result_slice_ptr(ira, result_loc, array_type->data.array.len, false); + } + } + + // cast from [N]T to []const T + if (is_slice(have_child_type) && needed_child_type->id == ZigTypeIdArray) { + ZigType *ptr_type = have_child_type->data.structure.fields[slice_ptr_index].type_entry; + assert(ptr_type->id == ZigTypeIdPointer); + if ((ptr_type->data.pointer.is_const || needed_child_type->data.array.len == 0) && + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, + needed_child_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_result_slice_ptr(ira, result_loc, needed_child_type->data.array.len, true); + } + } + + // cast from T to ?T + if (have_child_type->id == ZigTypeIdOptional) { + if (types_match_const_cast_only(ira, have_child_type->data.maybe.child_type, needed_child_type, source_node, + false).id == ConstCastResultIdOk) + { + return ir_analyze_result_optional_payload(ira, result_loc, needed_child_type, true); + } + } + + // cast from T to E!T + if (have_child_type->id == ZigTypeIdErrorUnion) { + if (types_match_const_cast_only(ira, have_child_type->data.error_union.payload_type, needed_child_type, + source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_result_error_union_payload(ira, result_loc, needed_child_type); + } + } + + // cast from E to E!T + if (have_child_type->id == ZigTypeIdErrorUnion && + needed_child_type->id == ZigTypeIdErrorSet) { - if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) { - return ira->codegen->invalid_instruction; - } - if (!type_has_bits(actual_type)) { - return ir_get_ref(ira, source_instr, value, false, false); - } + return ir_analyze_result_error_union_code(ira, result_loc, needed_child_type); } + // cast from T to E!?T + if (have_child_type->id == ZigTypeIdErrorUnion && + have_child_type->data.error_union.payload_type->id == ZigTypeIdOptional && + needed_child_type->id != ZigTypeIdOptional) + { + ZigType *wanted_child_type = have_child_type->data.error_union.payload_type->data.maybe.child_type; + if (types_match_const_cast_only(ira, wanted_child_type, needed_child_type, source_node, + false).id == ConstCastResultIdOk || + needed_child_type->id == ZigTypeIdNull || + needed_child_type->id == ZigTypeIdComptimeInt || + needed_child_type->id == ZigTypeIdComptimeFloat) + { + IrInstruction *cast1 = ir_implicit_cast_result(ira, source_node, result_loc, + have_child_type->data.error_union.payload_type, allow_failure); + if (cast1 == nullptr) + return nullptr; + if (type_is_invalid(cast1->value.type)) + return ira->codegen->invalid_instruction; + + return ir_implicit_cast_result(ira, source_node, cast1, needed_child_type, allow_failure); + } + } // cast from undefined to anything - if (actual_type->id == ZigTypeIdUndefined) { - return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); + if (needed_child_type->id == ZigTypeIdUndefined) { + return result_loc; } - ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, + if (allow_failure) { + return nullptr; + } + + ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, 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); + buf_ptr(&have_child_type->name), + buf_ptr(&needed_child_type->name))); + report_recursive_error(ira, source_node, &const_cast_result, parent_msg); return ira->codegen->invalid_instruction; } -static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type) { - assert(value); - assert(value != ira->codegen->invalid_instruction); - assert(!expected_type || !type_is_invalid(expected_type)); - assert(value->value.type); - assert(!type_is_invalid(value->value.type)); - if (expected_type == nullptr) - return value; // anything will do - if (expected_type == value->value.type) - return value; // match - if (value->value.type->id == ZigTypeIdUnreachable) - return value; - - return ir_analyze_cast(ira, value, expected_type, value); -} - static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) { Error err; ZigType *type_entry = ptr->value.type; @@ -11018,23 +12569,23 @@ 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; } + // if it's infer, that means it's void + if (child_type == ira->codegen->builtin_types.entry_infer) { + return ir_const_void(ira, source_instruction); + } if (instr_is_comptime(ptr)) { if (ptr->value.special == ConstValSpecialUndef) { ir_add_error(ira, ptr, buf_sprintf("attempt to dereference undefined value")); return ira->codegen->invalid_instruction; } - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst || - ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) - { + if (ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { 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))) @@ -11046,9 +12597,13 @@ 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); + source_instruction->source_node, ptr, nullptr); load_ptr_instruction->value.type = child_type; return load_ptr_instruction; } else { @@ -11270,7 +12825,14 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio if (type_is_invalid(value->value.type)) return ir_unreach_error(ira); - IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type); + IrInstruction *result_loc = nullptr; + if (instruction->result_loc != nullptr) { + result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ir_unreach_error(ira); + } + + IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->scalar_return_type); if (type_is_invalid(casted_value->value.type) && ira->explicit_return_type_source_node != nullptr) { ErrorMsg *msg = ira->codegen->errors.last(); add_error_note(ira->codegen, msg, ira->explicit_return_type_source_node, @@ -11285,16 +12847,24 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio ir_add_error(ira, casted_value, buf_sprintf("function returns address of local variable")); return ir_unreach_error(ira); } + + if (result_loc != nullptr && instr_is_comptime(result_loc) && + !ir_should_inline(ira->new_irb.exec, instruction->base.scope)) + { + IrInstruction *loaded = ir_get_deref(ira, &instruction->base, result_loc); + result_loc->value.special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, &instruction->base, result_loc, loaded); + } + IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, casted_value); + instruction->base.source_node, casted_value, nullptr); result->value.type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } 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; } @@ -11369,6 +12939,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; } @@ -11568,19 +13140,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) @@ -12481,16 +14052,12 @@ 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; - return ira->codegen->invalid_instruction; - } - ZigType *explicit_type = nullptr; IrInstruction *var_type = nullptr; if (decl_var_instruction->var_type != nullptr) { @@ -12498,32 +14065,51 @@ 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; } } AstNode *source_node = decl_var_instruction->base.source_node; - IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, explicit_type); bool is_comptime_var = ir_get_var_is_comptime(var); bool var_class_requires_const = false; - ZigType *result_type = casted_init_value->value.type; + IrInstruction *var_ptr = decl_var_instruction->ptr->child; + if (type_is_invalid(var_ptr->value.type)) { + var->var_type = ira->codegen->builtin_types.entry_invalid; + return ira->codegen->invalid_instruction; + } + + assert(var_ptr->value.type->id == ZigTypeIdPointer); + + ZigType *result_type = var_ptr->value.type->data.pointer.child_type; if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; } else if (result_type->id == ZigTypeIdUnreachable || result_type->id == ZigTypeIdOpaque) { ir_add_error_node(ira, source_node, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; + } else { + if ((err = type_resolve(ira->codegen, result_type, ResolveStatusZeroBitsKnown))) { + result_type = ira->codegen->builtin_types.entry_invalid; + } + } + + ConstExprValue *init_val = nullptr; + if (instr_is_comptime(var_ptr) && var_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { + init_val = const_ptr_pointee(ira, ira->codegen, &var_ptr->value, decl_var_instruction->base.source_node); + if (is_comptime_var) { + var->const_value = init_val; + } } switch (type_requires_comptime(ira->codegen, result_type)) { 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, @@ -12532,26 +14118,27 @@ 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 && - casted_init_value->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr && - casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) - { - var_class_requires_const = true; - if (!var->src_is_const && !is_comptime_var) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("functions marked inline must be stored in const or comptime var")); - AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node; - add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); - result_type = ira->codegen->builtin_types.entry_invalid; + if (init_val != nullptr) { + if (init_val->special == ConstValSpecialStatic && + init_val->type->id == ZigTypeIdFn && + init_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + init_val->data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) + { + var_class_requires_const = true; + if (!var->src_is_const && !is_comptime_var) { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("functions marked inline must be stored in const or comptime var")); + AstNode *proto_node = init_val->data.x_ptr.data.fn.fn_entry->proto_node; + add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); + result_type = ira->codegen->builtin_types.entry_invalid; + } } } 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 @@ -12573,8 +14160,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); @@ -12582,22 +14169,38 @@ 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; } } - if (casted_init_value->value.special != ConstValSpecialRuntime) { - if (var->mem_slot_index != SIZE_MAX) { + if (init_val != nullptr && init_val->special != ConstValSpecialRuntime) { + // Resolve ConstPtrMutInfer + if (var->gen_is_const) { + var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else if (is_comptime_var) { + var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeVar; + } else { + // we need a runtime ptr but we have a comptime val. + // since it's a comptime val there are no instructions for it. + // we memcpy the init value here + IrInstruction *deref = ir_get_deref(ira, var_ptr, var_ptr); + // If this assertion trips, something is wrong with the IR instructions, because + // we expected the above deref to return a constant value, but it created a runtime + // instruction. + assert(deref->value.special != ConstValSpecialRuntime); + var_ptr->value.special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref); + } + if (var_ptr->value.special == ConstValSpecialStatic && var->mem_slot_index != SIZE_MAX) { assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length); ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); - copy_const_val(mem_slot, &casted_init_value->value, !is_comptime_var || var->gen_is_const); - + copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const); if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { return ir_const_void(ira, &decl_var_instruction->base); } @@ -12605,7 +14208,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; } @@ -12613,9 +14216,8 @@ 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); + IrInstruction *result = ir_build_var_decl_gen(&ira->new_irb, decl_var_instruction->base.scope, + decl_var_instruction->base.source_node, var, var_ptr); result->value.type = ira->codegen->builtin_types.entry_void; return result; } @@ -12882,15 +14484,16 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i zig_unreachable(); } -static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, - IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst) +static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, + ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, + IrInstruction *async_allocator_inst) { Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); //Buf *free_field_name = buf_create_from_str("freeFn"); assert(async_allocator_inst->value.type->id == ZigTypeIdPointer); ZigType *container_type = async_allocator_inst->value.type->data.pointer.child_type; IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, alloc_field_name, &call_instruction->base, - async_allocator_inst, container_type); + async_allocator_inst, container_type, false); if (type_is_invalid(field_ptr_inst->value.type)) { return ira->codegen->invalid_instruction; } @@ -12915,10 +14518,59 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c ZigType *promise_type = get_promise_type(ira->codegen, return_type); ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); - IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, - fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr); - result->value.type = async_return_type; - return result; + IrInstruction *tmp_stack_space = ir_analyze_alloca(ira, &call_instruction->base, async_return_type, + 0, "", false); + tmp_stack_space->value.special = ConstValSpecialRuntime; + if (type_is_invalid(tmp_stack_space->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_call_inst = ir_build_call(&ira->new_irb, call_instruction->base.scope, + call_instruction->base.source_node, fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, + async_allocator_inst, nullptr, tmp_stack_space, nullptr, LValNone); + new_call_inst->value.type = async_return_type; + + IrInstruction *result_loc = nullptr; + if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { + result_loc = call_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + } + + switch (call_instruction->lval) { + case LValNone: + return new_call_inst; + case LValPtr: + return ir_get_ref(ira, &call_instruction->base, new_call_inst, true, false, nullptr); + case LValErrorUnionVal: { + IrInstruction *call_inst_ref = ir_get_ref(ira, &call_instruction->base, new_call_inst, + true, false, nullptr); + IrInstruction *payload_ptr = ir_analyze_unwrap_err_payload(ira, &call_instruction->base, + call_inst_ref, nullptr, false); + IrInstruction *payload_deref = ir_get_deref(ira, &call_instruction->base, payload_ptr); + ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, payload_deref); + IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, call_instruction->base.scope, + call_instruction->base.source_node, new_call_inst); + result->value.type = get_optional_type(ira->codegen, alloc_fn_error_set_type); + return result; + } + case LValErrorUnionPtr: { + IrInstruction *call_inst_ref = ir_get_ref(ira, &call_instruction->base, new_call_inst, + true, false, nullptr); + IrInstruction *payload_ptr = ir_analyze_unwrap_err_payload(ira, &call_instruction->base, + call_inst_ref, nullptr, false); + IrInstruction *payload_deref = ir_get_deref(ira, &call_instruction->base, payload_ptr); + ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, payload_deref); + + IrInstruction *result = ir_build_error_union_field_error_set(&ira->new_irb, + call_instruction->base.scope, call_instruction->base.source_node, new_call_inst, false); + result->value.type = get_pointer_to_type(ira->codegen, + get_optional_type(ira->codegen, alloc_fn_error_set_type), true); + return result; + } + case LValOptional: + zig_panic("TODO"); + } + zig_unreachable(); } static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, @@ -12947,7 +14599,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; @@ -13005,7 +14657,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; @@ -13059,9 +14711,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; } @@ -13070,14 +14720,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 @@ -13103,27 +14753,509 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, } else if (var->gen_is_const) { ptr_mut = ConstPtrMutComptimeConst; } else { - assert(!comptime_var_mem); - ptr_mut = ConstPtrMutRuntimeVar; + assert(!comptime_var_mem); + ptr_mut = ConstPtrMutRuntimeVar; + } + return ir_get_const_ptr(ira, instruction, mem_slot, var->var_type, + ptr_mut, is_const, is_volatile, var->align_bytes); + } + } + zig_unreachable(); + } + +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->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); + var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; + + return var_ptr_instruction; +} + +static void make_const_ptr_runtime(IrAnalyze *ira, IrInstruction *ptr) { + assert(ptr->id != IrInstructionIdConst); + bool is_static = (ptr->value.special != ConstValSpecialRuntime); + ptr->value.special = ConstValSpecialRuntime; + if (!is_static) return; + ConstExprValue *val; + switch (ptr->value.data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialHardCodedAddr: + case ConstPtrSpecialDiscard: + case ConstPtrSpecialFunction: + case ConstPtrSpecialNull: + return; + case ConstPtrSpecialRef: + val = ptr->value.data.x_ptr.data.ref.pointee; + break; + case ConstPtrSpecialBaseArray: + val = ptr->value.data.x_ptr.data.base_array.array_val; + break; + case ConstPtrSpecialBaseStruct: + val = ptr->value.data.x_ptr.data.base_struct.struct_val; + break; + case ConstPtrSpecialBaseErrorUnionCode: + val = ptr->value.data.x_ptr.data.base_err_union_code.err_union_val; + break; + case ConstPtrSpecialBaseErrorUnionPayload: + val = ptr->value.data.x_ptr.data.base_err_union_payload.err_union_val; + break; + case ConstPtrSpecialBaseOptionalPayload: + val = ptr->value.data.x_ptr.data.base_optional_payload.optional_val; + break; + } + + for (;;) { + is_static = (val->special != ConstValSpecialRuntime); + val->special = ConstValSpecialRuntime; + if (!is_static) return; + + switch (val->parent.id) { + case ConstParentIdNone: + return; + case ConstParentIdStruct: + val = val->parent.data.p_struct.struct_val; + continue; + case ConstParentIdErrUnionCode: + val = val->parent.data.p_err_union_code.err_union_val; + continue; + case ConstParentIdErrUnionPayload: + val = val->parent.data.p_err_union_payload.err_union_val; + continue; + case ConstParentIdOptionalPayload: + val = val->parent.data.p_optional_payload.optional_val; + continue; + case ConstParentIdArray: + val = val->parent.data.p_array.array_val; + continue; + case ConstParentIdUnion: + val = val->parent.data.p_union.union_val; + continue; + case ConstParentIdScalar: + val = val->parent.data.p_scalar.scalar_val; + continue; + } + zig_unreachable(); + } +} + +static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *uncasted_ptr, IrInstruction *uncasted_value) +{ + Error err; + if (uncasted_ptr->value.type->id != ZigTypeIdPointer) { + ir_add_error(ira, uncasted_ptr, + buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&uncasted_ptr->value.type->name))); + return ira->codegen->invalid_instruction; + } + + if (uncasted_ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { + return ir_const_void(ira, source_instr); + } + + if (uncasted_ptr->value.type->data.pointer.is_const && !source_instr->is_gen && !uncasted_ptr->is_gen) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + + IrInstruction *ptr = ir_implicit_cast_result(ira, source_instr->source_node, + uncasted_ptr, uncasted_value->value.type, true); + if (ptr == nullptr) { + ptr = uncasted_ptr; + } + if (type_is_invalid(ptr->value.type)) + 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 (type_is_invalid(value->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, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar || + ptr->value.data.x_ptr.mut == ConstPtrMutInfer) + { + 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); + } + } + if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) { + make_const_ptr_runtime(ira, ptr); + make_const_ptr_runtime(ira, uncasted_ptr); + if (get_codegen_ptr_type(value->value.type) != nullptr && ptr->id == IrInstructionIdAllocaGen) { + uint32_t child_ptr_alignment; + if ((err = resolve_ptr_align(ira, value->value.type, &child_ptr_alignment))) + return ira->codegen->invalid_instruction; + uint32_t parent_ptr_alignment; + if ((err = resolve_ptr_align(ira, ptr->value.type->data.pointer.child_type, &parent_ptr_alignment))) + return ira->codegen->invalid_instruction; + if (child_ptr_alignment > parent_ptr_alignment) { + ptr->value.type = adjust_ptr_child(ira->codegen, ptr->value.type, + adjust_ptr_align(ira->codegen, ptr->value.type->data.pointer.child_type, child_ptr_alignment)); + } + } + } else { + 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; +} + +// Note this is semantically different from what gets run with ir_build_set_nonnull_bit +static IrInstruction *ir_analyze_set_non_null_bit(IrAnalyze *ira, + IrInstruction *source_instr, IrInstruction *base_ptr, IrInstruction *non_null_bit) +{ + ZigType *ptr_type = base_ptr->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + + ZigType *opt_type = ptr_type->data.pointer.child_type; + if (type_is_invalid(opt_type)) + return ira->codegen->invalid_instruction; + + if (opt_type->id != ZigTypeIdOptional) { + ir_add_error(ira, base_ptr, + buf_sprintf("expected optional type, found '%s'", buf_ptr(&opt_type->name))); + return ira->codegen->invalid_instruction; + } + + if (instr_is_comptime(base_ptr)) { + zig_panic("TODO comptime set non null bit"); + } + + assert(handle_is_ptr(opt_type)); + + return ir_build_set_nonnull_bit(&ira->new_irb, + source_instr->scope, source_instr->source_node, base_ptr, non_null_bit, nullptr); +} + +static IrInstruction *analyze_runtime_call(IrAnalyze *ira, FnTypeId *fn_type_id, + IrInstructionCall *call_instruction, ZigFn *fn_entry, IrInstruction *fn_ref, size_t call_param_count, + IrInstruction **casted_args, FnInline fn_inline, IrInstruction *casted_new_stack) +{ + ZigType *return_type = fn_type_id->return_type; + + // For an optional or error union, this result location is a pointer to the payload field. + // For other types this is a base pointer to the value. + IrInstruction *payload_result_loc = nullptr; + // For an optional or error union, this is a base pointer. + // For other types this is the same as payload_result_loc. + IrInstruction *base_result_loc = nullptr; + + bool convert_to_value; + ZigType *scalar_result_type; // of the call instruction + ZigType *payload_result_type; // of the call instruction + if (return_type->id == ZigTypeIdErrorUnion) { + scalar_result_type = get_optional_type(ira->codegen, return_type->data.error_union.err_set_type); + payload_result_type = return_type->data.error_union.payload_type; + convert_to_value = call_instruction->lval != LValErrorUnionVal && call_instruction->lval != LValErrorUnionPtr; + } else if (return_type->id == ZigTypeIdErrorSet) { + scalar_result_type = return_type; + payload_result_type = ira->codegen->builtin_types.entry_void; + convert_to_value = call_instruction->lval != LValErrorUnionVal && call_instruction->lval != LValErrorUnionPtr; + } else if (return_type->id == ZigTypeIdOptional && handle_is_ptr(return_type)) { + scalar_result_type = ira->codegen->builtin_types.entry_bool; + payload_result_type = return_type->data.maybe.child_type; + convert_to_value = call_instruction->lval != LValOptional; + } else { + payload_result_type = return_type; + scalar_result_type = return_type; + convert_to_value = false; + } + + bool need_store_ptr = !convert_to_value && + (return_type == payload_result_type) && !handle_is_ptr(return_type); + + bool need_unwrap_optional = call_instruction->lval == LValOptional && need_store_ptr && + payload_result_type->id == ZigTypeIdOptional; + + IrInstruction *prev_result_loc = nullptr; + if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { + prev_result_loc = call_instruction->result_loc->child; + if (prev_result_loc->value.special == ConstValSpecialStatic && + prev_result_loc->value.data.x_ptr.special == ConstPtrSpecialDiscard) + { + need_store_ptr = false; + convert_to_value = false; + prev_result_loc = nullptr; + } + } else { + need_store_ptr = false; + } + if (prev_result_loc == nullptr) { + if (return_type->id == ZigTypeIdErrorUnion || + (type_has_bits(return_type) && want_first_arg_sret(ira->codegen, fn_type_id))) + { + prev_result_loc = ir_analyze_alloca(ira, &call_instruction->base, nullptr, 0, "", false); + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + } + } + + bool need_copy = false; + + if (prev_result_loc != nullptr) { + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + if (instr_is_comptime(prev_result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, prev_result_loc, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->special = ConstValSpecialRuntime; + } + } + + if (convert_to_value) { + base_result_loc = ir_implicit_cast_result(ira, call_instruction->base.source_node, + prev_result_loc, return_type, false); + if (type_is_invalid(base_result_loc->value.type)) + return ira->codegen->invalid_instruction; + if (return_type->id == ZigTypeIdErrorUnion) { + payload_result_loc = ir_analyze_unwrap_err_payload(ira, &call_instruction->base, + base_result_loc, nullptr, false); + if (type_is_invalid(payload_result_loc->value.type)) + return ira->codegen->invalid_instruction; + } else if (return_type->id == ZigTypeIdErrorSet) { + payload_result_loc = nullptr; + } else if (return_type->id == ZigTypeIdOptional) { + payload_result_loc = ir_analyze_unwrap_optional_payload(ira, &call_instruction->base, + base_result_loc, false); + if (type_is_invalid(payload_result_loc->value.type)) + return ira->codegen->invalid_instruction; + } else { + zig_unreachable(); + } + } else if (return_type->id != ZigTypeIdErrorSet) { + if (need_unwrap_optional) { + payload_result_type = payload_result_type->data.maybe.child_type; + } + payload_result_loc = ir_implicit_cast_result(ira, call_instruction->base.source_node, + prev_result_loc, payload_result_type, true); + if (payload_result_loc == nullptr) { + if (need_store_ptr) { + payload_result_loc = prev_result_loc; + } else { + payload_result_loc = ir_analyze_alloca(ira, &call_instruction->base, payload_result_type, + 0, "", false); + if (type_is_invalid(payload_result_loc->value.type)) + return ira->codegen->invalid_instruction; + payload_result_loc->value.special = ConstValSpecialRuntime; + need_copy = true; } - return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type, - ptr_mut, is_const, is_volatile, var->align_bytes); } + base_result_loc = payload_result_loc; + if (type_is_invalid(payload_result_loc->value.type)) + return ira->codegen->invalid_instruction; } - zig_unreachable(); } -no_mem_slot: + // This instruction, in the case of optional and error union, is always the error code / bool value. + // So if the LVal does not match this, we have to emit more instructions to adhere to the expected + // types. + IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, + call_instruction->base.scope, call_instruction->base.source_node, + fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, + casted_new_stack, payload_result_loc, nullptr, LValNone); + new_call_instruction->value.type = scalar_result_type; + if (scalar_result_type->id == ZigTypeIdUnreachable) + return ir_finish_anal(ira, new_call_instruction); - 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->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); + if (need_copy) { + IrInstruction *loaded = ir_get_deref(ira, &call_instruction->base, payload_result_loc); + if (type_is_invalid(loaded->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, prev_result_loc, loaded); + if (type_is_invalid(store_inst->value.type)) + return ira->codegen->invalid_instruction; + } - bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); - var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; + if (need_store_ptr) { + switch (call_instruction->lval) { + case LValNone: + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); + return new_call_instruction; + case LValPtr: + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); + return payload_result_loc; + case LValErrorUnionVal: { + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); + IrInstruction *err_code_ptr = ir_analyze_error_union_field_error_set(ira, &call_instruction->base, + payload_result_loc, false); + return ir_get_deref(ira, &call_instruction->base, err_code_ptr); + } + case LValErrorUnionPtr: { + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, new_call_instruction); + return ir_analyze_error_union_field_error_set(ira, &call_instruction->base, + payload_result_loc, false); + } + case LValOptional: { + IrInstruction *ref = ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); + IrInstruction *payload_ptr = ir_analyze_unwrap_optional_payload(ira, &call_instruction->base, ref, false); + IrInstruction *payload = ir_get_deref(ira, &call_instruction->base, payload_ptr); + ir_analyze_store_ptr(ira, &call_instruction->base, payload_result_loc, payload); + return ir_analyze_test_non_null(ira, &call_instruction->base, new_call_instruction); + } + } + zig_unreachable(); + } + if (!convert_to_value) { + switch (call_instruction->lval) { + case LValNone: + return new_call_instruction; + case LValPtr: + if (payload_result_loc != nullptr) + return payload_result_loc; + return ir_get_ref(ira, &call_instruction->base, new_call_instruction, true, false, nullptr); + case LValErrorUnionVal: { + if (return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) { + return new_call_instruction; + } else { + return ir_const(ira, &call_instruction->base, ira->codegen->builtin_types.entry_null); + } + } + case LValErrorUnionPtr: { + if (return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) { + ZigType *err_type; + if (return_type->id == ZigTypeIdErrorUnion) { + err_type = return_type->data.error_union.err_set_type; + } else { + assert(return_type->id == ZigTypeIdErrorSet); + err_type = return_type; + } + ZigType *opt_err_type = get_optional_type(ira->codegen, err_type); + IrInstruction *err_code_alloca = ir_analyze_alloca(ira, &call_instruction->base, + opt_err_type, 0, "err", false); + ir_analyze_store_ptr(ira, &call_instruction->base, err_code_alloca, new_call_instruction); + return err_code_alloca; + } else { + IrInstruction *null = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_null); + return ir_get_ref(ira, &call_instruction->base, null, true, false, nullptr); + } + } + case LValOptional: + if (return_type->id == ZigTypeIdOptional) { + return new_call_instruction; + } else { + return ir_const_bool(ira, &call_instruction->base, false); + } + } + zig_unreachable(); + } - return var_ptr_instruction; + if (return_type->id == ZigTypeIdErrorUnion) { + IrInstruction *err_code_ptr = ir_analyze_error_union_field_error_set(ira, + &call_instruction->base, base_result_loc, false); + if (type_is_invalid(err_code_ptr->value.type)) + return ira->codegen->invalid_instruction; + ir_analyze_store_ptr(ira, &call_instruction->base, err_code_ptr, new_call_instruction); + switch (call_instruction->lval) { + case LValNone: + return ir_get_deref(ira, &call_instruction->base, base_result_loc); + case LValPtr: + return base_result_loc; + case LValErrorUnionVal: + case LValErrorUnionPtr: + zig_unreachable(); + case LValOptional: + // This means incorrectly used optional syntax for error union. + zig_panic("TODO"); + } + zig_unreachable(); + } else if (return_type->id == ZigTypeIdErrorSet) { + ir_analyze_store_ptr(ira, &call_instruction->base, base_result_loc, new_call_instruction); + switch (call_instruction->lval) { + case LValNone: + return new_call_instruction; + case LValPtr: + return base_result_loc; + case LValErrorUnionVal: + case LValErrorUnionPtr: + zig_unreachable(); + case LValOptional: + // This means incorrectly used optional syntax for error union. + zig_panic("TODO"); + } + zig_unreachable(); + } else if (return_type->id == ZigTypeIdOptional) { + ir_analyze_set_non_null_bit(ira, &call_instruction->base, base_result_loc, new_call_instruction); + switch (call_instruction->lval) { + case LValNone: + return ir_get_deref(ira, &call_instruction->base, base_result_loc); + case LValPtr: + return base_result_loc; + case LValOptional: + zig_unreachable(); + case LValErrorUnionVal: + case LValErrorUnionPtr: + // This means incorrectly used error union for optional. + zig_panic("TODO"); + } + zig_unreachable(); + } else { + zig_unreachable(); + } } static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, @@ -13270,7 +15402,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) @@ -13286,18 +15418,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; } } @@ -13305,15 +15438,116 @@ 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->type); + copy_const_val(&new_instruction->value, result, true); + + if (call_instruction->result_loc != nullptr && call_instruction->result_loc->child != nullptr) { + IrInstruction *prev_result_loc = call_instruction->result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) return ira->codegen->invalid_instruction; + if (instr_is_comptime(prev_result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, prev_result_loc, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->special = ConstValSpecialStatic; + } + } + + switch (call_instruction->lval) { + case LValNone: { + IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, + prev_result_loc, new_instruction); + if (type_is_invalid(store_inst->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, ir_const_void(ira, &call_instruction->base)); + } + case LValErrorUnionVal: + case LValErrorUnionPtr: { + IrInstruction *ref_inst = ir_get_ref(ira, &call_instruction->base, new_instruction, + true, false, nullptr); + if (type_is_invalid(ref_inst->value.type)) + return ira->codegen->invalid_instruction; + if (new_instruction->value.type->id == ZigTypeIdErrorSet) { + IrInstruction *undef = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_undef); + ir_analyze_store_ptr(ira, &call_instruction->base, prev_result_loc, undef); + return ir_finish_anal(ira, ref_inst); + } + IrInstruction *payload_ptr = ir_analyze_unwrap_err_payload(ira, + &call_instruction->base, ref_inst, nullptr, false); + if (type_is_invalid(payload_ptr->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *payload = ir_get_deref(ira, &call_instruction->base, payload_ptr); + if (type_is_invalid(payload->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, + prev_result_loc, payload); + if (type_is_invalid(store_inst->value.type)) + return ira->codegen->invalid_instruction; + assert(new_instruction->value.type->id == ZigTypeIdErrorUnion); + ZigType *err_set_type = new_instruction->value.type->data.error_union.err_set_type; + ZigType *opt_err_set_type = get_optional_type(ira->codegen, err_set_type); + IrInstruction *err_code_ptr = ir_analyze_result_error_union_code(ira, ref_inst, + opt_err_set_type); + if (type_is_invalid(err_code_ptr->value.type)) + return ira->codegen->invalid_instruction; + if (call_instruction->lval == LValErrorUnionPtr) + return ir_finish_anal(ira, err_code_ptr); + IrInstruction *err_code = ir_get_deref(ira, &call_instruction->base, err_code_ptr); + if (type_is_invalid(err_code->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, err_code); + } + case LValOptional: { + IrInstruction *ref_inst = ir_get_ref(ira, &call_instruction->base, new_instruction, + true, false, nullptr); + if (type_is_invalid(ref_inst->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *payload_ptr = ir_analyze_unwrap_optional_payload(ira, + &call_instruction->base, ref_inst, false); + if (type_is_invalid(payload_ptr->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *payload = ir_get_deref(ira, &call_instruction->base, payload_ptr); + if (type_is_invalid(payload->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *store_inst = ir_analyze_store_ptr(ira, &call_instruction->base, + prev_result_loc, payload); + if (type_is_invalid(store_inst->value.type)) + return ira->codegen->invalid_instruction; + assert(new_instruction->value.type->id == ZigTypeIdOptional); + IrInstruction *is_non_null = ir_analyze_test_non_null(ira, &call_instruction->base, + new_instruction); + if (type_is_invalid(is_non_null->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, is_non_null); + } + case LValPtr: + zig_panic("TODO"); + } + zig_unreachable(); } - IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->value.type); - // TODO should we use copy_const_val? - new_instruction->value = result->value; - new_instruction->value.type = return_type; - return ir_finish_anal(ira, new_instruction); + switch (call_instruction->lval) { + case LValNone: + return ir_finish_anal(ira, new_instruction); + case LValPtr: { + IrInstruction *ref_inst = ir_get_ref(ira, &call_instruction->base, new_instruction, + true, false, nullptr); + return ir_finish_anal(ira, ref_inst); + } + case LValErrorUnionPtr: + zig_unreachable(); + case LValErrorUnionVal: + zig_unreachable(); + case LValOptional: + zig_unreachable(); + } + zig_unreachable(); } IrInstruction *casted_new_stack = nullptr; @@ -13470,18 +15704,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; } @@ -13559,29 +15796,20 @@ 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); - ir_add_alloca(ira, result, result->value.type); return ir_finish_anal(ira, result); } - assert(async_allocator_inst == nullptr); - IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, - 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; - - ir_add_alloca(ira, new_call_instruction, return_type); - - return ir_finish_anal(ira, new_call_instruction); + return analyze_runtime_call(ira, impl_fn_type_id, call_instruction, impl_fn, + nullptr, impl_param_count, casted_args, fn_inline, casted_new_stack); } ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); @@ -13664,7 +15892,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref, casted_args, call_param_count, async_allocator_inst); - ir_add_alloca(ira, result, result->value.type); return ir_finish_anal(ira, result); } @@ -13674,12 +15901,8 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call return ira->codegen->invalid_instruction; } - IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb, - call_instruction->base.scope, call_instruction->base.source_node, - fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack); - new_call_instruction->value.type = return_type; - ir_add_alloca(ira, new_call_instruction, return_type); - return ir_finish_anal(ira, new_call_instruction); + return analyze_runtime_call(ira, fn_type_id, call_instruction, fn_entry, fn_ref, + call_param_count, casted_args, fn_inline, casted_new_stack); } static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *call_instruction) { @@ -13704,12 +15927,100 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC return ira->codegen->invalid_instruction; } - IrInstruction *arg = call_instruction->args[0]->child; + IrInstruction *first_arg_result_loc = (call_instruction->first_arg_result_loc == nullptr) ? + nullptr : call_instruction->first_arg_result_loc->child; + if (first_arg_result_loc != nullptr) { + IrInstruction *result_loc = call_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + result_loc = resolve_possible_alloca_inference(ira, result_loc, dest_type); + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + + if (is_slice(dest_type)) { + if (type_is_invalid(first_arg_result_loc->value.type)) + return ira->codegen->invalid_instruction; + // If this is a slice cast, this is the equivalent of a container init fields instruction. + // Deal with ConstPtrMutInfer. + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(first_arg_result_loc)) { + result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result_loc->value.special = ConstValSpecialRuntime; + } + } + return ir_const_void(ira, &call_instruction->base); + } + + switch (call_instruction->lval) { + case LValPtr: + zig_unreachable(); + case LValNone: { + IrInstruction *deref = ir_get_deref(ira, &call_instruction->base, result_loc); + if (type_is_invalid(deref->value.type)) + return ira->codegen->invalid_instruction; + return ir_finish_anal(ira, deref); + } + case LValErrorUnionVal: { + assert(result_loc != nullptr); + IrInstruction *only_arg = call_instruction->args[0]->child; + if (type_is_invalid(only_arg->value.type)) + return ira->codegen->invalid_instruction; + if (only_arg->value.type->id == ZigTypeIdErrorSet) { + IrInstruction *undef = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_undef); + ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, undef); + return ir_finish_anal(ira, only_arg); + } else if (only_arg->value.type->id == ZigTypeIdErrorUnion) { + zig_panic("TODO"); + } else { + IrInstruction *null = ir_const(ira, &call_instruction->base, + ira->codegen->builtin_types.entry_null); + return ir_finish_anal(ira, null); + } + } + case LValOptional: { + assert(result_loc != nullptr); + IrInstruction *only_arg = call_instruction->args[0]->child; + if (type_is_invalid(only_arg->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *true_value = ir_const_bool(ira, &call_instruction->base, true); + return ir_finish_anal(ira, true_value); + } + case LValErrorUnionPtr: + zig_panic("TODO"); + } + } + IrInstruction *arg_value = call_instruction->args[0]->child; + if (type_is_invalid(arg_value->value.type)) + return ira->codegen->invalid_instruction; + IrInstruction *result = ir_implicit_cast(ira, arg_value, dest_type); + + if (call_instruction->result_loc == nullptr || call_instruction->result_loc->child == nullptr) { + switch (call_instruction->lval) { + case LValNone: + return ir_finish_anal(ira, result); + case LValPtr: { + IrInstruction *ptr_to_result = ir_get_ref(ira, &call_instruction->base, result, + true, false, nullptr); + return ir_finish_anal(ira, ptr_to_result); + } + case LValErrorUnionVal: + case LValErrorUnionPtr: + case LValOptional: + zig_unreachable(); + } + } - IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg); - if (type_is_invalid(cast_instruction->value.type)) + // we must store into the result loc + IrInstruction *result_loc = call_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; - return ir_finish_anal(ira, cast_instruction); + result_loc = resolve_possible_alloca_inference(ira, result_loc, dest_type); + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + ir_analyze_store_ptr(ira, &call_instruction->base, result_loc, result); + return result; } else if (fn_ref->value.type->id == ZigTypeIdFn) { ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref); if (fn_table_entry == nullptr) @@ -13774,6 +16085,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", @@ -13806,6 +16124,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: @@ -14001,9 +16322,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 (casted_condition == ira->codegen->invalid_instruction) + 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 ? @@ -14022,10 +16348,22 @@ 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); + // The if condition is not comptime known. This means we must mark the result location + // as runtime known, in case it is ConstPtrMutInfer. + if (cond_br_instruction->result_loc != nullptr) { + IrInstruction *result_loc = cond_br_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ir_unreach_error(ira); + if (instr_is_comptime(result_loc)) { + ConstExprValue *result_ptr = ir_resolve_const(ira, result_loc, UndefBad); + if (result_ptr == nullptr) + return ir_unreach_error(ira); + assert(result_ptr->type->id == ZigTypeIdPointer); + if (result_ptr->data.x_ptr.mut == ConstPtrMutInfer) { + result_ptr->special = ConstValSpecialRuntime; + } + } + } 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); @@ -14038,7 +16376,7 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi IrInstruction *result = ir_build_cond_br(&ira->new_irb, cond_br_instruction->base.scope, cond_br_instruction->base.source_node, - casted_condition, new_then_block, new_else_block, nullptr); + casted_condition, new_then_block, new_else_block, nullptr, nullptr); result->value.type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } @@ -14065,8 +16403,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; @@ -14115,14 +16452,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); @@ -14179,33 +16526,6 @@ static IrInstruction *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructi return result; } -static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra(g, - ptr_type->data.pointer.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_type->data.pointer.ptr_len, - new_align, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); -} - -static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { - assert(is_slice(slice_type)); - ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry, - new_align); - return get_slice_type(g, ptr_type); -} - -static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra(g, - ptr_type->data.pointer.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_len, - ptr_type->data.pointer.explicit_alignment, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes); -} - static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { Error err; IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->child; @@ -14369,13 +16689,32 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct if (array_ptr_val == nullptr) return ira->codegen->invalid_instruction; + if (array_ptr_val->special == ConstValSpecialUndef && array_type->id == ZigTypeIdArray) { + array_ptr_val->data.x_array.special = ConstArraySpecialNone; + array_ptr_val->data.x_array.data.s_none.elements = create_const_vals(array_type->data.array.len); + array_ptr_val->special = ConstValSpecialStatic; + for (size_t i = 0; i != array_type->data.array.len; i += 1) { + ConstExprValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i]; + elem_val->special = ConstValSpecialUndef; + elem_val->type = array_type->data.array.child_type; + elem_val->parent.id = ConstParentIdArray; + elem_val->parent.data.p_array.array_val = array_ptr_val; + elem_val->parent.data.p_array.elem_index = i; + } + } + if (array_ptr_val->special != ConstValSpecialRuntime && (array_type->id != ZigTypeIdPointer || array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) { if (array_type->id == ZigTypeIdPointer) { - IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); + IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, + elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, safety_check_on, + elem_ptr_instruction->ptr_len); + result->is_gen = array_ptr->is_gen; + result->value.type = return_type; ConstExprValue *out_val = &result->value; + out_val->special = ConstValSpecialStatic; out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; size_t new_index; size_t mem_size; @@ -14412,10 +16751,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, @@ -14428,11 +16775,13 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, false, elem_ptr_instruction->ptr_len); + result->is_gen = array_ptr->is_gen; result->value.type = return_type; return result; } ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index]; IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); + result->is_gen = array_ptr->is_gen; ConstExprValue *out_val = &result->value; uint64_t slice_len = bigint_as_unsigned(&len_field->data.x_bigint); if (index >= slice_len) { @@ -14465,14 +16814,32 @@ 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) { - IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); + IrInstruction *result; + if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, + elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, + false, elem_ptr_instruction->ptr_len); + result->value.type = return_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, &elem_ptr_instruction->base, return_type); + } + result->is_gen = array_ptr->is_gen; ConstExprValue *out_val = &result->value; out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut; @@ -14512,6 +16879,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, safety_check_on, elem_ptr_instruction->ptr_len); + result->is_gen = array_ptr->is_gen; result->value.type = return_type; return result; } @@ -14536,7 +16904,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, IrInstruction *bound_fn_value = ir_build_const_bound_fn(&ira->new_irb, source_instr->scope, source_instr->source_node, fn_entry, container_ptr); - return ir_get_ref(ira, source_instr, bound_fn_value, true, false); + return ir_get_ref(ira, source_instr, bound_fn_value, true, false, nullptr); } } const char *prefix_name; @@ -14557,7 +16925,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, } static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, - IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type) + IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type, bool initialize) { Error err; @@ -14588,14 +16956,39 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ return ira->codegen->invalid_instruction; if (type_is_invalid(struct_val->type)) return ira->codegen->invalid_instruction; + + if (struct_val->special == ConstValSpecialUndef) { + struct_val->data.x_struct.fields = create_const_vals(bare_type->data.structure.src_field_count); + struct_val->special = ConstValSpecialStatic; + for (size_t i = 0; i < bare_type->data.structure.src_field_count; i += 1) { + ConstExprValue *field_val = &struct_val->data.x_struct.fields[i]; + field_val->special = ConstValSpecialUndef; + field_val->type = bare_type->data.structure.fields[i].type_entry; + ConstParent *parent = get_const_val_parent(ira->codegen, field_val); + if (parent != nullptr) { + parent->id = ConstParentIdStruct; + parent->data.p_struct.struct_val = struct_val; + parent->data.p_struct.field_index = i; + } + } + } + assert(struct_val->special != ConstValSpecialUndef); ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), (uint32_t)host_int_bytes_for_result_type); - IrInstruction *result = ir_const(ira, source_instr, ptr_type); + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, container_ptr, field); + result->value.type = ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, ptr_type); + } ConstExprValue *const_val = &result->value; const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; - const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; + const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; const_val->data.x_ptr.data.base_struct.struct_val = struct_val; const_val->data.x_ptr.data.base_struct.field_index = field->src_index; return result; @@ -14631,22 +17024,35 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ if (type_is_invalid(union_val->type)) return ira->codegen->invalid_instruction; - TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); - if (actual_field == nullptr) - zig_unreachable(); + if (union_val->special == ConstValSpecialUndef || initialize) { + ConstExprValue *payload_val = create_const_vals(1); + payload_val->special = ConstValSpecialUndef; + payload_val->type = field->type_entry; + ConstParent *parent = get_const_val_parent(ira->codegen, payload_val); + if (parent != nullptr) { + parent->id = ConstParentIdUnion; + parent->data.p_union.union_val = union_val; + } - if (field != actual_field) { - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), - buf_ptr(actual_field->name))); - return ira->codegen->invalid_instruction; - } + union_val->special = ConstValSpecialStatic; + bigint_init_bigint(&union_val->data.x_union.tag, &field->enum_field->value); + union_val->data.x_union.payload = payload_val; + } else { + TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); + if (actual_field == nullptr) + zig_unreachable(); + if (field != actual_field) { + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), + buf_ptr(actual_field->name))); + return ira->codegen->invalid_instruction; + } + } ConstExprValue *payload_val = union_val->data.x_union.payload; ZigType *field_type = field->type_entry; - if (field_type->id == ZigTypeIdVoid) { - assert(payload_val == nullptr); + if (field_type->id == ZigTypeIdVoid && payload_val == nullptr) { payload_val = create_const_vals(1); payload_val->special = ConstValSpecialStatic; payload_val->type = field_type; @@ -14655,16 +17061,27 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, PtrLenSingle, 0, 0, 0); - IrInstruction *result = ir_const(ira, source_instr, ptr_type); + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, container_ptr, field, + IrInstructionUnionFieldPtrIdResultPtr); + result->value.type = ptr_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, ptr_type); + } ConstExprValue *const_val = &result->value; const_val->data.x_ptr.special = ConstPtrSpecialRef; - const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; + const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; const_val->data.x_ptr.data.ref.pointee = payload_val; return result; } } - IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); + IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, container_ptr, field, + initialize ? IrInstructionUnionFieldPtrIdResultPtr : IrInstructionUnionFieldPtrIdRef); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, PtrLenSingle, 0, 0, 0); return result; @@ -14758,7 +17175,37 @@ static ErrorTableEntry *find_err_table_entry(ZigType *err_set_type, Buf *field_n return nullptr; } -static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { +static IrInstruction *ir_analyze_error_literal(IrAnalyze *ira, IrInstruction *source_instr, Buf *name) { + ErrorTableEntry *err_entry; + auto existing_entry = ira->codegen->error_table.maybe_get(name); + if (existing_entry) { + err_entry = existing_entry->value; + } else { + err_entry = allocate(1); + err_entry->decl_node = source_instr->source_node; + buf_init_from_buf(&err_entry->name, name); + size_t error_value_count = ira->codegen->errors_by_index.length; + assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); + err_entry->value = error_value_count; + ira->codegen->errors_by_index.append(err_entry); + ira->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(ira->codegen->dbuilder, + buf_ptr(name), error_value_count)); + ira->codegen->error_table.put(name, err_entry); + } + if (err_entry->set_with_only_this_in_it == nullptr) { + err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen, + source_instr->scope, source_instr->source_node, err_entry); + } + ZigType *err_set_type = err_entry->set_with_only_this_in_it; + + IrInstruction *result = ir_const(ira, source_instr, err_set_type); + result->value.data.x_err_set = err_entry; + return result; +} + +static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, + IrInstructionFieldPtr *field_ptr_instruction) +{ Error err; IrInstruction *container_ptr = field_ptr_instruction->container_ptr->child; if (type_is_invalid(container_ptr->value.type)) @@ -14770,6 +17217,16 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc buf_ptr(&container_ptr->value.type->name))); return ira->codegen->invalid_instruction; } + + if (field_ptr_instruction->container_type != nullptr) { + ZigType *container_type = ir_resolve_type(ira, field_ptr_instruction->container_type->child); + if (type_is_invalid(container_type)) + return ira->codegen->invalid_instruction; + container_ptr = resolve_possible_alloca_inference(ira, container_ptr, container_type); + if (type_is_invalid(container_ptr->value.type)) + return ira->codegen->invalid_instruction; + } + ZigType *container_type = container_ptr->value.type->data.pointer.child_type; Buf *field_name = field_ptr_instruction->field_name_buffer; @@ -14790,10 +17247,12 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc if (container_type->id == ZigTypeIdPointer) { ZigType *bare_type = container_ref_type(container_type); IrInstruction *container_child = ir_get_deref(ira, &field_ptr_instruction->base, container_ptr); - IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_child, bare_type); + IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, + container_child, bare_type, field_ptr_instruction->initialize); return result; } else { - IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_ptr, container_type); + IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, + container_ptr, container_type, field_ptr_instruction->initialize); return result; } } else if (is_array_ref(container_type)) { @@ -14909,49 +17368,28 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->invalid_instruction; } else if (child_type->id == ZigTypeIdErrorSet) { - ErrorTableEntry *err_entry; - ZigType *err_set_type; + bool ptr_is_const = true; + bool ptr_is_volatile = false; if (type_is_global_error_set(child_type)) { - auto existing_entry = ira->codegen->error_table.maybe_get(field_name); - if (existing_entry) { - err_entry = existing_entry->value; - } else { - err_entry = allocate(1); - err_entry->decl_node = field_ptr_instruction->base.source_node; - buf_init_from_buf(&err_entry->name, field_name); - size_t error_value_count = ira->codegen->errors_by_index.length; - assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); - err_entry->value = error_value_count; - ira->codegen->errors_by_index.append(err_entry); - ira->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(ira->codegen->dbuilder, - buf_ptr(field_name), error_value_count)); - ira->codegen->error_table.put(field_name, err_entry); - } - if (err_entry->set_with_only_this_in_it == nullptr) { - err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen, - field_ptr_instruction->base.scope, field_ptr_instruction->base.source_node, - err_entry); - } - err_set_type = err_entry->set_with_only_this_in_it; - } else { - if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.source_node)) { - return ira->codegen->invalid_instruction; - } - err_entry = find_err_table_entry(child_type, field_name); - if (err_entry == nullptr) { - ir_add_error(ira, &field_ptr_instruction->base, - buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name))); - return ira->codegen->invalid_instruction; - } - err_set_type = child_type; + IrInstruction *value = ir_analyze_error_literal(ira, &field_ptr_instruction->base, field_name); + return ir_get_ref(ira, &field_ptr_instruction->base, value, + ptr_is_const, ptr_is_volatile, nullptr); + } + if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.source_node)) { + return ira->codegen->invalid_instruction; + } + ErrorTableEntry *err_entry = find_err_table_entry(child_type, field_name); + if (err_entry == nullptr) { + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name))); + return ira->codegen->invalid_instruction; } + ZigType *err_set_type = child_type; ConstExprValue *const_val = create_const_vals(1); const_val->special = ConstValSpecialStatic; const_val->type = err_set_type; const_val->data.x_err_set = err_entry; - bool ptr_is_const = true; - bool ptr_is_volatile = false; return ir_get_const_ptr(ira, &field_ptr_instruction->base, const_val, err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); } else if (child_type->id == ZigTypeIdInt) { @@ -15155,74 +17593,36 @@ 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 (ptr == nullptr) + return ir_const_void(ira, &instruction->base); 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; - } - - 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) - return ira->codegen->invalid_instruction; + return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); +} - 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; +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; - return ira->codegen->invalid_instruction; - } - } + IrInstruction *deref = ir_get_deref(ira, &instruction->base, ptr); + if (instruction->result_loc == nullptr || instruction->result_loc->child == nullptr) + return deref; - 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; + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + + ir_analyze_store_ptr(ira, &instruction->base, result_loc, deref); + + return deref; } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { @@ -15693,11 +18093,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) { @@ -15706,60 +18102,73 @@ 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) { + if (!safety_check_on) + return base_ptr; + 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")); + if (!safety_check_on) { + IrInstruction *undef = ir_const_undef(ira, source_instr); + IrInstruction *casted_undef = ir_implicit_cast(ira, undef, child_type); + return ir_get_ref(ira, source_instr, casted_undef, true, false, nullptr); + } + 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; @@ -15768,13 +18177,61 @@ 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_optional_unwrap_val(IrAnalyze *ira, + IrInstructionOptionalUnwrapVal *instruction) +{ + IrInstruction *opt = instruction->opt->child; + if (type_is_invalid(opt->value.type)) + return ira->codegen->invalid_instruction; + + ZigType *opt_type = opt->value.type; + if (opt_type->id != ZigTypeIdOptional) { + ir_add_error_node(ira, opt->source_node, + buf_sprintf("expected optional type, found '%s'", buf_ptr(&opt_type->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *child_type = opt_type->data.maybe.child_type; + if (instr_is_comptime(opt)) { + ConstExprValue *opt_val = ir_resolve_const(ira, opt, UndefBad); + if (!opt_val) + return ira->codegen->invalid_instruction; + if (optional_value_is_null(opt_val)) { + ir_add_error(ira, &instruction->base, buf_sprintf("unable to unwrap null")); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_const(ira, &instruction->base, child_type); + if (types_have_same_zig_comptime_repr(opt_val->type, child_type)) { + copy_const_val(&result->value, opt_val, false); + } else { + copy_const_val(&result->value, opt_val->data.x_optional, false); + } + result->value.type = child_type; + return result; + } + + IrInstruction *result = ir_build_optional_unwrap_val(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, opt, instruction->safety_check_on); + result->value.type = child_type; + return result; +} + static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) { IrInstruction *value = ctz_instruction->value->child; if (type_is_invalid(value->value.type)) { @@ -16012,10 +18469,27 @@ static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira, new_case->block->ref_instruction = &switch_br_instruction->base; } + // The switch target is not comptime known. This means we must mark the result location + // as runtime known, in case it is ConstPtrMutInfer. + if (switch_br_instruction->result_loc != nullptr) { + IrInstruction *result_loc = switch_br_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ir_unreach_error(ira); + if (instr_is_comptime(result_loc)) { + ConstExprValue *result_ptr = ir_resolve_const(ira, result_loc, UndefBad); + if (result_ptr == nullptr) + return ir_unreach_error(ira); + assert(result_ptr->type->id == ZigTypeIdPointer); + if (result_ptr->data.x_ptr.mut == ConstPtrMutInfer) { + result_ptr->special = ConstValSpecialRuntime; + } + } + } + IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base); IrInstruction *result = ir_build_switch_br(&ira->new_irb, switch_br_instruction->base.scope, switch_br_instruction->base.source_node, - target_value, new_else_block, case_count, cases, nullptr, nullptr); + target_value, new_else_block, case_count, cases, nullptr, nullptr, nullptr); result->value.type = ira->codegen->builtin_types.entry_unreachable; return ir_finish_anal(ira, result); } @@ -16075,9 +18549,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; } @@ -16107,8 +18579,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, @@ -16132,8 +18603,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; } @@ -16193,7 +18663,8 @@ static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstru } IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, target_value_ptr, field); + instruction->base.scope, instruction->base.source_node, target_value_ptr, field, + IrInstructionUnionFieldPtrIdSwitch); result->value.type = get_pointer_to_type(ira->codegen, field->type_entry, target_value_ptr->value.type->data.pointer.is_const); return result; @@ -16281,16 +18752,24 @@ static IrInstruction *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRe IrInstruction *value = ref_instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - return ir_get_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile); + IrInstruction *result_loc = nullptr; + if (ref_instruction->result_loc != nullptr) { + result_loc = ref_instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + } + return ir_get_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile, + result_loc); } static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction, - ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields) + ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields, + IrInstruction *result_loc) { 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) { @@ -16300,8 +18779,8 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI } IrInstructionContainerInitFieldsField *field = &fields[0]; - IrInstruction *field_value = field->value->child; - if (type_is_invalid(field_value->value.type)) + IrInstruction *field_result_loc = field->result_loc->child; + if (type_is_invalid(field_result_loc->value.type)) return ira->codegen->invalid_instruction; TypeUnionField *type_field = find_union_type_field(container_type, field->name); @@ -16315,49 +18794,25 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI if (type_is_invalid(type_field->type_entry)) return ira->codegen->invalid_instruction; - IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry); - if (casted_field_value == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; - - if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_instruction; - - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); - if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || - !type_has_bits(casted_field_value->value.type)) - { - ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); - if (!field_val) - return ira->codegen->invalid_instruction; - - IrInstruction *result = ir_const(ira, instruction, container_type); - 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; + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(field_result_loc) && + field_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result_loc->value.special = ConstValSpecialRuntime; } - - return result; } - - IrInstruction *new_instruction = ir_build_union_init(&ira->new_irb, - instruction->scope, instruction->source_node, - container_type, type_field, casted_field_value); - new_instruction->value.type = container_type; - ir_add_alloca(ira, new_instruction, container_type); - return new_instruction; + return ir_const_void(ira, instruction); } static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction, - ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields) + ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields, + IrInstruction *result_loc) { - Error err; if (container_type->id == ZigTypeIdUnion) { - return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields); + return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, + fields, result_loc); } if (container_type->id != ZigTypeIdStruct || is_slice(container_type)) { ir_add_error(ira, instruction, @@ -16366,28 +18821,26 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; } - if ((err = ensure_complete_type(ira->codegen, container_type))) - return ira->codegen->invalid_instruction; - size_t actual_field_count = container_type->data.structure.src_field_count; - - IrInstruction *first_non_const_instruction = nullptr; - AstNode **field_assign_nodes = allocate(actual_field_count); + ZigList const_ptrs = {}; + + // Here we iterate over the fields that have been initialized, and emit + // compile errors for missing fields and duplicate fields. + // It is only now that we find out whether the struct initialization can be a comptime + // value, but we have already emitted runtime instructions for the fields that + // were initialized with runtime values, and have omitted instructions that would have + // initialized fields with comptime values. + // So now we must clean up this situation. If it turns out the struct initialization can + // be a comptime value, overwrite ConstPtrMutInfer with ConstPtrMutComptimeConst. + // Otherwise, we must emit instructions to runtime-initialize the fields that have + // comptime-known values. - IrInstructionStructInitField *new_fields = allocate(actual_field_count); - - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); - - ConstExprValue const_val = {}; - const_val.special = ConstValSpecialStatic; - const_val.type = container_type; - const_val.data.x_struct.fields = create_const_vals(actual_field_count); for (size_t i = 0; i < instr_field_count; i += 1) { IrInstructionContainerInitFieldsField *field = &fields[i]; - IrInstruction *field_value = field->value->child; - if (type_is_invalid(field_value->value.type)) + IrInstruction *field_result_loc = field->result_loc->child; + if (type_is_invalid(field_result_loc->value.type)) return ira->codegen->invalid_instruction; TypeStructField *type_field = find_struct_type_field(container_type, field->name); @@ -16401,10 +18854,6 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (type_is_invalid(type_field->type_entry)) return ira->codegen->invalid_instruction; - IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry); - if (casted_field_value == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; - size_t field_index = type_field->src_index; AstNode *existing_assign_node = field_assign_nodes[field_index]; if (existing_assign_node) { @@ -16414,20 +18863,10 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc } field_assign_nodes[field_index] = field->source_node; - new_fields[field_index].value = casted_field_value; - new_fields[field_index].type_struct_field = type_field; - - if (const_val.special == ConstValSpecialStatic) { - if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) { - ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); - if (!field_val) - return ira->codegen->invalid_instruction; - - copy_const_val(&const_val.data.x_struct.fields[field_index], field_val, true); - } else { - first_non_const_instruction = casted_field_value; - const_val.special = ConstValSpecialRuntime; - } + if (instr_is_comptime(field_result_loc) && + field_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + const_ptrs.append(field_result_loc); } } @@ -16442,171 +18881,127 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (any_missing) return ira->codegen->invalid_instruction; - 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; - - for (size_t i = 0; i < instr_field_count; i += 1) { - ConstExprValue *field_val = &out_val->data.x_struct.fields[i]; - ConstParent *parent = get_const_val_parent(ira->codegen, field_val); - if (parent != nullptr) { - parent->id = ConstParentIdStruct; - parent->data.p_struct.field_index = i; - parent->data.p_struct.struct_val = out_val; + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (const_ptrs.length == actual_field_count) { + result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result_loc->value.special = ConstValSpecialRuntime; + for (size_t i = 0; i < const_ptrs.length; i += 1) { + IrInstruction *field_result_loc = const_ptrs.at(i); + IrInstruction *deref = ir_get_deref(ira, field_result_loc, field_result_loc); + field_result_loc->value.special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, field_result_loc, field_result_loc, deref); } } - - 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; } - IrInstruction *new_instruction = ir_build_struct_init(&ira->new_irb, - instruction->scope, instruction->source_node, - container_type, actual_field_count, new_fields); - new_instruction->value.type = container_type; - ir_add_alloca(ira, new_instruction, container_type); - return new_instruction; + return ir_const_void(ira, instruction); } 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); - - 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); + // handled by IrInstructionInferArrayType + assert(!is_slice(container_type)); - 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); + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); + size_t elem_count = instruction->elem_count; - IrInstruction **new_items = allocate(elem_count); + if (container_type->id == ZigTypeIdStruct && elem_count == 0) + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, result_loc); - IrInstruction *first_non_const_instruction = nullptr; + 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); + } - 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; + if (container_type->id != ZigTypeIdArray) { + 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; + } - IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); - if (casted_arg == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; + // array is same as slice init but we make a compile error if the length is wrong + ZigType *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); - new_items[i] = casted_arg; + 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; + } - 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; + // The Result Location Mechanism has already emitted runtime instructions to + // initialize runtime elements and has omitted instructions for the comptime + // elements. However it is only now that we find out whether the array initialization + // can be a comptime value. So we must clean up the situation. If it turns out + // array initialization can be a comptime value, overwrite ConstPtrMutInfer with + // ConstPtrMutComptimeConst. Otherwise, emit instructions to runtime-initialize the + // elements that have comptime-known values. + ZigList const_ptrs = {}; - 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; - } - } - } + for (size_t i = 0; i < elem_count; i += 1) { + IrInstruction *elem_result_loc = instruction->elem_result_loc_list[i]->child; + if (type_is_invalid(elem_result_loc->value.type)) + return ira->codegen->invalid_instruction; - 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; - } - } - return result; - } + assert(elem_result_loc->value.type->id == ZigTypeIdPointer); - 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 (instr_is_comptime(elem_result_loc) && + elem_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *elem_val = const_ptr_pointee_unchecked(ira->codegen, &elem_result_loc->value); + if (value_is_comptime(elem_val)) { + const_ptrs.append(elem_result_loc); } + } + } - 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); + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (const_ptrs.length == elem_count) { + result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; } else { - 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; + make_const_ptr_runtime(ira, result_loc); + for (size_t i = 0; i < const_ptrs.length; i += 1) { + IrInstruction *elem_result_loc = const_ptrs.at(i); + assert(elem_result_loc->value.special == ConstValSpecialStatic); + IrInstruction *deref = ir_get_deref(ira, elem_result_loc, elem_result_loc); + make_const_ptr_runtime(ira, elem_result_loc); + ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref); + } } - } else { - ir_add_error(ira, container_type_value, - buf_sprintf("expected type, found '%s' value", buf_ptr(&container_type_value->value.type->name))); - return ira->codegen->invalid_instruction; } + + return ir_const_void(ira, &instruction->base); } -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)) return ira->codegen->invalid_instruction; + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, - instruction->field_count, instruction->fields); + instruction->field_count, instruction->fields, result_loc); } static IrInstruction *ir_analyze_instruction_compile_err(IrAnalyze *ira, @@ -16659,7 +19054,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; @@ -16920,10 +19315,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) { @@ -16978,7 +19374,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); @@ -17009,33 +19404,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; } @@ -17055,8 +19447,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; @@ -17120,7 +19512,6 @@ 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); @@ -17131,9 +19522,9 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco 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; @@ -17426,7 +19817,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); @@ -17436,9 +19826,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); @@ -17465,7 +19855,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); @@ -17489,9 +19878,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; @@ -17560,7 +19949,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); @@ -17593,9 +19981,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); @@ -17635,7 +20023,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); @@ -17669,9 +20056,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); @@ -17741,7 +20128,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); @@ -17778,9 +20164,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; @@ -17824,8 +20210,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; @@ -17897,10 +20283,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); @@ -18050,7 +20436,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; @@ -18059,6 +20445,13 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + result_loc = resolve_possible_alloca_inference(ira, result_loc, operand_type); + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + // TODO let this be volatile ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type); @@ -18122,11 +20515,15 @@ 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); - result->value.type = get_optional_type(ira->codegen, operand_type); - ir_add_alloca(ira, result, result->value.type); + if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + result_loc->value.special = ConstValSpecialRuntime; + } + + IrInstruction *result = ir_build_cmpxchg_gen(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, + operand_type, casted_ptr, casted_cmp_value, casted_new_value, + success_order, failure_order, result_loc, instruction->is_weak); + result->value.type = ira->codegen->builtin_types.entry_bool; return result; } @@ -18263,167 +20660,51 @@ static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstru } } - if (target->value.type->id != ZigTypeIdFloat) { - ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'", - buf_ptr(&target->value.type->name))); - return ira->codegen->invalid_instruction; - } - - return ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type); -} - -static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) { - 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; - - if (target->value.type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, instruction->target, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value.type->name))); - return ira->codegen->invalid_instruction; - } - - 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; - - 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; - - IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) - return ira->codegen->invalid_instruction; - - bool src_ptr_const; - bool src_ptr_volatile; - uint32_t src_ptr_align; - if (target->value.type->id == ZigTypeIdPointer) { - src_ptr_const = target->value.type->data.pointer.is_const; - src_ptr_volatile = target->value.type->data.pointer.is_volatile; - - if ((err = resolve_ptr_align(ira, target->value.type, &src_ptr_align))) - return ira->codegen->invalid_instruction; - } else if (is_slice(target->value.type)) { - ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; - src_ptr_const = src_ptr_type->data.pointer.is_const; - src_ptr_volatile = src_ptr_type->data.pointer.is_volatile; - - if ((err = resolve_ptr_align(ira, src_ptr_type, &src_ptr_align))) - return ira->codegen->invalid_instruction; - } else { - src_ptr_const = true; - src_ptr_volatile = false; - - if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_instruction; - - src_ptr_align = get_abi_alignment(ira->codegen, target->value.type); - } - - if ((err = type_resolve(ira->codegen, dest_child_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_instruction; - - ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_child_type, - src_ptr_const, src_ptr_volatile, PtrLenUnknown, - src_ptr_align, 0, 0); - ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); - - ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - src_ptr_const, src_ptr_volatile, PtrLenUnknown, - src_ptr_align, 0, 0); - ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); - - IrInstruction *casted_value = ir_implicit_cast(ira, target, u8_slice); - if (type_is_invalid(casted_value->value.type)) - return ira->codegen->invalid_instruction; - - bool have_known_len = false; - uint64_t known_len; - - if (instr_is_comptime(casted_value)) { - ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); - if (!val) - return ira->codegen->invalid_instruction; - - ConstExprValue *len_val = &val->data.x_struct.fields[slice_len_index]; - if (value_is_comptime(len_val)) { - known_len = bigint_as_unsigned(&len_val->data.x_bigint); - have_known_len = true; - } - } - - if (casted_value->value.data.rh_slice.id == RuntimeHintSliceIdLen) { - known_len = casted_value->value.data.rh_slice.len; - have_known_len = true; - } - - if (have_known_len) { - uint64_t child_type_size = type_size(ira->codegen, dest_child_type); - uint64_t remainder = known_len % child_type_size; - if (remainder != 0) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base, - buf_sprintf("unable to convert [%" ZIG_PRI_u64 "]u8 to %s: size mismatch", - known_len, buf_ptr(&dest_slice_type->name))); - add_error_note(ira->codegen, msg, instruction->dest_child_type->source_node, - buf_sprintf("%s has size %" ZIG_PRI_u64 "; remaining bytes: %" ZIG_PRI_u64, - buf_ptr(&dest_child_type->name), child_type_size, remainder)); - return ira->codegen->invalid_instruction; - } - } - - return ir_resolve_cast(ira, &instruction->base, casted_value, dest_slice_type, CastOpResizeSlice, true); + if (target->value.type->id != ZigTypeIdFloat) { + ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->invalid_instruction; + } + + return ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type); } -static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) { - Error err; +static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) { + 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; - if (!is_slice(target->value.type)) { + if (target->value.type->id != ZigTypeIdErrorSet) { ir_add_error(ira, instruction->target, - buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } - ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + return ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type); +} - uint32_t alignment; - if ((err = resolve_ptr_align(ira, src_ptr_type, &alignment))) - return ira->codegen->invalid_instruction; +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { + Error err; - ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - src_ptr_type->data.pointer.is_const, src_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - alignment, 0, 0); - ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); + 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; + } - return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); + *result_align = get_ptr_align(ira->codegen, ty); + return ErrorNone; } static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { @@ -18630,10 +20911,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); @@ -18745,10 +21034,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) { @@ -18781,10 +21078,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) { @@ -18812,9 +21117,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)) @@ -18837,46 +21142,60 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction end = nullptr; } - ZigType *return_type; + ZigType *slice_ptr_type; + IrInstruction *ptr; if (array_type->id == ZigTypeIdArray) { 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, + slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, + 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); - return_type = get_slice_type(ira->codegen, slice_ptr_type); + ptr_ptr_type->data.pointer.explicit_alignment, 0, 0); + ptr = ptr_ptr; } else if (array_type->id == ZigTypeIdPointer) { + ptr = ir_get_deref(ira, &instruction->base, ptr_ptr); + if (type_is_invalid(ptr->value.type)) + return ira->codegen->invalid_instruction; + if (array_type->data.pointer.ptr_len == PtrLenSingle) { ZigType *main_type = array_type->data.pointer.child_type; if (main_type->id == ZigTypeIdArray) { - ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, + slice_ptr_type = get_pointer_to_type_extra(ira->codegen, main_type->data.pointer.child_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, PtrLenUnknown, array_type->data.pointer.explicit_alignment, 0, 0); - return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer")); return ira->codegen->invalid_instruction; } } else { - return_type = get_slice_type(ira->codegen, array_type); + slice_ptr_type = array_type; if (!end) { ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); return ira->codegen->invalid_instruction; } } } else if (is_slice(array_type)) { - ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; - return_type = get_slice_type(ira->codegen, ptr_type); + ptr = ir_get_deref(ira, &instruction->base, ptr_ptr); + if (type_is_invalid(ptr->value.type)) + return ira->codegen->invalid_instruction; + slice_ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; } else { ir_add_error(ira, &instruction->base, buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); return ira->codegen->invalid_instruction; } + ZigType *return_type = get_slice_type(ira->codegen, slice_ptr_type); + + IrInstruction *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + result_loc = resolve_possible_alloca_inference(ira, result_loc, return_type); + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; if (instr_is_comptime(ptr_ptr) && value_is_comptime(&casted_start->value) && @@ -18944,6 +21263,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; @@ -18951,6 +21276,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); @@ -18981,6 +21308,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; @@ -18988,6 +21321,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(); @@ -19053,6 +21388,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, @@ -19061,19 +21402,34 @@ 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]; init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); + if (result_loc->value.special != ConstValSpecialRuntime) { + return ir_analyze_store_ptr(ira, &instruction->base, result_loc, result); + } + return result; } + if (instr_is_comptime(result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, result_loc, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + ptr_val->special = ConstValSpecialRuntime; + } + } + IrInstruction *new_instruction = ir_build_slice(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - ptr_ptr, casted_start, end, instruction->safety_check_on); + ptr, casted_start, end, instruction->safety_check_on, + result_loc); new_instruction->value.type = return_type; - ir_add_alloca(ira, new_instruction, return_type); return new_instruction; } @@ -19416,7 +21772,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)); } } @@ -19442,105 +21799,265 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct } } -static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, - IrInstructionUnwrapErrCode *instruction) +static IrInstruction *ir_analyze_error_union_field_error_set(IrAnalyze *ira, + IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on) { - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; - - // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. + 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 == 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; - } + if (type_entry->id == ZigTypeIdErrorSet) { + return base_ptr; + } + if (type_entry->id != ZigTypeIdErrorUnion) { + if (!safety_check_on) { + IrInstruction *null = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_null); + return ir_get_ref(ira, source_instr, null, true, true, nullptr); } - - 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, + ir_add_error(ira, base_ptr, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + + ZigType *err_set_type = type_entry->data.error_union.err_set_type; + ZigType *opt_err_set = get_optional_type(ira->codegen, err_set_type); + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, opt_err_set, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, + ptr_type->data.pointer.explicit_alignment, 0, 0); + + if (!resolve_inferred_error_set(ira->codegen, err_set_type, source_instr->source_node)) + return ira->codegen->invalid_instruction; + if (err_set_type->data.error_set.err_count == 0) { + IrInstruction *null = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_null); + return ir_get_ref(ira, source_instr, null, true, true, nullptr); + } + + if (instr_is_comptime(base_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + if (err_union_val->special == ConstValSpecialUndef) { + ConstExprValue *vals = create_const_vals(2); + ConstExprValue *err_set_val = &vals[0]; + ConstExprValue *payload_val = &vals[1]; + + err_set_val->special = ConstValSpecialUndef; + err_set_val->type = opt_err_set; + + payload_val->special = ConstValSpecialUndef; + payload_val->type = type_entry->data.error_union.payload_type; + + err_union_val->special = ConstValSpecialStatic; + err_union_val->data.x_err_union.error_set = err_set_val; + err_union_val->data.x_err_union.payload = payload_val; + } + + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_error_union_field_error_set(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, safety_check_on); + result->value.type = result_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, result_type); + } + ConstExprValue *const_val = &result->value; + const_val->data.x_ptr.special = ConstPtrSpecialBaseErrorUnionCode; + const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + const_val->data.x_ptr.data.base_err_union_code.err_union_val = err_union_val; + return result; + } + } + + IrInstruction *result = ir_build_error_union_field_error_set(&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_unwrap_err_payload(IrAnalyze *ira, - IrInstructionUnwrapErrPayload *instruction) +static IrInstruction *ir_analyze_instruction_error_union_field_error_set(IrAnalyze *ira, + IrInstructionErrorUnionFieldErrorSet *instruction) { - assert(instruction->value->child); - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) + IrInstruction *base_ptr = instruction->ptr->child; + if (type_is_invalid(base_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; + return ir_analyze_error_union_field_error_set(ira, &instruction->base, base_ptr, instruction->safety_check_on); +} + +static IrInstruction *ir_analyze_unwrap_err_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, IrInstruction *result_loc, bool safety_check_on) +{ + 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) { - ZigType *payload_type = type_entry->data.error_union.payload_type; - if (type_is_invalid(payload_type)) { + if (result_loc != nullptr && type_entry->id == ZigTypeIdErrorSet) { + IrInstruction *undef = ir_const_undef(ira, source_instr); + return ir_analyze_store_ptr(ira, source_instr, result_loc, undef); + } + if (type_entry->id != ZigTypeIdErrorUnion) { + if (result_loc == nullptr) { + ir_add_error(ira, base_ptr, + buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); 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); + IrInstruction *deref = ir_get_deref(ira, source_instr, base_ptr); + return ir_analyze_store_ptr(ira, source_instr, result_loc, deref); + } + + 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(base_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (err_union_val == nullptr) return ira->codegen->invalid_instruction; + if (err_union_val->special == ConstValSpecialUndef) { + ConstExprValue *vals = create_const_vals(2); + ConstExprValue *err_set_val = &vals[0]; + ConstExprValue *payload_val = &vals[1]; + + ZigType *opt_err_set = get_optional_type(ira->codegen, type_entry->data.error_union.err_set_type); + err_set_val->special = ConstValSpecialStatic; + err_set_val->type = opt_err_set; + err_set_val->data.x_err_set = nullptr; + + payload_val->special = ConstValSpecialUndef; + payload_val->type = payload_type; + + err_union_val->special = ConstValSpecialStatic; + err_union_val->data.x_err_union.error_set = err_set_val; + err_union_val->data.x_err_union.payload = payload_val; + } if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; if (err != nullptr) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, source_instr, buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_const(ira, &instruction->base, result_type); + IrInstruction *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, nullptr, safety_check_on); + result->value.type = result_type; + result->value.special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, 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; + result->value.data.x_ptr.mut = ptr_val->data.x_ptr.mut; + if (result_loc == nullptr) { + return result; + } else { + IrInstruction *deref = ir_get_deref(ira, source_instr, result); + return ir_analyze_store_ptr(ira, source_instr, result_loc, deref); + } } } + } - 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; + IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, nullptr, safety_check_on); + result->value.type = result_type; + if (result_loc == nullptr) { return result; } else { - ir_add_error(ira, value, - buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); + IrInstruction *deref = ir_get_deref(ira, source_instr, result); + return ir_analyze_store_ptr(ira, source_instr, result_loc, deref); + } +} + +static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, + IrInstructionUnwrapErrPayload *instruction) +{ + IrInstruction *base_ptr = instruction->value->child; + if (type_is_invalid(base_ptr->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *result_loc = nullptr; + if (instruction->result_loc != nullptr) { + result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + } + + return ir_analyze_unwrap_err_payload(ira, &instruction->base, base_ptr, result_loc, instruction->safety_check_on); +} + +static IrInstruction *ir_analyze_instruction_assert_non_error(IrAnalyze *ira, + IrInstructionAssertNonError *instruction) +{ + IrInstruction *err_code = instruction->err_code->child; + if (type_is_invalid(err_code->value.type)) + return ira->codegen->invalid_instruction; + + assert(err_code->value.type->id == ZigTypeIdOptional); + assert(err_code->value.type->data.maybe.child_type->id == ZigTypeIdErrorSet); + + if (instr_is_comptime(err_code)) { + ConstExprValue *err_val = ir_resolve_const(ira, err_code, UndefBad); + if (err_val == nullptr) + return ira->codegen->invalid_instruction; + // just like optional pointers are represented with the same comptime + // value representation as normal pointers, + // optional error sets are represented with the same comptime value + // representation as normal error sets. + ErrorTableEntry *err = err_val->data.x_err_set; + if (err == nullptr) + return ir_const_void(ira, &instruction->base); + ir_add_error(ira, &instruction->base, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_instruction; + } + + ir_build_assert_non_error(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, err_code); + return ir_const_void(ira, &instruction->base); +} + +static IrInstruction *ir_analyze_instruction_assert_non_null(IrAnalyze *ira, + IrInstructionAssertNonNull *instruction) +{ + IrInstruction *is_non_null = instruction->is_non_null->child; + if (type_is_invalid(is_non_null->value.type)) + return ira->codegen->invalid_instruction; + + assert(is_non_null->value.type->id == ZigTypeIdBool); + + if (instr_is_comptime(is_non_null)) { + bool is_non_null_bool; + if (!ir_resolve_bool(ira, is_non_null, &is_non_null_bool)) + return ira->codegen->invalid_instruction; + if (is_non_null_bool) + return ir_const_void(ira, &instruction->base); + ir_add_error(ira, &instruction->base, buf_sprintf("unable to unwrap null")); return ira->codegen->invalid_instruction; } + ir_build_assert_non_null(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, is_non_null); + return ir_const_void(ira, &instruction->base); } static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) { @@ -19867,7 +22384,13 @@ static IrInstruction *ir_analyze_instruction_check_statement_is_void(IrAnalyze * if (type_is_invalid(statement_type)) return ira->codegen->invalid_instruction; - if (statement_type->id != ZigTypeIdVoid) { + // TODO this is a temporary hack because I'm about to rework coroutines anyway + bool is_async = exec_is_async(ira->new_irb.exec); + if (is_async) { + return ir_const_void(ira, &instruction->base); + } + + if (statement_type->id != ZigTypeIdVoid && statement_type != ira->codegen->builtin_types.entry_infer) { ir_add_error(ira, &instruction->base, buf_sprintf("expression value is ignored")); } @@ -19957,7 +22480,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; @@ -20005,8 +22528,12 @@ 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; + if (val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr); + } else { + result = ir_const(ira, source_instr, dest_type); + } copy_const_val(&result->value, val, false); result->value.type = dest_type; return result; @@ -20029,9 +22556,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, @@ -20057,7 +22582,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)) @@ -20195,14 +22720,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) { @@ -20242,111 +22782,12 @@ 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: zig_panic("TODO buf_read_value_bytes union type"); - } - 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 = ensure_complete_type(ira->codegen, dest_type))) - return ira->codegen->invalid_instruction; - - if ((err = ensure_complete_type(ira->codegen, 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; - } - - switch (src_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 from type '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_instruction; - default: - break; - } - - 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))); - 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, - 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)); - return ira->codegen->invalid_instruction; - } - - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); - if (!val) - return ira->codegen->invalid_instruction; - - IrInstruction *result = ir_const(ira, &instruction->base, 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))) - 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); - result->value.type = dest_type; - return result; + } + zig_unreachable(); } static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { @@ -20400,9 +22841,8 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstructionDeclRef *instruction) { Tld *tld = instruction->tld; - LVal lval = instruction->lval; - resolve_top_level_decl(ira->codegen, tld, lval == LValPtr, instruction->base.source_node); + resolve_top_level_decl(ira->codegen, tld, true, instruction->base.source_node); if (tld->resolution == TldResolutionInvalid) return ira->codegen->invalid_instruction; @@ -20410,27 +22850,17 @@ 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; - IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var); - if (type_is_invalid(var_ptr->value.type)) - return ira->codegen->invalid_instruction; - if (tld_var->extern_lib_name != nullptr) { add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node); } - if (lval == LValPtr) { - return var_ptr; - } else { - return ir_get_deref(ira, &instruction->base, var_ptr); - } + return ir_get_var_ptr(ira, &instruction->base, var); } - case TldIdFn: - { + case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); @@ -20439,13 +22869,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node); } - IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, fn_entry); - if (lval == LValPtr) { - return ir_get_ref(ira, &instruction->base, ref_instruction, true, false); - } else { - return ref_instruction; - } + return ir_create_const_fn(&ira->new_irb, instruction->base.scope, instruction->base.source_node, fn_entry); } } zig_unreachable(); @@ -20476,8 +22900,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; @@ -21303,18 +23726,547 @@ static IrInstruction *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, return ir_const_void(ira, &instruction->base); } +static IrInstruction *ir_analyze_result_return(IrAnalyze *ira, IrInstruction *source_inst) { + // Here we create a pass2 instruction for getting the return value pointer. + // However we create a const value for it and mark it with ConstPtrMutInfer + // so that if the return expression is comptime-known, we can emit a memcpy + // rather than runtime instructions. + ZigType *result_type = get_pointer_to_type(ira->codegen, ira->payload_return_type, false); + + IrInstruction *result = ir_build_result_return(&ira->new_irb, source_inst->scope, source_inst->source_node); + result->value.type = result_type; + if (ira->result_value != nullptr) { + result->value.special = ConstValSpecialStatic; + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = ira->result_value; + result->value.data.x_ptr.mut = ConstPtrMutComptimeVar; + } else if (type_has_bits(ira->payload_return_type) && handle_is_ptr(ira->payload_return_type)) { + ConstExprValue *pointee = create_const_vals(1); + pointee->special = ConstValSpecialUndef; + pointee->type = ira->payload_return_type; + + result->value.special = ConstValSpecialStatic; + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = pointee; + result->value.data.x_ptr.mut = ConstPtrMutInfer; + } else { + result->value.special = ConstValSpecialRuntime; + } + return result; +} + +static IrInstruction *ir_analyze_instruction_result_return(IrAnalyze *ira, IrInstructionResultReturn *instruction) { + ZigFn *fn = exec_fn_entry(ira->new_irb.exec); + if (fn == nullptr) { + ir_add_error(ira, &instruction->base, buf_sprintf("return outside function")); + return ira->codegen->invalid_instruction; + } + + return ir_analyze_result_return(ira, &instruction->base); +} + +static IrInstruction *ir_analyze_instruction_result_child(IrAnalyze *ira, IrInstructionResultChild *instruction) { + return instruction->prev_result_loc->child; +} + +static IrInstruction *ir_analyze_instruction_result_ptr_cast(IrAnalyze *ira, + IrInstructionResultPtrCast *instruction) +{ + ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->base.source_node, + prev_result_loc, elem_type, false); + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + ZigType *new_ptr_type = adjust_ptr_child(ira->codegen, new_result_loc->value.type, + ira->codegen->builtin_types.entry_infer); + IrInstruction *ptr_cast = ir_build_ptr_cast_gen(ira, &instruction->base, new_ptr_type, new_result_loc); + reinterpret_cast(ptr_cast)->pass1_parent = &instruction->base; + + return ptr_cast; +} + +static IrInstruction *ir_analyze_instruction_result_cast(IrAnalyze *ira, IrInstructionResultCast *instruction) { + ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->base.source_node, + prev_result_loc, elem_type, false); + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + new_result_loc->is_gen = prev_result_loc->is_gen; + return new_result_loc; +} + +static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *var_type, + uint32_t align, const char *name_hint, bool force_comptime) +{ + Error err; + + ConstExprValue *pointee = create_const_vals(1); + pointee->special = ConstValSpecialUndef; + + IrInstructionAllocaGen *result = ir_create_alloca_gen(&ira->new_irb, source_inst->scope, + source_inst->source_node, align, name_hint); + result->base.value.special = ConstValSpecialStatic; + result->base.value.data.x_ptr.special = ConstPtrSpecialRef; + result->base.value.data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer; + result->base.value.data.x_ptr.data.ref.pointee = pointee; + + if (var_type == nullptr) { + // We use a pointer to a special opaque type to signal that we want type inference. + result->base.value.type = get_pointer_to_type_extra(ira->codegen, + ira->codegen->builtin_types.entry_infer, false, false, PtrLenSingle, align, 0, 0); + result->base.value.data.x_ptr.data.ref.pointee->type = ira->codegen->builtin_types.entry_infer; + } else { + if ((err = resolve_alloca_inference(ira, result, var_type))) + return ira->codegen->invalid_instruction; + } + + ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); + if (fn_entry != nullptr) { + fn_entry->alloca_list.append(result); + } + result->base.is_gen = true; + return &result->base; +} + +static IrInstruction *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAllocaSrc *instruction) { + uint32_t align = 0; + if (instruction->align != nullptr) { + if (!ir_resolve_align(ira, instruction->align->child, &align)) { + return ira->codegen->invalid_instruction; + } + } + + bool is_comptime = false; + if (instruction->is_comptime != nullptr) { + if (!ir_resolve_bool(ira, instruction->is_comptime->child, &is_comptime)) + return ira->codegen->invalid_instruction; + } + + ZigType *var_type = nullptr; + if (instruction->child_type != nullptr) { + var_type = ir_resolve_type(ira, instruction->child_type->child); + if (type_is_invalid(var_type)) + return ira->codegen->invalid_instruction; + } + + return ir_analyze_alloca(ira, &instruction->base, var_type, align, instruction->name_hint, is_comptime); +} + +static IrInstruction *ir_analyze_instruction_first_arg_result_loc(IrAnalyze *ira, + IrInstructionFirstArgResultLoc *instruction) +{ + IrInstruction *fn_ref = instruction->fn_ref->child; + if (type_is_invalid(fn_ref->value.type)) + return ira->codegen->invalid_instruction; + + if (fn_ref->value.type->id == ZigTypeIdMetaType) { + // Result of this instruction should be the implicitly casted result location. + + ZigType *dest_type = ir_resolve_type(ira, fn_ref); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->base.source_node, + instruction->prev_result_loc->child, dest_type, true); + if (new_result_loc == nullptr) + return nullptr; + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + return new_result_loc; + } + + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); + return ir_analyze_alloca(ira, &instruction->base, nullptr, 0, "", is_comptime); +} + +static IrInstruction *ir_analyze_instruction_infer_array_type(IrAnalyze *ira, + IrInstructionInferArrayType *instruction) +{ + ZigType *src_type = ir_resolve_type(ira, instruction->src_type->child); + if (type_is_invalid(src_type)) + return ira->codegen->invalid_instruction; + + if (!is_slice(src_type)) { + return ir_const_type(ira, &instruction->base, src_type); + } + + ZigType *child_type = src_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type; + ZigType *array_type = get_array_type(ira->codegen, child_type, instruction->elem_count); + return ir_const_type(ira, &instruction->base, array_type); +} + +static IrInstruction *ir_analyze_instruction_infer_comptime(IrAnalyze *ira, + IrInstructionInferCompTime *instruction) +{ + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = instruction->new_result_loc->child; + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + if (prev_result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(new_result_loc) && + new_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + prev_result_loc->value.special = ConstValSpecialRuntime; + } + } + return ir_const_void(ira, &instruction->base); +} + +static IrInstruction *ir_analyze_instruction_from_bytes_len(IrAnalyze *ira, + IrInstructionFromBytesLenSrc *instruction) +{ + Error err; + + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = instruction->new_result_loc->child; + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + assert(prev_result_loc->value.type->id == ZigTypeIdPointer); + ZigType *slice_type = prev_result_loc->value.type->data.pointer.child_type; + assert(is_slice(slice_type)); + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *elem_type = slice_ptr_type->data.pointer.child_type; + + if (prev_result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(new_result_loc) && + new_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *slice_val = const_ptr_pointee(ira, ira->codegen, &new_result_loc->value, + instruction->base.source_node); + if (slice_val == nullptr) + return ira->codegen->invalid_instruction; + + if (slice_val->special != ConstValSpecialUndef) { + ConstExprValue *ptr_val = &slice_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *len_val = &slice_val->data.x_struct.fields[slice_len_index]; + if (ptr_val->special == ConstValSpecialStatic && len_val->special == ConstValSpecialStatic) { + // copy the length and divide it from the new_result_loc + // we need a new slice const val though, because if we modify the length it will + // modify the length of the new_result_loc + ConstExprValue *new_slice_val = create_const_vals(1); + copy_const_val(new_slice_val, slice_val, false); + prev_result_loc->value.data.x_ptr.special = ConstPtrSpecialRef; + prev_result_loc->value.data.x_ptr.data.ref.pointee = new_slice_val; + + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; + + size_t elem_size = type_size(ira->codegen, elem_type); + BigInt elem_size_bigint; + bigint_init_unsigned(&elem_size_bigint, elem_size); + + BigInt tmp_bigint; + bigint_rem(&tmp_bigint, &len_val->data.x_bigint, &elem_size_bigint); + + if (bigint_cmp_zero(&tmp_bigint) != CmpEQ) { + Buf *remaining_text = buf_alloc(); + bigint_append_buf(remaining_text, &tmp_bigint, 10); + Buf *bytes_text = buf_alloc(); + bigint_append_buf(bytes_text, &len_val->data.x_bigint, 10); + ir_add_error(ira, &instruction->base, buf_sprintf( + "converting slice of %s bytes to '%s' (element size %zu) leaves %s bytes remaining", + buf_ptr(bytes_text), + buf_ptr(&slice_type->name), + elem_size, + buf_ptr(remaining_text))); + return ira->codegen->invalid_instruction; + } + + ConstExprValue *new_len_val = &new_slice_val->data.x_struct.fields[slice_len_index]; + new_len_val->special = ConstValSpecialStatic; + bigint_div_trunc(&new_len_val->data.x_bigint, &len_val->data.x_bigint, &elem_size_bigint); + + prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + + return ir_const_void(ira, &instruction->base); + } + } + } + // comptime didn't work, mark the instructions as runtime and fall through + prev_result_loc->value.special = ConstValSpecialRuntime; + new_result_loc->value.special = ConstValSpecialRuntime; + } + + return ir_build_from_bytes_len_gen(ira, &instruction->base, prev_result_loc, elem_type); +} + +static IrInstruction *ir_analyze_instruction_to_bytes_len(IrAnalyze *ira, + IrInstructionToBytesLenSrc *instruction) +{ + Error err; + + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = instruction->new_result_loc->child; + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + assert(new_result_loc->value.type->id == ZigTypeIdPointer); + ZigType *slice_type = new_result_loc->value.type->data.pointer.child_type; + assert(is_slice(slice_type)); + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *elem_type = slice_ptr_type->data.pointer.child_type; + + if (prev_result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(new_result_loc) && + new_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ConstExprValue *slice_val = const_ptr_pointee(ira, ira->codegen, &new_result_loc->value, + instruction->base.source_node); + if (slice_val == nullptr) + return ira->codegen->invalid_instruction; + + if (slice_val->special != ConstValSpecialUndef) { + ConstExprValue *ptr_val = &slice_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *len_val = &slice_val->data.x_struct.fields[slice_len_index]; + if (ptr_val->special == ConstValSpecialStatic && len_val->special == ConstValSpecialStatic) { + // copy the length from the new_result_loc and multiply it by the element size + // we need a new slice const val though, because if we modify the length it will + // modify the length of the new_result_loc + ConstExprValue *new_slice_val = create_const_vals(1); + copy_const_val(new_slice_val, slice_val, false); + prev_result_loc->value.data.x_ptr.special = ConstPtrSpecialRef; + prev_result_loc->value.data.x_ptr.data.ref.pointee = new_slice_val; + + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; + + size_t elem_size = type_size(ira->codegen, elem_type); + BigInt elem_size_bigint; + bigint_init_unsigned(&elem_size_bigint, elem_size); + + ConstExprValue *new_len_val = &new_slice_val->data.x_struct.fields[slice_len_index]; + new_len_val->special = ConstValSpecialStatic; + bigint_mul(&new_len_val->data.x_bigint, &len_val->data.x_bigint, &elem_size_bigint); + + prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + return ir_const_void(ira, &instruction->base); + } + } + } + // comptime didn't work, mark the instructions as runtime and fall through + prev_result_loc->value.special = ConstValSpecialRuntime; + new_result_loc->value.special = ConstValSpecialRuntime; + } + + return ir_build_to_bytes_len_gen(ira, &instruction->base, prev_result_loc, elem_type); +} + + +static IrInstruction *ir_analyze_instruction_set_non_null_bit(IrAnalyze *ira, + IrInstructionSetNonNullBit *instruction) +{ + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *non_null_bit = instruction->non_null_bit->child; + if (type_is_invalid(non_null_bit->value.type)) + return ira->codegen->invalid_instruction; + + IrInstruction *new_result_loc = instruction->new_result_loc->child; + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + assert(prev_result_loc->value.type->id == ZigTypeIdPointer); + ZigType *opt_type = prev_result_loc->value.type->data.pointer.child_type; + assert(opt_type->id == ZigTypeIdOptional); + + if (!handle_is_ptr(opt_type)) { + if (prev_result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(new_result_loc) && + new_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + prev_result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + prev_result_loc->value.special = ConstValSpecialRuntime; + } + } + return ir_const_void(ira, &instruction->base); + } + + if (instr_is_comptime(prev_result_loc) && instr_is_comptime(new_result_loc) && + instr_is_comptime(non_null_bit)) + { + zig_panic("TODO comptime cmpxchg"); + } + + prev_result_loc->value.special = ConstValSpecialRuntime; + IrInstruction *result = ir_build_set_nonnull_bit(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, prev_result_loc, non_null_bit, nullptr); + result->value.type = ira->codegen->builtin_types.entry_void; + return result; +} + +static IrInstruction *ir_analyze_instruction_error_literal(IrAnalyze *ira, IrInstructionErrorLiteral *instruction) { + return ir_analyze_error_literal(ira, &instruction->base, instruction->name); +} + +static IrInstruction *ir_analyze_instruction_result_bytes_to_slice(IrAnalyze *ira, + IrInstructionResultBytesToSlice *instruction) +{ + // Here the pointer properties are wrong except for the child element type. + // This gets corrected later by resolve_alloca_inference + ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, 0, 0, 0); + ZigType *slice_of_bytes = get_slice_type(ira->codegen, slice_ptr_type); + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + IrInstruction *new_result_loc = ir_implicit_cast_result(ira, instruction->base.source_node, + prev_result_loc, slice_of_bytes, false); + if (type_is_invalid(new_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + // This pointer type is also wrong and we don't even know the child type. + // Also corrected later by resolve_alloca_inference + ZigType *new_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_infer, + true, false, PtrLenUnknown, 0, 0, 0); + ZigType *new_slice_type = get_slice_type(ira->codegen, new_ptr_type); + ZigType *new_result_loc_type = adjust_ptr_child(ira->codegen, prev_result_loc->value.type, new_slice_type); + + IrInstruction *result = ir_build_ptr_cast_gen(ira, &instruction->base, new_result_loc_type, new_result_loc); + reinterpret_cast(result)->pass1_parent = &instruction->base; + + // At compile time pointer casts are represented as the pointer type disagreeing + // with the element type, so that's easy to do here. + if (instr_is_comptime(new_result_loc) && new_result_loc->value.data.x_ptr.mut != ConstPtrMutInfer) { + ConstExprValue *val = ir_resolve_const(ira, new_result_loc, UndefOk); + if (!val) + return ira->codegen->invalid_instruction; + + copy_const_val(&result->value, val, false); + result->value.type = new_result_loc_type; + return result; + } + + return result; +} + +// This function is called once we have enough information to do the analysis. +static IrInstruction *ir_analyze_result_slice_to_bytes(IrAnalyze *ira, + IrInstruction *source_inst, IrInstruction *prev_result_loc, ZigType *elem_type, ZigType *bytes_slice_type) +{ + Error err; + + if (!is_slice(bytes_slice_type)) { + ir_add_error(ira, source_inst, + buf_sprintf("expected slice, found '%s'", buf_ptr(&bytes_slice_type->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *bytes_ptr_type = bytes_slice_type->data.structure.fields[slice_ptr_index].type_entry; + if (bytes_ptr_type->data.pointer.child_type != ira->codegen->builtin_types.entry_u8) { + ir_add_error(ira, source_inst, + buf_sprintf("expected slice of u8, found '%s'", buf_ptr(&bytes_slice_type->name))); + return ira->codegen->invalid_instruction; + } + + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + + + uint32_t bytes_ptr_align; + if ((err = resolve_ptr_align(ira, bytes_ptr_type, &bytes_ptr_align))) + return ira->codegen->invalid_instruction; + + ZigType *elem_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, + bytes_ptr_type->data.pointer.is_const, bytes_ptr_type->data.pointer.is_volatile, + PtrLenUnknown, bytes_ptr_align, 0, 0); + ZigType *slice_of_elem = get_slice_type(ira->codegen, elem_ptr_type); + IrInstruction *casted_prev_result_loc = ir_implicit_cast_result(ira, source_inst->source_node, + prev_result_loc, slice_of_elem, false); + if (type_is_invalid(casted_prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + ZigType *new_result_loc_type = adjust_ptr_child(ira->codegen, casted_prev_result_loc->value.type, + bytes_slice_type); + IrInstruction *result = ir_build_result_slice_to_bytes_gen(ira, source_inst, new_result_loc_type, + casted_prev_result_loc); + + if (instr_is_comptime(casted_prev_result_loc)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, casted_prev_result_loc, UndefOk); + if (!ptr_val) + return ira->codegen->invalid_instruction; + + copy_const_val(&result->value, ptr_val, false); + result->value.type = new_result_loc_type; + return result; + } + + return result; +} + +static IrInstruction *ir_analyze_instruction_result_slice_to_bytes(IrAnalyze *ira, + IrInstructionResultSliceToBytesSrc *instruction) +{ + ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction; + + IrInstruction *prev_result_loc = instruction->prev_result_loc->child; + if (type_is_invalid(prev_result_loc->value.type)) + return ira->codegen->invalid_instruction; + + // We don't have enough type information to do anything. So we emit a placeholder instruction + // and then will fix things later when resolve_alloca_inference is called. + // This works because this is a result location instruction and so the first reference to it + // will be from resolve_alloca_inference. + return ir_build_result_slice_to_bytes_placeholder(ira, &instruction->base, prev_result_loc, elem_type); +} + static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: case IrInstructionIdWidenOrShorten: - case IrInstructionIdStructInit: - case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: case IrInstructionIdUnionFieldPtr: case IrInstructionIdOptionalWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: + case IrInstructionIdPtrOfArrayToSlice: + case IrInstructionIdArrayToSlice: + case IrInstructionIdDeclVarGen: + case IrInstructionIdAllocaGen: + case IrInstructionIdResultSlicePtr: + case IrInstructionIdResultErrorUnionPayload: + case IrInstructionIdResultErrorUnionCode: + case IrInstructionIdPtrCastGen: + case IrInstructionIdCmpxchgGen: + case IrInstructionIdResultSliceToBytesPlaceholder: + case IrInstructionIdResultSliceToBytesGen: + case IrInstructionIdFromBytesLenGen: + case IrInstructionIdToBytesLenGen: + case IrInstructionIdUnwrapErrCode: zig_unreachable(); case IrInstructionIdReturn: @@ -21325,8 +24277,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: @@ -21371,8 +24323,10 @@ 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 IrInstructionIdOptionalUnwrapVal: + return ir_analyze_instruction_optional_unwrap_val(ira, (IrInstructionOptionalUnwrapVal *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -21413,8 +24367,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: @@ -21425,10 +24379,6 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction); case IrInstructionIdErrSetCast: return ir_analyze_instruction_err_set_cast(ira, (IrInstructionErrSetCast *)instruction); - case IrInstructionIdFromBytes: - return ir_analyze_instruction_from_bytes(ira, (IrInstructionFromBytes *)instruction); - case IrInstructionIdToBytes: - return ir_analyze_instruction_to_bytes(ira, (IrInstructionToBytes *)instruction); case IrInstructionIdIntToFloat: return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); case IrInstructionIdFloatToInt: @@ -21465,10 +24415,12 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErr: return ir_analyze_instruction_test_err(ira, (IrInstructionTestErr *)instruction); - case IrInstructionIdUnwrapErrCode: - return ir_analyze_instruction_unwrap_err_code(ira, (IrInstructionUnwrapErrCode *)instruction); case IrInstructionIdUnwrapErrPayload: return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstructionUnwrapErrPayload *)instruction); + case IrInstructionIdAssertNonError: + return ir_analyze_instruction_assert_non_error(ira, (IrInstructionAssertNonError *)instruction); + case IrInstructionIdAssertNonNull: + return ir_analyze_instruction_assert_non_null(ira, (IrInstructionAssertNonNull *)instruction); case IrInstructionIdFnProto: return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction); case IrInstructionIdTestComptime: @@ -21481,10 +24433,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 IrInstructionIdBitCast: - return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)instruction); + case IrInstructionIdPtrCastSrc: + return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction); case IrInstructionIdIntToPtr: return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction); case IrInstructionIdPtrToInt: @@ -21581,21 +24531,53 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction); case IrInstructionIdCheckRuntimeScope: return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction); + case IrInstructionIdResultReturn: + return ir_analyze_instruction_result_return(ira, (IrInstructionResultReturn *)instruction); + case IrInstructionIdResultChild: + return ir_analyze_instruction_result_child(ira, (IrInstructionResultChild *)instruction); + case IrInstructionIdResultPtrCast: + return ir_analyze_instruction_result_ptr_cast(ira, (IrInstructionResultPtrCast *)instruction); + case IrInstructionIdResultCast: + return ir_analyze_instruction_result_cast(ira, (IrInstructionResultCast *)instruction); + case IrInstructionIdAllocaSrc: + return ir_analyze_instruction_alloca(ira, (IrInstructionAllocaSrc *)instruction); + case IrInstructionIdErrorUnionFieldErrorSet: + return ir_analyze_instruction_error_union_field_error_set(ira, (IrInstructionErrorUnionFieldErrorSet *)instruction); + case IrInstructionIdFirstArgResultLoc: + return ir_analyze_instruction_first_arg_result_loc(ira, (IrInstructionFirstArgResultLoc *)instruction); + case IrInstructionIdInferArrayType: + return ir_analyze_instruction_infer_array_type(ira, (IrInstructionInferArrayType *)instruction); + case IrInstructionIdInferCompTime: + return ir_analyze_instruction_infer_comptime(ira, (IrInstructionInferCompTime *)instruction); + case IrInstructionIdFromBytesLenSrc: + return ir_analyze_instruction_from_bytes_len(ira, (IrInstructionFromBytesLenSrc *)instruction); + case IrInstructionIdToBytesLenSrc: + return ir_analyze_instruction_to_bytes_len(ira, (IrInstructionToBytesLenSrc *)instruction); + case IrInstructionIdResultOptionalPayload: + return ir_analyze_instruction_result_optional_payload(ira, (IrInstructionResultOptionalPayload *)instruction); + case IrInstructionIdSetNonNullBit: + return ir_analyze_instruction_set_non_null_bit(ira, (IrInstructionSetNonNullBit *)instruction); + case IrInstructionIdErrorLiteral: + return ir_analyze_instruction_error_literal(ira, (IrInstructionErrorLiteral *)instruction); + case IrInstructionIdResultBytesToSlice: + return ir_analyze_instruction_result_bytes_to_slice(ira, (IrInstructionResultBytesToSlice *)instruction); + case IrInstructionIdResultSliceToBytesSrc: + return ir_analyze_instruction_result_slice_to_bytes(ira, (IrInstructionResultSliceToBytesSrc *)instruction); } zig_unreachable(); } static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *old_instruction) { IrInstruction *new_instruction = ir_analyze_instruction_nocast(ira, old_instruction); - assert(new_instruction->value.type != nullptr); + assert(new_instruction == nullptr || new_instruction->value.type != nullptr); old_instruction->child = new_instruction; return new_instruction; } // This function attempts to evaluate IR code while doing type checking and other analysis. // It emits a new IrExecutable which is partially evaluated IR code. -ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec, - ZigType *expected_type, AstNode *expected_type_source_node) +ConstExprValue *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec, + ZigType *expected_type, AstNode *expected_type_source_node, ConstExprValue *result_value) { assert(!old_exec->invalid); assert(expected_type == nullptr || !type_is_invalid(expected_type)); @@ -21603,11 +24585,40 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ IrAnalyze *ira = allocate(1); old_exec->analysis = ira; ira->codegen = codegen; + ira->result_value = result_value; ZigFn *fn_entry = exec_fn_entry(old_exec); bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; ira->explicit_return_type = is_async ? get_promise_type(codegen, expected_type) : expected_type; ira->explicit_return_type_source_node = expected_type_source_node; + ira->scalar_return_type = ira->explicit_return_type; + ira->payload_return_type = ira->explicit_return_type; + + if (fn_entry != nullptr && !is_async) { + if (ira->explicit_return_type->id == ZigTypeIdErrorUnion) { + ira->scalar_return_type = get_optional_type(ira->codegen, + ira->explicit_return_type->data.error_union.err_set_type); + ira->payload_return_type = ira->explicit_return_type->data.error_union.payload_type; + } else if (ira->explicit_return_type->id == ZigTypeIdOptional) { + if (handle_is_ptr(ira->explicit_return_type)) { + ira->scalar_return_type = ira->codegen->builtin_types.entry_bool; + ira->payload_return_type = ira->explicit_return_type->data.maybe.child_type; + } + } + } else if (is_async) { + if (expected_type->id == ZigTypeIdErrorUnion) { + ira->payload_return_type = expected_type->data.error_union.payload_type; + } else if (expected_type->id == ZigTypeIdOptional) { + ira->payload_return_type = expected_type->data.maybe.child_type; + } else { + ira->payload_return_type = expected_type; + } + } + + if (result_value != nullptr) { + result_value->special = ConstValSpecialUndef; + result_value->type = ira->payload_return_type; + } ira->old_irb.codegen = codegen; ira->old_irb.exec = old_exec; @@ -21638,24 +24649,63 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ } IrInstruction *new_instruction = ir_analyze_instruction(ira, old_instruction); - if (type_is_invalid(new_instruction->value.type) && ir_should_inline(new_exec, old_instruction->scope)) { - return ira->codegen->builtin_types.entry_invalid; - } + if (new_instruction != nullptr) { + if (type_is_invalid(new_instruction->value.type) && ir_should_inline(new_exec, old_instruction->scope)) { + return &ira->codegen->invalid_instruction->value; + } - // unreachable instructions do their own control flow. - if (new_instruction->value.type->id == ZigTypeIdUnreachable) - continue; + // unreachable instructions do their own control flow. + if (new_instruction->value.type->id == ZigTypeIdUnreachable) + continue; + } ira->instruction_index += 1; } + if (new_exec->invalid) { - return ira->codegen->builtin_types.entry_invalid; + return &ira->codegen->invalid_instruction->value; } else if (ira->src_implicit_return_type_list.length == 0) { - return codegen->builtin_types.entry_unreachable; + return &ira->codegen->unreach_instruction->value; + } + ZigType *implicit_scalar_return_type = ir_resolve_peer_types(ira, expected_type_source_node, expected_type, + ira->src_implicit_return_type_list.items, ira->src_implicit_return_type_list.length); + if (type_is_invalid(implicit_scalar_return_type)) + return &ira->codegen->invalid_instruction->value; + if (result_value == nullptr) { + ConstExprValue *result_val = create_const_vals(1); + result_val->type = implicit_scalar_return_type; + result_val->special = ConstValSpecialRuntime; + return result_val; + } + + ConstExprValue *scalar_result = ir_exec_const_result(codegen, new_exec); + if (type_is_invalid(scalar_result->type)) + return &ira->codegen->invalid_instruction->value; + + if (ira->scalar_return_type == ira->explicit_return_type) { + return scalar_result; + } else if (ira->scalar_return_type->id == ZigTypeIdVoid) { + return result_value; + } else if (ira->scalar_return_type->id == ZigTypeIdBool) { + ConstExprValue *result_val = create_const_vals(1); + result_val->type = ira->explicit_return_type; + result_val->special = ConstValSpecialStatic; + if (scalar_result->data.x_bool) { + result_val->data.x_optional = result_value; + } else { + result_val->data.x_optional = nullptr; + } + return result_val; + } else if (ira->scalar_return_type->id == ZigTypeIdOptional) { + ConstExprValue *result_val = create_const_vals(1); + result_val->type = ira->explicit_return_type; + result_val->special = ConstValSpecialStatic; + result_val->data.x_err_union.error_set = scalar_result; + result_val->data.x_err_union.payload = result_value; + return result_val; } else { - return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, - ira->src_implicit_return_type_list.length); + zig_unreachable(); } } @@ -21666,7 +24716,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: @@ -21681,7 +24732,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCInclude: case IrInstructionIdCDefine: case IrInstructionIdCUndef: - case IrInstructionIdCmpxchg: case IrInstructionIdFence: case IrInstructionIdMemset: case IrInstructionIdMemcpy: @@ -21709,18 +24759,27 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: case IrInstructionIdAtomicRmw: + case IrInstructionIdAssertNonError: + case IrInstructionIdAssertNonNull: + case IrInstructionIdContainerInitFields: + case IrInstructionIdContainerInitList: + case IrInstructionIdInferCompTime: + case IrInstructionIdFromBytesLenSrc: + case IrInstructionIdFromBytesLenGen: + case IrInstructionIdToBytesLenSrc: + case IrInstructionIdToBytesLenGen: + case IrInstructionIdSetNonNullBit: + case IrInstructionIdSlice: + case IrInstructionIdCmpxchgGen: + case IrInstructionIdCmpxchgSrc: + case IrInstructionIdArrayToSlice: return true; case IrInstructionIdPhi: case IrInstructionIdUnOp: case IrInstructionIdBinOp: - case IrInstructionIdLoadPtr: case IrInstructionIdConst: case IrInstructionIdCast: - case IrInstructionIdContainerInitList: - case IrInstructionIdContainerInitFields: - case IrInstructionIdStructInit: - case IrInstructionIdUnionInit: case IrInstructionIdFieldPtr: case IrInstructionIdElemPtr: case IrInstructionIdVarPtr: @@ -21734,7 +24793,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: - case IrInstructionIdUnwrapOptional: + case IrInstructionIdOptionalUnwrapPtr: + case IrInstructionIdOptionalUnwrapVal: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdPopCount: @@ -21746,7 +24806,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdTruncate: case IrInstructionIdIntType: case IrInstructionIdBoolNot: - case IrInstructionIdSlice: case IrInstructionIdMemberCount: case IrInstructionIdMemberType: case IrInstructionIdMemberName: @@ -21761,8 +24820,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: - case IrInstructionIdPtrCast: - case IrInstructionIdBitCast: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdPtrCastGen: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: case IrInstructionIdIntToPtr: @@ -21801,11 +24860,33 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: - case IrInstructionIdFromBytes: - case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: + case IrInstructionIdResultOptionalPayload: + case IrInstructionIdResultSlicePtr: + case IrInstructionIdResultErrorUnionPayload: + case IrInstructionIdResultErrorUnionCode: + case IrInstructionIdResultReturn: + case IrInstructionIdResultChild: + case IrInstructionIdResultPtrCast: + case IrInstructionIdResultCast: + case IrInstructionIdResultSliceToBytesSrc: + case IrInstructionIdResultSliceToBytesPlaceholder: + case IrInstructionIdResultSliceToBytesGen: + case IrInstructionIdResultBytesToSlice: + case IrInstructionIdAllocaSrc: + case IrInstructionIdAllocaGen: + case IrInstructionIdErrorUnionFieldErrorSet: + case IrInstructionIdFirstArgResultLoc: + case IrInstructionIdInferArrayType: + case IrInstructionIdErrorLiteral: return false; + case IrInstructionIdLoadPtr: + return reinterpret_cast(instruction)->result_loc != nullptr; + + case IrInstructionIdPtrOfArrayToSlice: + return reinterpret_cast(instruction)->result_loc != nullptr; + case IrInstructionIdAsm: { IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction; @@ -21815,7 +24896,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { { IrInstructionUnwrapErrPayload *unwrap_err_payload_instruction = (IrInstructionUnwrapErrPayload *)instruction; - return unwrap_err_payload_instruction->safety_check_on; + return unwrap_err_payload_instruction->safety_check_on || + unwrap_err_payload_instruction->result_loc != nullptr; } } zig_unreachable(); diff --git a/src/ir.hpp b/src/ir.hpp index 7af1d7f52ba8..89044c2fde66 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -13,13 +13,13 @@ 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); -ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, - ZigType *expected_type, AstNode *expected_type_source_node); +ConstExprValue *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, + ZigType *expected_type, AstNode *expected_type_source_node, ConstExprValue *result_value); bool ir_has_side_effects(IrInstruction *instruction); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b5099db86aaf..311a8b206bad 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -70,6 +70,17 @@ static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) ir_print_const_value(irp, &const_instruction->base.value); } +static const char *lval_str(LVal lval) { + switch (lval) { + case LValNone: return "None"; + case LValPtr: return "Ptr"; + case LValErrorUnionVal: return "ErrorUnionVal"; + case LValErrorUnionPtr: return "ErrorUnionPtr"; + case LValOptional: return "Optional"; + } + zig_unreachable(); +} + static const char *ir_bin_op_id_str(IrBinOp op_id) { switch (op_id) { case IrBinOpInvalid: @@ -172,7 +183,12 @@ 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(IrPrint *irp, IrInstructionDeclVarSrc *decl_var_instruction) { + if (decl_var_instruction->var->is_comptime != nullptr) { + fprintf(irp->f, "comptime<"); + ir_print_other_instruction(irp, decl_var_instruction->var->is_comptime); + fprintf(irp->f, "> "); + } 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) { @@ -188,11 +204,7 @@ static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instr fprintf(irp->f, " "); } fprintf(irp->f, "= "); - 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); - } + ir_print_other_instruction(irp, decl_var_instruction->ptr); } static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) { @@ -202,6 +214,7 @@ static void ir_print_cast(IrPrint *irp, IrInstructionCast *cast_instruction) { } static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) { + fprintf(irp->f, "Call "); if (call_instruction->is_async) { fprintf(irp->f, "async"); if (call_instruction->async_allocator != nullptr) { @@ -224,7 +237,12 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) { fprintf(irp->f, ", "); ir_print_other_instruction(irp, arg); } - fprintf(irp->f, ")"); + fprintf(irp->f, ") result="); + ir_print_other_instruction(irp, call_instruction->result_loc); + if (call_instruction->first_arg_result_loc != nullptr) { + fprintf(irp->f, ", first_arg_result="); + ir_print_other_instruction(irp, call_instruction->first_arg_result_loc); + } } static void ir_print_cond_br(IrPrint *irp, IrInstructionCondBr *cond_br_instruction) { @@ -266,17 +284,18 @@ static void ir_print_phi(IrPrint *irp, IrInstructionPhi *phi_instruction) { static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerInitList *instruction) { ir_print_other_instruction(irp, instruction->container_type); fprintf(irp->f, "{"); - if (instruction->item_count > 50) { - fprintf(irp->f, "...(%" ZIG_PRI_usize " items)...", instruction->item_count); + if (instruction->elem_count > 50) { + fprintf(irp->f, "...(%" ZIG_PRI_usize " items)...", instruction->elem_count); } else { - for (size_t i = 0; i < instruction->item_count; i += 1) { - IrInstruction *item = instruction->items[i]; + for (size_t i = 0; i < instruction->elem_count; i += 1) { + IrInstruction *elem_result_loc = instruction->elem_result_loc_list[i]; if (i != 0) fprintf(irp->f, ", "); - ir_print_other_instruction(irp, item); + ir_print_other_instruction(irp, elem_result_loc); } } - fprintf(irp->f, "}"); + fprintf(irp->f, "} result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerInitFields *instruction) { @@ -286,30 +305,10 @@ static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerI IrInstructionContainerInitFieldsField *field = &instruction->fields[i]; const char *comma = (i == 0) ? "" : ", "; fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field->name)); - ir_print_other_instruction(irp, field->value); + ir_print_other_instruction(irp, field->result_loc); } - fprintf(irp->f, "} // container init"); -} - -static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruction) { - fprintf(irp->f, "%s {", buf_ptr(&instruction->struct_type->name)); - for (size_t i = 0; i < instruction->field_count; i += 1) { - IrInstructionStructInitField *field = &instruction->fields[i]; - Buf *field_name = field->type_struct_field->name; - const char *comma = (i == 0) ? "" : ", "; - fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field_name)); - ir_print_other_instruction(irp, field->value); - } - fprintf(irp->f, "} // struct init"); -} - -static void ir_print_union_init(IrPrint *irp, IrInstructionUnionInit *instruction) { - Buf *field_name = instruction->field->enum_field->name; - - fprintf(irp->f, "%s {", buf_ptr(&instruction->union_type->name)); - fprintf(irp->f, ".%s = ", buf_ptr(field_name)); - ir_print_other_instruction(irp, instruction->init_value); - fprintf(irp->f, "} // union init"); + fprintf(irp->f, "} // result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) { @@ -332,8 +331,11 @@ static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) { } static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { - fprintf(irp->f, "*"); + fprintf(irp->f, "LoadPtr(ptr="); ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ",result="); + ir_print_other_instruction(irp, instruction->result_loc); + fprintf(irp->f, ")"); } static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) { @@ -479,15 +481,24 @@ 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, "OptionalUnwrapPtr("); + ir_print_other_instruction(irp, instruction->base_ptr); + fprintf(irp->f, ")"); + if (!instruction->safety_check_on) { + fprintf(irp->f, " // no safety"); + } +} + +static void ir_print_optional_unwrap_val(IrPrint *irp, IrInstructionOptionalUnwrapVal *instruction) { + fprintf(irp->f, "OptionalUnwrapVal("); + ir_print_other_instruction(irp, instruction->opt); + fprintf(irp->f, ")"); if (!instruction->safety_check_on) { fprintf(irp->f, " // no safety"); } @@ -558,6 +569,8 @@ static void ir_print_ref(IrPrint *irp, IrInstructionRef *instruction) { const char *volatile_str = instruction->is_volatile ? "volatile " : ""; fprintf(irp->f, "%s%sref ", const_str, volatile_str); ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, " result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_compile_err(IrPrint *irp, IrInstructionCompileErr *instruction) { @@ -613,7 +626,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 +640,17 @@ 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(%s,", buf_ptr(&instruction->type->name)); + 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, ") result="); + ir_print_other_instruction(irp, instruction->result_loc); +} + static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) { fprintf(irp->f, "@fence("); ir_print_other_instruction(irp, instruction->order_value); @@ -665,20 +689,6 @@ static void ir_print_err_set_cast(IrPrint *irp, IrInstructionErrSetCast *instruc fprintf(irp->f, ")"); } -static void ir_print_from_bytes(IrPrint *irp, IrInstructionFromBytes *instruction) { - fprintf(irp->f, "@bytesToSlice("); - ir_print_other_instruction(irp, instruction->dest_child_type); - fprintf(irp->f, ", "); - ir_print_other_instruction(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_to_bytes(IrPrint *irp, IrInstructionToBytes *instruction) { - fprintf(irp->f, "@sliceToBytes("); - ir_print_other_instruction(irp, instruction->target); - fprintf(irp->f, ")"); -} - static void ir_print_int_to_float(IrPrint *irp, IrInstructionIntToFloat *instruction) { fprintf(irp->f, "@intToFloat("); ir_print_other_instruction(irp, instruction->dest_type); @@ -741,7 +751,8 @@ static void ir_print_slice(IrPrint *irp, IrInstructionSlice *instruction) { fprintf(irp->f, ".."); if (instruction->end) ir_print_other_instruction(irp, instruction->end); - fprintf(irp->f, "]"); + fprintf(irp->f, "] result="); + ir_print_other_instruction(irp, instruction->result_loc); } static void ir_print_member_count(IrPrint *irp, IrInstructionMemberCount *instruction) { @@ -820,15 +831,16 @@ 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, ")"); + fprintf(irp->f, ") result="); + ir_print_other_instruction(irp, instruction->result_loc); if (!instruction->safety_check_on) { fprintf(irp->f, " // no safety"); } @@ -879,7 +891,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,18 +901,14 @@ static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { fprintf(irp->f, ")"); } -static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { - fprintf(irp->f, "@bitCast("); - if (instruction->dest_type) { - ir_print_other_instruction(irp, instruction->dest_type); - } - fprintf(irp->f, ","); - ir_print_other_instruction(irp, instruction->value); +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_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, ")"); } @@ -1004,8 +1012,7 @@ static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { } static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) { - const char *ptr_str = (instruction->lval == LValPtr) ? "ptr " : ""; - fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name)); + fprintf(irp->f, "declref %s", buf_ptr(instruction->tld->name)); } static void ir_print_panic(IrPrint *irp, IrInstructionPanic *instruction) { @@ -1323,6 +1330,192 @@ static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { fprintf(irp->f, ")"); } +static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *instruction) { + fprintf(irp->f, "DeclVarGen(name=%s,ptr=", buf_ptr(&instruction->var->name)); + ir_print_other_instruction(irp, instruction->var_ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_result_optional_payload(IrPrint *irp, IrInstructionResultOptionalPayload *instruction) { + fprintf(irp->f, "ResultOptionalPayload("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->payload_type); + fprintf(irp->f, "make_non_null=%s)", instruction->make_non_null ? "yes" : "no"); +} + +static void ir_print_result_slice_ptr(IrPrint *irp, IrInstructionResultSlicePtr *instruction) { + fprintf(irp->f, "ResultSlicePtr("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",len=%" ZIG_PRI_u64 ")", instruction->len); +} + +static void ir_print_result_error_union_payload(IrPrint *irp, IrInstructionResultErrorUnionPayload *instruction) { + fprintf(irp->f, "ResultErrorUnionPayload("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_result_error_union_code(IrPrint *irp, IrInstructionResultErrorUnionCode *instruction) { + fprintf(irp->f, "ResultErrorUnionCode("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_result_return(IrPrint *irp, IrInstructionResultReturn *instruction) { + fprintf(irp->f, "ResultReturn"); +} + +static void ir_print_result_child(IrPrint *irp, IrInstructionResultChild *instruction) { + fprintf(irp->f, "ResultChild(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",lval=%s)", lval_str(instruction->lval)); +} + +static void ir_print_result_bytes_to_slice(IrPrint *irp, IrInstructionResultBytesToSlice *instruction) { + fprintf(irp->f, "ResultBytesToSlice("); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_result_slice_to_bytes_src(IrPrint *irp, IrInstructionResultSliceToBytesSrc *instruction) { + fprintf(irp->f, "ResultSliceToBytesSrc"); +} + +static void ir_print_result_slice_to_bytes_placeholder(IrPrint *irp, IrInstructionResultSliceToBytesPlaceholder *instruction) { + fprintf(irp->f, "ResultSliceToBytesPlaceholder"); +} + +static void ir_print_result_slice_to_bytes_gen(IrPrint *irp, IrInstructionResultSliceToBytesGen *instruction) { + fprintf(irp->f, "ResultSliceToBytesGen"); +} + +static void ir_print_result_ptr_cast(IrPrint *irp, IrInstructionResultPtrCast *instruction) { + fprintf(irp->f, "ResultPtrCast(ty="); + ir_print_other_instruction(irp, instruction->elem_type); + fprintf(irp->f, ",prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_result_cast(IrPrint *irp, IrInstructionResultCast *instruction) { + fprintf(irp->f, "ResultCast(child_ty="); + ir_print_other_instruction(irp, instruction->elem_type); + fprintf(irp->f, ",prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_alloca_src(IrPrint *irp, IrInstructionAllocaSrc *instruction) { + fprintf(irp->f, "AllocaSrc(ty="); + ir_print_other_instruction(irp, instruction->child_type); + fprintf(irp->f, ",align="); + ir_print_other_instruction(irp, instruction->align); + fprintf(irp->f, ",name=%s)", instruction->name_hint); +} + +static void ir_print_alloca_gen(IrPrint *irp, IrInstructionAllocaGen *instruction) { + fprintf(irp->f, "AllocaGen(align=%" PRIu32 ",name=%s)", instruction->align, instruction->name_hint); +} + +static void ir_print_assert_non_error(IrPrint *irp, IrInstructionAssertNonError *instruction) { + fprintf(irp->f, "AssertNonError("); + ir_print_other_instruction(irp, instruction->err_code); + fprintf(irp->f, ")"); +} + +static void ir_print_assert_non_null(IrPrint *irp, IrInstructionAssertNonNull *instruction) { + fprintf(irp->f, "AssertNonNull("); + ir_print_other_instruction(irp, instruction->is_non_null); + fprintf(irp->f, ")"); +} + +static void ir_print_error_union_field_error_set(IrPrint *irp, IrInstructionErrorUnionFieldErrorSet *instruction) { + fprintf(irp->f, "ErrorUnionFieldErrorSet("); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_first_arg_result_loc(IrPrint *irp, IrInstructionFirstArgResultLoc *instruction) { + fprintf(irp->f, "FirstArgResultLoc(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",fn="); + ir_print_other_instruction(irp, instruction->fn_ref); + fprintf(irp->f, ")"); +} + +static void ir_print_infer_array_type(IrPrint *irp, IrInstructionInferArrayType *instruction) { + fprintf(irp->f, "InferArrayType(src_type="); + ir_print_other_instruction(irp, instruction->src_type); + fprintf(irp->f, ",elem_count=%zu)", instruction->elem_count); +} + +static void ir_print_infer_comptime(IrPrint *irp, IrInstructionInferCompTime *instruction) { + fprintf(irp->f, "InferCompTime(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",new_result="); + ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_from_bytes_len_src(IrPrint *irp, IrInstructionFromBytesLenSrc *instruction) { + fprintf(irp->f, "FromBytesLenSrc(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",new_result="); + ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_from_bytes_len_gen(IrPrint *irp, IrInstructionFromBytesLenGen *instruction) { + fprintf(irp->f, "FromBytesLenGen(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",elem_type=%s)", buf_ptr(&instruction->elem_type->name)); +} + +static void ir_print_to_bytes_len_src(IrPrint *irp, IrInstructionToBytesLenSrc *instruction) { + fprintf(irp->f, "ToBytesLenSrc(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",new_result="); + ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_to_bytes_len_gen(IrPrint *irp, IrInstructionToBytesLenGen *instruction) { + fprintf(irp->f, "ToBytesLenGen(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",elem_type=%s)", buf_ptr(&instruction->elem_type->name)); +} + +static void ir_print_set_non_null_bit(IrPrint *irp, IrInstructionSetNonNullBit *instruction) { + fprintf(irp->f, "SetNonNullBit(prev_result="); + ir_print_other_instruction(irp, instruction->prev_result_loc); + fprintf(irp->f, ",new_result="); + ir_print_other_instruction(irp, instruction->new_result_loc); + fprintf(irp->f, ",non_null_bit="); + ir_print_other_instruction(irp, instruction->non_null_bit); + fprintf(irp->f, ")"); +} + +static void ir_print_error_literal(IrPrint *irp, IrInstructionErrorLiteral *instruction) { + fprintf(irp->f, "error.%s", buf_ptr(instruction->name)); +} + +static void ir_print_ptr_of_array_to_slice(IrPrint *irp, IrInstructionPtrOfArrayToSlice *instruction) { + fprintf(irp->f, "PtrOfArrayToSlice(value="); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ",result="); + ir_print_other_instruction(irp, instruction->result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_array_to_slice(IrPrint *irp, IrInstructionArrayToSlice *instruction) { + fprintf(irp->f, "ArrayToSlice(array="); + ir_print_other_instruction(irp, instruction->array); + fprintf(irp->f, ",result="); + ir_print_other_instruction(irp, instruction->result_loc); + fprintf(irp->f, ")"); +} + static void ir_print_bswap(IrPrint *irp, IrInstructionBswap *instruction) { fprintf(irp->f, "@bswap("); if (instruction->type != nullptr) { @@ -1361,8 +1554,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(irp, (IrInstructionDeclVarSrc *)instruction); break; case IrInstructionIdCast: ir_print_cast(irp, (IrInstructionCast *)instruction); @@ -1388,12 +1581,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdContainerInitFields: ir_print_container_init_fields(irp, (IrInstructionContainerInitFields *)instruction); break; - case IrInstructionIdStructInit: - ir_print_struct_init(irp, (IrInstructionStructInit *)instruction); - break; - case IrInstructionIdUnionInit: - ir_print_union_init(irp, (IrInstructionUnionInit *)instruction); - break; case IrInstructionIdUnreachable: ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction); break; @@ -1452,10 +1639,13 @@ 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 IrInstructionIdOptionalUnwrapVal: + ir_print_optional_unwrap_val(irp, (IrInstructionOptionalUnwrapVal *)instruction); break; case IrInstructionIdCtz: ir_print_ctz(irp, (IrInstructionCtz *)instruction); @@ -1508,8 +1698,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); @@ -1526,12 +1719,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdErrSetCast: ir_print_err_set_cast(irp, (IrInstructionErrSetCast *)instruction); break; - case IrInstructionIdFromBytes: - ir_print_from_bytes(irp, (IrInstructionFromBytes *)instruction); - break; - case IrInstructionIdToBytes: - ir_print_to_bytes(irp, (IrInstructionToBytes *)instruction); - break; case IrInstructionIdIntToFloat: ir_print_int_to_float(irp, (IrInstructionIntToFloat *)instruction); break; @@ -1607,11 +1794,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 IrInstructionIdBitCast: - ir_print_bit_cast(irp, (IrInstructionBitCast *)instruction); + case IrInstructionIdPtrCastGen: + ir_print_ptr_cast_gen(irp, (IrInstructionPtrCastGen *)instruction); break; case IrInstructionIdWidenOrShorten: ir_print_widen_or_shorten(irp, (IrInstructionWidenOrShorten *)instruction); @@ -1775,6 +1962,93 @@ 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; + case IrInstructionIdResultOptionalPayload: + ir_print_result_optional_payload(irp, (IrInstructionResultOptionalPayload *)instruction); + break; + case IrInstructionIdResultSlicePtr: + ir_print_result_slice_ptr(irp, (IrInstructionResultSlicePtr *)instruction); + break; + case IrInstructionIdResultErrorUnionPayload: + ir_print_result_error_union_payload(irp, (IrInstructionResultErrorUnionPayload *)instruction); + break; + case IrInstructionIdResultErrorUnionCode: + ir_print_result_error_union_code(irp, (IrInstructionResultErrorUnionCode *)instruction); + break; + case IrInstructionIdResultReturn: + ir_print_result_return(irp, (IrInstructionResultReturn *)instruction); + break; + case IrInstructionIdResultChild: + ir_print_result_child(irp, (IrInstructionResultChild *)instruction); + break; + case IrInstructionIdResultBytesToSlice: + ir_print_result_bytes_to_slice(irp, (IrInstructionResultBytesToSlice *)instruction); + break; + case IrInstructionIdResultSliceToBytesSrc: + ir_print_result_slice_to_bytes_src(irp, (IrInstructionResultSliceToBytesSrc *)instruction); + break; + case IrInstructionIdResultSliceToBytesPlaceholder: + ir_print_result_slice_to_bytes_placeholder(irp, (IrInstructionResultSliceToBytesPlaceholder *)instruction); + break; + case IrInstructionIdResultSliceToBytesGen: + ir_print_result_slice_to_bytes_gen(irp, (IrInstructionResultSliceToBytesGen *)instruction); + break; + case IrInstructionIdResultPtrCast: + ir_print_result_ptr_cast(irp, (IrInstructionResultPtrCast *)instruction); + break; + case IrInstructionIdResultCast: + ir_print_result_cast(irp, (IrInstructionResultCast *)instruction); + break; + case IrInstructionIdAllocaSrc: + ir_print_alloca_src(irp, (IrInstructionAllocaSrc *)instruction); + break; + case IrInstructionIdAllocaGen: + ir_print_alloca_gen(irp, (IrInstructionAllocaGen *)instruction); + break; + case IrInstructionIdAssertNonError: + ir_print_assert_non_error(irp, (IrInstructionAssertNonError *)instruction); + break; + case IrInstructionIdAssertNonNull: + ir_print_assert_non_null(irp, (IrInstructionAssertNonNull *)instruction); + break; + case IrInstructionIdErrorUnionFieldErrorSet: + ir_print_error_union_field_error_set(irp, (IrInstructionErrorUnionFieldErrorSet *)instruction); + break; + case IrInstructionIdFirstArgResultLoc: + ir_print_first_arg_result_loc(irp, (IrInstructionFirstArgResultLoc *)instruction); + break; + case IrInstructionIdInferArrayType: + ir_print_infer_array_type(irp, (IrInstructionInferArrayType *)instruction); + break; + case IrInstructionIdInferCompTime: + ir_print_infer_comptime(irp, (IrInstructionInferCompTime *)instruction); + break; + case IrInstructionIdFromBytesLenSrc: + ir_print_from_bytes_len_src(irp, (IrInstructionFromBytesLenSrc *)instruction); + break; + case IrInstructionIdFromBytesLenGen: + ir_print_from_bytes_len_gen(irp, (IrInstructionFromBytesLenGen *)instruction); + break; + case IrInstructionIdToBytesLenSrc: + ir_print_to_bytes_len_src(irp, (IrInstructionToBytesLenSrc *)instruction); + break; + case IrInstructionIdToBytesLenGen: + ir_print_to_bytes_len_gen(irp, (IrInstructionToBytesLenGen *)instruction); + break; + case IrInstructionIdSetNonNullBit: + ir_print_set_non_null_bit(irp, (IrInstructionSetNonNullBit *)instruction); + break; + case IrInstructionIdErrorLiteral: + ir_print_error_literal(irp, (IrInstructionErrorLiteral *)instruction); + break; + case IrInstructionIdPtrOfArrayToSlice: + ir_print_ptr_of_array_to_slice(irp, (IrInstructionPtrOfArrayToSlice *)instruction); + break; + case IrInstructionIdArrayToSlice: + ir_print_array_to_slice(irp, (IrInstructionArrayToSlice *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/src/parser.cpp b/src/parser.cpp index 077365995e87..d272e1077fb9 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; @@ -1717,10 +1717,8 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { if (error != nullptr) { Token *dot = expect_token(pc, TokenIdDot); Token *name = expect_token(pc, TokenIdSymbol); - AstNode *left = ast_create_node(pc, NodeTypeErrorType, error); - AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); - res->data.field_access_expr.struct_expr = left; - res->data.field_access_expr.field_name = token_buf(name); + AstNode *res = ast_create_node(pc, NodeTypeErrorLiteral, dot); + res->data.error_literal.name = token_buf(name); return res; } @@ -2204,7 +2202,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 +2997,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); @@ -3095,5 +3093,8 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeSuspend: visit_field(&node->data.suspend.block, visit, context); break; + case NodeTypeErrorLiteral: + // none + break; } } diff --git a/std/debug/index.zig b/std/debug/index.zig index 445f943594cd..5819840414a9 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -1953,7 +1953,8 @@ fn scanAllCompileUnits(di: *DwarfInfo) !void { if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; - const pc_range = x: { + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451261326 + const pc_range: ?PcRange = x: { if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { const pc_end = switch (high_pc_value.*) { diff --git a/std/fmt/index.zig b/std/fmt/index.zig index b010072273b5..99885ed40862 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -710,7 +710,7 @@ fn formatIntSigned( const minus_sign: u8 = '-'; try output(context, (*[1]u8)(&minus_sign)[0..]); const new_value = @intCast(uint, -(value + 1)) + 1; - const new_width = if (width == 0) 0 else (width - 1); + const new_width: usize = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } else if (width == 0) { return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); @@ -718,7 +718,7 @@ fn formatIntSigned( const plus_sign: u8 = '+'; try output(context, (*[1]u8)(&plus_sign)[0..]); const new_value = @intCast(uint, value); - const new_width = if (width == 0) 0 else (width - 1); + const new_width: usize = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } } @@ -760,7 +760,7 @@ fn formatIntUnsigned( if (leftover_padding == 0) break; } mem.set(u8, buf[0..index], '0'); - return output(context, buf); + return output(context, &buf); } else { const padded_buf = buf[index - padding ..]; mem.set(u8, padded_buf[0..padding], '0'); diff --git a/std/heap.zig b/std/heap.zig index 46b247fa7ead..b2210a317108 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -243,7 +243,8 @@ pub const ArenaAllocator = struct { const cur_buf = cur_node.data[@sizeOf(BufNode)..]; const addr = @ptrToInt(cur_buf.ptr) + self.end_index; const rem = @rem(addr, alignment); - const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451261326 + const march_forward_bytes: usize = if (rem == 0) 0 else (alignment - rem); const adjusted_index = self.end_index + march_forward_bytes; const new_end_index = adjusted_index + n; if (new_end_index > cur_buf.len) { @@ -290,7 +291,8 @@ pub const FixedBufferAllocator = struct { const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); const addr = @ptrToInt(self.buffer.ptr) + self.end_index; const rem = @rem(addr, alignment); - const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 + const march_forward_bytes: usize = if (rem == 0) 0 else (alignment - rem); const adjusted_index = self.end_index + march_forward_bytes; const new_end_index = adjusted_index + n; if (new_end_index > self.buffer.len) { @@ -348,7 +350,8 @@ pub const ThreadSafeFixedBufferAllocator = struct { while (true) { const addr = @ptrToInt(self.buffer.ptr) + end_index; const rem = @rem(addr, alignment); - const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 + const march_forward_bytes: usize = if (rem == 0) 0 else (alignment - rem); const adjusted_index = end_index + march_forward_bytes; const new_end_index = adjusted_index + n; if (new_end_index > self.buffer.len) { diff --git a/std/math/index.zig b/std/math/index.zig index f37de2505b24..d254c50b3840 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -378,7 +378,7 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t return u0; } const is_signed = from < 0; - const largest_positive_integer = max(if (from<0) (-from)-1 else from, to); // two's complement + const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement const base = log2(largest_positive_integer); const upper = (1 << base) - 1; var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 129bde913fd2..fb3649735d9c 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -22,8 +22,8 @@ nakedcc fn _start() noreturn { switch (builtin.arch) { builtin.Arch.x86_64 => { argc_ptr = asm ("lea (%%rsp), %[argc]" - : [argc] "=r" (-> [*]usize) - ); + : [argc] "=r" (-> [*]usize) + ); }, builtin.Arch.i386 => { argc_ptr = asm ("lea (%%esp), %[argc]" @@ -104,11 +104,12 @@ inline fn callMain() u8 { builtin.TypeId.ErrorUnion => { root.main() catch |err| { std.debug.warn("error: {}\n", @errorName(err)); - if (builtin.os != builtin.Os.zen) { - if (@errorReturnTrace()) |trace| { - std.debug.dumpStackTrace(trace); - } - } + // TODO: restore this + //if (builtin.os != builtin.Os.zen) { + // if (@errorReturnTrace()) |trace| { + // std.debug.dumpStackTrace(trace); + // } + //} return 1; }; return 0; diff --git a/std/special/compiler_rt/fixint.zig b/std/special/compiler_rt/fixint.zig index fd31798cc2f9..2c85bc73d7c9 100644 --- a/std/special/compiler_rt/fixint.zig +++ b/std/special/compiler_rt/fixint.zig @@ -63,9 +63,17 @@ pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t { // Cast to final signed result if (negative) { - return if (uint_result >= -math.minInt(fixint_t)) math.minInt(fixint_t) else -@intCast(fixint_t, uint_result); + if (uint_result >= -math.minInt(fixint_t)) { + return math.minInt(fixint_t); + } else { + return -@intCast(fixint_t, uint_result); + } } else { - return if (uint_result >= math.maxInt(fixint_t)) math.maxInt(fixint_t) else @intCast(fixint_t, uint_result); + if (uint_result >= math.maxInt(fixint_t)) { + return math.maxInt(fixint_t); + } else { + return @intCast(fixint_t, uint_result); + } } } diff --git a/std/special/panic.zig b/std/special/panic.zig index bd3ad971e0cb..88a202832b24 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -18,8 +18,11 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn std.os.abort(); }, else => { - const first_trace_addr = @ptrToInt(@returnAddress()); - std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); + // TODO put the original panic code back + std.debug.warn("panic: {}\n", msg); + std.os.abort(); + //const first_trace_addr = @ptrToInt(@returnAddress()); + //std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); }, } } diff --git a/test/behavior.zig b/test/behavior.zig deleted file mode 100644 index 10cd08dad717..000000000000 --- 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/array.zig b/test/cases/array.zig deleted file mode 100644 index 7c63a649a8f3..000000000000 --- 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 63e37c857c1d..000000000000 --- 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/bitreverse.zig b/test/cases/bitreverse.zig deleted file mode 100644 index 3721e68a9431..000000000000 --- 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/bswap.zig b/test/cases/bswap.zig deleted file mode 100644 index 57993077e1a8..000000000000 --- 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/import.zig b/test/cases/import.zig deleted file mode 100644 index 6d6d4b020860..000000000000 --- 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/optional.zig b/test/cases/optional.zig deleted file mode 100644 index d43682bbec3c..000000000000 --- 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/popcount.zig b/test/cases/popcount.zig deleted file mode 100644 index 7dc7f28c0e9c..000000000000 --- 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/reflection.zig b/test/cases/reflection.zig deleted file mode 100644 index b9b8aff4e16a..000000000000 --- 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 11c6b2f6ba36..000000000000 --- 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/type_info.zig b/test/cases/type_info.zig deleted file mode 100644 index cec532d5d304..000000000000 --- 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/stage1/behavior.zig b/test/stage1/behavior.zig new file mode 100644 index 000000000000..e545a4c4184d --- /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/cases/align.zig b/test/stage1/behavior/align.zig similarity index 69% rename from test/cases/align.zig rename to test/stage1/behavior/align.zig index 3dff57feb882..aa7a93ad8482 100644 --- a/test/cases/align.zig +++ b/test/stage1/behavior/align.zig @@ -1,13 +1,13 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; 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); + assertOrPanic(@typeOf(&foo).alignment == 4); + assertOrPanic(@typeOf(&foo) == *align(4) u8); const slice = (*[1]u8)(&foo)[0..]; - assert(@typeOf(slice) == []align(4) u8); + assertOrPanic(@typeOf(slice) == []align(4) u8); } fn derp() align(@sizeOf(usize) * 2) i32 { @@ -17,9 +17,9 @@ 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); + assertOrPanic(derp() == 1234); + assertOrPanic(@typeOf(noop1) == fn () align(1) void); + assertOrPanic(@typeOf(noop4) == fn () align(4) void); noop1(); noop4(); } @@ -30,7 +30,7 @@ var baz: packed struct { } = undefined; test "packed struct alignment" { - assert(@typeOf(&baz.b) == *align(1) u32); + assertOrPanic(@typeOf(&baz.b) == *align(1) u32); } const blah: packed struct { @@ -40,17 +40,17 @@ const blah: packed struct { } = undefined; test "bit field alignment" { - assert(@typeOf(&blah.b) == *align(1:3:1) const u3); + assertOrPanic(@typeOf(&blah.b) == *align(1:3:1) const u3); } test "default alignment allows unspecified in type syntax" { - assert(*u32 == *align(@alignOf(u32)) u32); + assertOrPanic(*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); + assertOrPanic(addUnaligned(&a, &b) == 7); } fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { @@ -60,7 +60,7 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { 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); + 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]; @@ -77,7 +77,7 @@ fn testBytesAlign(b: u8) void { b, }; const ptr = @ptrCast(*u32, &bytes[0]); - assert(ptr.* == 0x33333333); + assertOrPanic(ptr.* == 0x33333333); } test "specifying alignment allows slice cast" { @@ -91,13 +91,13 @@ fn testBytesAlignSlice(b: u8) void { b, }; const slice: []u32 = @bytesToSlice(u32, bytes[0..]); - assert(slice[0] == 0x33333333); + assertOrPanic(slice[0] == 0x33333333); } test "@alignCast pointers" { var x: u32 align(4) = 1; expectsOnly1(&x); - assert(x == 2); + assertOrPanic(x == 2); } fn expectsOnly1(x: *align(1) u32) void { expects4(@alignCast(4, x)); @@ -113,7 +113,7 @@ test "@alignCast slices" { }; const slice = array[0..]; sliceExpectsOnly1(slice); - assert(slice[0] == 2); + assertOrPanic(slice[0] == 2); } fn sliceExpectsOnly1(slice: []align(1) u32) void { sliceExpects4(@alignCast(4, slice)); @@ -128,7 +128,7 @@ test "implicitly decreasing fn alignment" { } fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void { - assert(ptr() == answer); + assertOrPanic(ptr() == answer); } fn alignedSmall() align(8) i32 { @@ -139,7 +139,7 @@ fn alignedBig() align(16) i32 { } test "@alignCast functions" { - assert(fnExpectsOnly1(simple4) == 0x19); + assertOrPanic(fnExpectsOnly1(simple4) == 0x19); } fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 { return fnExpects4(@alignCast(4, ptr)); @@ -152,9 +152,9 @@ fn simple4() align(4) i32 { } test "generic function with align param" { - assert(whyWouldYouEverDoThis(1) == 0x1); - assert(whyWouldYouEverDoThis(4) == 0x1); - assert(whyWouldYouEverDoThis(8) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(1) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(4) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(8) == 0x1); } fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { @@ -164,28 +164,28 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { test "@ptrCast preserves alignment of bigger source" { var x: u32 align(16) = 1234; const ptr = @ptrCast(*u8, &x); - assert(@typeOf(ptr) == *align(16) u8); + 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 }; - assert(@typeOf(&array[0]) == *align(4) u8); - assert(@typeOf(&array[1]) == *u8); - assert(@typeOf(&array[2]) == *align(2) u8); - assert(@typeOf(&array[3]) == *u8); + 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 }; - 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); + 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 assert(@typeOf(smaller[0..]) == []align(2) u32); - comptime assert(@typeOf(smaller[0..].ptr) == [*]align(2) u32); + 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); @@ -198,14 +198,14 @@ test "runtime known array index has best alignment possible" { testIndex2(array[0..].ptr, 3, *u8); } fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { - comptime assert(@typeOf(&smaller[index]) == T); + comptime assertOrPanic(@typeOf(&smaller[index]) == T); } fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void { - comptime assert(@typeOf(&ptr[index]) == T); + comptime assertOrPanic(@typeOf(&ptr[index]) == T); } test "alignstack" { - assert(fnWithAlignedStack() == 1234); + assertOrPanic(fnWithAlignedStack() == 1234); } fn fnWithAlignedStack() i32 { @@ -214,7 +214,7 @@ fn fnWithAlignedStack() i32 { } test "alignment of structs" { - assert(@alignOf(struct { + assertOrPanic(@alignOf(struct { a: i32, b: *i32, }) == @alignOf(usize)); diff --git a/test/cases/alignof.zig b/test/stage1/behavior/alignof.zig similarity index 61% rename from test/cases/alignof.zig rename to test/stage1/behavior/alignof.zig index 433e86e45edf..98c805908b72 100644 --- a/test/cases/alignof.zig +++ b/test/stage1/behavior/alignof.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); const maxInt = std.math.maxInt; @@ -10,8 +10,9 @@ const Foo = struct { }; test "@alignOf(T) before referencing T" { - comptime assert(@alignOf(Foo) != maxInt(usize)); + comptime assertOrPanic(@alignOf(Foo) != maxInt(usize)); if (builtin.arch == builtin.Arch.x86_64) { - comptime assert(@alignOf(Foo) == 4); + comptime assertOrPanic(@alignOf(Foo) == 4); } } + diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig new file mode 100644 index 000000000000..118330520986 --- /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 000000000000..48701c583621 --- /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/cases/atomics.zig b/test/stage1/behavior/atomics.zig similarity index 65% rename from test/cases/atomics.zig rename to test/stage1/behavior/atomics.zig index 67c9ab3dd117..fa3c5f29a603 100644 --- a/test/cases/atomics.zig +++ b/test/stage1/behavior/atomics.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -7,18 +7,18 @@ const AtomicOrder = builtin.AtomicOrder; test "cmpxchg" { var x: i32 = 1234; if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); + assertOrPanic(x1 == 1234); } else { @panic("cmpxchg should have failed"); } while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); + assertOrPanic(x1 == 1234); } - assert(x == 5678); + assertOrPanic(x == 5678); - assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == 42); + assertOrPanic(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assertOrPanic(x == 42); } test "fence" { @@ -30,24 +30,24 @@ test "fence" { test "atomicrmw and atomicload" { var data: u8 = 200; testAtomicRmw(&data); - assert(data == 42); + assertOrPanic(data == 42); testAtomicLoad(&data); } fn testAtomicRmw(ptr: *u8) void { const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); - assert(prev_value == 200); + assertOrPanic(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); + 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); - assert(x == 42); + assertOrPanic(x == 42); } test "cmpxchg with ptr" { @@ -56,16 +56,16 @@ test "cmpxchg with ptr" { var data3: i32 = 9101; var x: *i32 = &data1; if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); + assertOrPanic(x1 == &data1); } else { @panic("cmpxchg should have failed"); } while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); + assertOrPanic(x1 == &data1); } - assert(x == &data3); + assertOrPanic(x == &data3); - assert(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == &data2); + assertOrPanic(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assertOrPanic(x == &data2); } diff --git a/test/cases/bit_shifting.zig b/test/stage1/behavior/bit_shifting.zig similarity index 90% rename from test/cases/bit_shifting.zig rename to test/stage1/behavior/bit_shifting.zig index 325e765bb0a5..3290688358fe 100644 --- a/test/cases/bit_shifting.zig +++ b/test/stage1/behavior/bit_shifting.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; 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); + 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 { @@ -77,12 +77,12 @@ fn testShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, c var node_buffer: [node_count]Table.Node = undefined; for (node_buffer) |*node, i| { const key = @intCast(Key, i); - assert(table.get(key) == null); + assertOrPanic(table.get(key) == null); node.init(key, {}); table.put(node); } for (node_buffer) |*node, i| { - assert(table.get(@intCast(Key, i)) == node); + assertOrPanic(table.get(@intCast(Key, i)) == node); } } diff --git a/test/cases/bitcast.zig b/test/stage1/behavior/bitcast.zig similarity index 78% rename from test/cases/bitcast.zig rename to test/stage1/behavior/bitcast.zig index d85a84ed225e..19030255e4f7 100644 --- a/test/cases/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const maxInt = std.math.maxInt; test "@bitCast i32 -> u32" { @@ -8,8 +8,8 @@ test "@bitCast i32 -> u32" { } fn testBitCast_i32_u32() void { - assert(conv(-1) == maxInt(u32)); - assert(conv2(maxInt(u32)) == -1); + assertOrPanic(conv(-1) == maxInt(u32)); + assertOrPanic(conv2(maxInt(u32)) == -1); } fn conv(x: i32) u32 { @@ -27,11 +27,10 @@ test "@bitCast extern enum to its integer type" { fn testBitCastExternEnum() void { var SOCK_DGRAM = @This().B; var sock_dgram = @bitCast(c_int, SOCK_DGRAM); - assert(sock_dgram == 1); + 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 000000000000..97787ace84f2 --- /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/cases/bool.zig b/test/stage1/behavior/bool.zig similarity index 50% rename from test/cases/bool.zig rename to test/stage1/behavior/bool.zig index 3e4ac9c1cfe4..2d7241526f42 100644 --- a/test/cases/bool.zig +++ b/test/stage1/behavior/bool.zig @@ -1,25 +1,25 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "bool literals" { - assert(true); - assert(!false); + assertOrPanic(true); + assertOrPanic(!false); } test "cast bool to int" { const t = true; const f = false; - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); + assertOrPanic(@boolToInt(t) == u32(1)); + assertOrPanic(@boolToInt(f) == u32(0)); nonConstCastBoolToInt(t, f); } fn nonConstCastBoolToInt(t: bool, f: bool) void { - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); + assertOrPanic(@boolToInt(t) == u32(1)); + assertOrPanic(@boolToInt(f) == u32(0)); } test "bool cmp" { - assert(testBoolCmp(true, false) == false); + assertOrPanic(testBoolCmp(true, false) == false); } fn testBoolCmp(a: bool, b: bool) bool { return a == b; @@ -30,6 +30,6 @@ 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); + 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 000000000000..8084538e03c0 --- /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/cases/bugs/1076.zig b/test/stage1/behavior/bugs/1076.zig similarity index 75% rename from test/cases/bugs/1076.zig rename to test/stage1/behavior/bugs/1076.zig index 7b843123104e..69a7e70f7dbb 100644 --- a/test/cases/bugs/1076.zig +++ b/test/stage1/behavior/bugs/1076.zig @@ -1,6 +1,6 @@ const std = @import("std"); const mem = std.mem; -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "comptime code should not modify constant data" { testCastPtrOfArrayToSliceAndPtr(); @@ -11,6 +11,6 @@ fn testCastPtrOfArrayToSliceAndPtr() void { var array = "aoeu"; const x: [*]u8 = &array; x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); + assertOrPanic(mem.eql(u8, array[0..], "boeu")); } diff --git a/test/cases/bugs/1111.zig b/test/stage1/behavior/bugs/1111.zig similarity index 100% rename from test/cases/bugs/1111.zig rename to test/stage1/behavior/bugs/1111.zig diff --git a/test/cases/bugs/1277.zig b/test/stage1/behavior/bugs/1277.zig similarity index 100% rename from test/cases/bugs/1277.zig rename to test/stage1/behavior/bugs/1277.zig diff --git a/test/cases/bugs/1322.zig b/test/stage1/behavior/bugs/1322.zig similarity index 66% rename from test/cases/bugs/1322.zig rename to test/stage1/behavior/bugs/1322.zig index 2de92191ec17..2e67f4473fec 100644 --- a/test/cases/bugs/1322.zig +++ b/test/stage1/behavior/bugs/1322.zig @@ -13,7 +13,7 @@ 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); + std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).c); a = A{ .b = B.None }; - std.debug.assert(@TagType(B)(a.b) == @TagType(B).None); + std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).None); } diff --git a/test/cases/bugs/1381.zig b/test/stage1/behavior/bugs/1381.zig similarity index 100% rename from test/cases/bugs/1381.zig rename to test/stage1/behavior/bugs/1381.zig diff --git a/test/cases/bugs/1421.zig b/test/stage1/behavior/bugs/1421.zig similarity index 70% rename from test/cases/bugs/1421.zig rename to test/stage1/behavior/bugs/1421.zig index fcbb8b70e4f8..fbc932781ab9 100644 --- a/test/cases/bugs/1421.zig +++ b/test/stage1/behavior/bugs/1421.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const S = struct { fn method() builtin.TypeInfo { @@ -10,5 +10,5 @@ const S = struct { test "functions with return type required to be comptime are generic" { const ti = S.method(); - assert(builtin.TypeId(ti) == builtin.TypeId.Struct); + assertOrPanic(builtin.TypeId(ti) == builtin.TypeId.Struct); } diff --git a/test/cases/bugs/1442.zig b/test/stage1/behavior/bugs/1442.zig similarity index 100% rename from test/cases/bugs/1442.zig rename to test/stage1/behavior/bugs/1442.zig diff --git a/test/cases/bugs/1486.zig b/test/stage1/behavior/bugs/1486.zig similarity index 51% rename from test/cases/bugs/1486.zig rename to test/stage1/behavior/bugs/1486.zig index 98fae36d3a0f..0483e3828cde 100644 --- a/test/cases/bugs/1486.zig +++ b/test/stage1/behavior/bugs/1486.zig @@ -1,11 +1,11 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const ptr = &global; var global: u64 = 123; test "constant pointer to global variable causes runtime load" { global = 1234; - assert(&global == ptr); - assert(ptr.* == 1234); + assertOrPanic(&global == ptr); + assertOrPanic(ptr.* == 1234); } diff --git a/test/cases/bugs/394.zig b/test/stage1/behavior/bugs/394.zig similarity index 68% rename from test/cases/bugs/394.zig rename to test/stage1/behavior/bugs/394.zig index b0afec23572a..766ad9e15761 100644 --- a/test/cases/bugs/394.zig +++ b/test/stage1/behavior/bugs/394.zig @@ -7,12 +7,12 @@ const S = struct { y: E, }; -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "bug 394 fixed" { const x = S{ .x = 3, .y = E{ .B = 1 }, }; - assert(x.x == 3); + assertOrPanic(x.x == 3); } diff --git a/test/cases/bugs/655.zig b/test/stage1/behavior/bugs/655.zig similarity index 67% rename from test/cases/bugs/655.zig rename to test/stage1/behavior/bugs/655.zig index ebb8da0658d4..67ba6a231f33 100644 --- a/test/cases/bugs/655.zig +++ b/test/stage1/behavior/bugs/655.zig @@ -3,10 +3,10 @@ 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); + comptime std.debug.assertOrPanic(@typeOf(&x) == *const other_file.Integer); foo(&x); } fn foo(x: *const other_file.Integer) void { - std.debug.assert(x.* == 1234); + std.debug.assertOrPanic(x.* == 1234); } diff --git a/test/cases/bugs/655_other_file.zig b/test/stage1/behavior/bugs/655_other_file.zig similarity index 100% rename from test/cases/bugs/655_other_file.zig rename to test/stage1/behavior/bugs/655_other_file.zig diff --git a/test/cases/bugs/656.zig b/test/stage1/behavior/bugs/656.zig similarity index 84% rename from test/cases/bugs/656.zig rename to test/stage1/behavior/bugs/656.zig index f93f0ac4d51f..cb37fe67fe84 100644 --- a/test/cases/bugs/656.zig +++ b/test/stage1/behavior/bugs/656.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const PrefixOp = union(enum) { Return, @@ -22,7 +22,7 @@ fn foo(a: bool, b: bool) void { PrefixOp.AddrOf => |addr_of_info| { if (b) {} if (addr_of_info.align_expr) |align_expr| { - assert(align_expr == 1234); + assertOrPanic(align_expr == 1234); } }, PrefixOp.Return => {}, diff --git a/test/cases/bugs/726.zig b/test/stage1/behavior/bugs/726.zig similarity index 71% rename from test/cases/bugs/726.zig rename to test/stage1/behavior/bugs/726.zig index 2acc91eb26c1..ce20480c6332 100644 --- a/test/cases/bugs/726.zig +++ b/test/stage1/behavior/bugs/726.zig @@ -1,9 +1,9 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "@ptrCast from const to nullable" { const c: u8 = 4; var x: ?*const u8 = @ptrCast(?*const u8, &c); - assert(x.?.* == 4); + assertOrPanic(x.?.* == 4); } test "@ptrCast from var in empty struct to nullable" { @@ -11,6 +11,6 @@ test "@ptrCast from var in empty struct to nullable" { var c: u8 = 4; }; var x: ?*const u8 = @ptrCast(?*const u8, &container.c); - assert(x.?.* == 4); + assertOrPanic(x.?.* == 4); } diff --git a/test/cases/bugs/828.zig b/test/stage1/behavior/bugs/828.zig similarity index 100% rename from test/cases/bugs/828.zig rename to test/stage1/behavior/bugs/828.zig diff --git a/test/cases/bugs/920.zig b/test/stage1/behavior/bugs/920.zig similarity index 95% rename from test/cases/bugs/920.zig rename to test/stage1/behavior/bugs/920.zig index 2903f05a29ab..e29c5c4acf5a 100644 --- a/test/cases/bugs/920.zig +++ b/test/stage1/behavior/bugs/920.zig @@ -60,6 +60,6 @@ test "bug 920 fixed" { }; for (NormalDist1.f) |_, i| { - std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]); + std.debug.assertOrPanic(NormalDist1.f[i] == NormalDist.f[i]); } } diff --git a/test/cases/byval_arg_var.zig b/test/stage1/behavior/byval_arg_var.zig similarity index 70% rename from test/cases/byval_arg_var.zig rename to test/stage1/behavior/byval_arg_var.zig index 826b9cc9e593..14ee212ce07c 100644 --- a/test/cases/byval_arg_var.zig +++ b/test/stage1/behavior/byval_arg_var.zig @@ -2,11 +2,11 @@ const std = @import("std"); var result: []const u8 = "wrong"; -test "aoeu" { +test "pass string literal byvalue to a generic var param" { start(); blowUpStack(10); - std.debug.assert(std.mem.eql(u8, result, "string literal")); + std.debug.assertOrPanic(std.mem.eql(u8, result, "string literal")); } fn start() void { diff --git a/test/cases/cancel.zig b/test/stage1/behavior/cancel.zig similarity index 84% rename from test/cases/cancel.zig rename to test/stage1/behavior/cancel.zig index c0f74fd34f44..863da4bdb831 100644 --- a/test/cases/cancel.zig +++ b/test/stage1/behavior/cancel.zig @@ -10,9 +10,9 @@ test "cancel forwards" { 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); + std.debug.assertOrPanic(defer_f1); + std.debug.assertOrPanic(defer_f2); + std.debug.assertOrPanic(defer_f3); } async fn f1() void { @@ -47,10 +47,10 @@ test "cancel backwards" { 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); + std.debug.assertOrPanic(defer_b1); + std.debug.assertOrPanic(defer_b2); + std.debug.assertOrPanic(defer_b3); + std.debug.assertOrPanic(defer_b4); } async fn b1() void { diff --git a/test/cases/cast.zig b/test/stage1/behavior/cast.zig similarity index 66% rename from test/cases/cast.zig rename to test/stage1/behavior/cast.zig index bd45bbc00f08..1a8b61454069 100644 --- a/test/cases/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; @@ -7,12 +7,12 @@ test "int to ptr cast" { const x = usize(13); const y = @intToPtr(*u8, x); const z = @ptrToInt(y); - assert(z == 13); + assertOrPanic(z == 13); } test "integer literal to pointer cast" { const vga_mem = @intToPtr(*u16, 0xB8000); - assert(@ptrToInt(vga_mem) == 0xB8000); + assertOrPanic(@ptrToInt(vga_mem) == 0xB8000); } test "pointer reinterpret const float to int" { @@ -20,7 +20,7 @@ test "pointer reinterpret const float to int" { const float_ptr = &float; const int_ptr = @ptrCast(*const i32, float_ptr); const int_val = int_ptr.*; - assert(int_val == 858993411); + assertOrPanic(int_val == 858993411); } test "implicitly cast indirect pointer to maybe-indirect pointer" { @@ -44,10 +44,10 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { 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)); + 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" { @@ -57,14 +57,14 @@ test "explicit cast from integer to error type" { fn testCastIntToErr(err: anyerror) void { const x = @errorToInt(err); const y = @intToError(x); - assert(error.ItBroke == y); + assertOrPanic(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")); + 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"; @@ -75,30 +75,32 @@ test "peer resolve array and const slice" { comptime testPeerResolveArrayConstSlice(true); } fn testPeerResolveArrayConstSlice(b: bool) void { - const value1 = if (b) "aoeu" else ([]const u8)("zz"); + // TODO: https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 + const value1: []const u8 = 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")); + 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; - assert((try y).? == 1); + assertOrPanic((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); + assertOrPanic((b catch unreachable).?.a == 1); } test "implicitly cast from int to anyerror!?T" { @@ -113,7 +115,7 @@ fn implicitIntLitToOptional() void { test "return null from fn() anyerror!?&T" { const a = returnNullFromOptionalTypeErrorRef(); const b = returnNullLitFromOptionalTypeErrorRef(); - assert((try a) == null and (try b) == null); + assertOrPanic((try a) == null and (try b) == null); } fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { const a: ?*A = null; @@ -124,11 +126,11 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { } test "peer type resolution: ?T and T" { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); comptime { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); } } fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { @@ -140,11 +142,11 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { } test "peer type resolution: [0]u8 and []const u8" { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); comptime { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); } } fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { @@ -156,8 +158,8 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "implicitly cast from [N]T to ?[]const T" { - assert(mem.eql(u8, castToOptionalSlice().?, "hi")); - comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi")); + assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); } fn castToOptionalSlice() ?[]const u8 { @@ -170,7 +172,7 @@ test "implicitly cast from [0]T to anyerror![]T" { } fn testCastZeroArrayToErrSliceMut() void { - assert((gimmeErrOrSlice() catch unreachable).len == 0); + assertOrPanic((gimmeErrOrSlice() catch unreachable).len == 0); } fn gimmeErrOrSlice() anyerror![]u8 { @@ -181,14 +183,14 @@ 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); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((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); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } } fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { @@ -206,7 +208,7 @@ test "resolve undefined with integer" { fn testResolveUndefWithInt(b: bool, x: i32) void { const value = if (b) x else undefined; if (b) { - assert(value == x); + assertOrPanic(value == x); } } @@ -218,17 +220,17 @@ test "implicit cast from &const [N]T to []const T" { fn testCastConstArrayRefToConstSlice() void { const blah = "aoeu"; const const_array_ref = &blah; - assert(@typeOf(const_array_ref) == *const [4]u8); + assertOrPanic(@typeOf(const_array_ref) == *const [4]u8); const slice: []const u8 = const_array_ref; - assert(mem.eql(u8, slice, "aoeu")); + 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 - //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")); + //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 { @@ -252,9 +254,9 @@ test "@floatToInt" { fn testFloatToInts() void { const x = i32(1e4); - assert(x == 10000); + assertOrPanic(x == 10000); const y = @floatToInt(i32, f32(1e4)); - assert(y == 10000); + assertOrPanic(y == 10000); expectFloatToInt(f16, 255.1, u8, 255); expectFloatToInt(f16, 127.2, i8, 127); expectFloatToInt(f16, -128.2, i8, -128); @@ -265,7 +267,7 @@ fn testFloatToInts() void { } fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { - assert(@floatToInt(I, f) == i); + assertOrPanic(@floatToInt(I, f) == i); } test "cast u128 to f128 and back" { @@ -274,7 +276,7 @@ test "cast u128 to f128 and back" { } fn testCast128() void { - assert(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); + assertOrPanic(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); } fn cast128Int(x: f128) u128 { @@ -294,9 +296,9 @@ test "const slice widen cast" { }; const u32_value = @bytesToSlice(u32, bytes[0..])[0]; - assert(u32_value == 0x12121212); + assertOrPanic(u32_value == 0x12121212); - assert(@bitCast(u32, bytes) == 0x12121212); + assertOrPanic(@bitCast(u32, bytes) == 0x12121212); } test "single-item pointer of array to slice and to unknown length pointer" { @@ -308,76 +310,76 @@ fn testCastPtrOfArrayToSliceAndPtr() void { var array = "aoeu"; const x: [*]u8 = &array; x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); + assertOrPanic(mem.eql(u8, array[0..], "boeu")); const y: []u8 = &array; y[0] += 1; - assert(mem.eql(u8, array[0..], "coeu")); + 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; - assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); + assertOrPanic(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); + assertOrPanic(@typeOf(result) == i32); + assertOrPanic(result == 1234); } test "@floatCast comptime_int and comptime_float" { { const result = @floatCast(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f16, 1234.0); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f32, 1234.0); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } } test "comptime_int @intToFloat" { { const result = @intToFloat(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @intToFloat(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + 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 assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); + 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); - assert(z == 0xff); + assertOrPanic(z == 0xff); } test "implicit cast undefined to optional" { - assert(MakeType(void).getNull() == null); - assert(MakeType(void).getNonNull() != null); + assertOrPanic(MakeType(void).getNull() == null); + assertOrPanic(MakeType(void).getNonNull() != null); } fn MakeType(comptime T: type) type { @@ -397,16 +399,16 @@ test "implicit cast from *[N]T to ?[*]T" { var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; x = &y; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); + assertOrPanic(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])); + 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.assert(a == 2); + std.debug.assertOrPanic(a == 2); } fn incrementVoidPtrValue(value: ?*c_void) void { @@ -416,7 +418,7 @@ fn incrementVoidPtrValue(value: ?*c_void) void { 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 })); + assertOrPanic(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); } fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { @@ -440,27 +442,27 @@ 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)); + 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); - assert(b.* == 1); + assertOrPanic(b.* == 1); var ptr2: ?*c_void = &a; var c: *u32 = @ptrCast(*u32, ptr2.?); - assert(c.* == 1); + assertOrPanic(c.* == 1); } test "@intCast to comptime_int" { - assert(@intCast(comptime_int, 0) == 0); + 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; - assert(b == 255); + assertOrPanic(b == 255); } test "@intToEnum passed a comptime_int to an enum with one item" { @@ -468,5 +470,5 @@ test "@intToEnum passed a comptime_int to an enum with one item" { A, }; const x = @intToEnum(E, 0); - assert(x == E.A); + assertOrPanic(x == E.A); } diff --git a/test/cases/const_slice_child.zig b/test/stage1/behavior/const_slice_child.zig similarity index 80% rename from test/cases/const_slice_child.zig rename to test/stage1/behavior/const_slice_child.zig index 07d02d5df06f..5b9b70a55801 100644 --- a/test/cases/const_slice_child.zig +++ b/test/stage1/behavior/const_slice_child.zig @@ -1,5 +1,5 @@ const debug = @import("std").debug; -const assert = debug.assert; +const assertOrPanic = debug.assertOrPanic; var argv: [*]const [*]const u8 = undefined; @@ -15,10 +15,10 @@ test "const slice child" { } 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")); + assertOrPanic(args.len == 3); + assertOrPanic(streql(args[0], "one")); + assertOrPanic(streql(args[1], "two")); + assertOrPanic(streql(args[2], "three")); } fn bar(argc: usize) void { diff --git a/test/cases/coroutine_await_struct.zig b/test/stage1/behavior/coroutine_await_struct.zig similarity index 86% rename from test/cases/coroutine_await_struct.zig rename to test/stage1/behavior/coroutine_await_struct.zig index 79168715d8e2..6ca2a301eca5 100644 --- a/test/cases/coroutine_await_struct.zig +++ b/test/stage1/behavior/coroutine_await_struct.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const Foo = struct { x: i32, @@ -18,8 +18,8 @@ test "coroutine await struct" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result.x == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + assertOrPanic(await_final_result.x == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); diff --git a/test/cases/coroutines.zig b/test/stage1/behavior/coroutines.zig similarity index 86% rename from test/cases/coroutines.zig rename to test/stage1/behavior/coroutines.zig index 89490ebc2c1c..a2327c506011 100644 --- a/test/cases/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; var x: i32 = 1; @@ -9,9 +9,9 @@ test "create a coroutine and cancel it" { defer da.deinit(); const p = try async<&da.allocator> simpleAsyncFn(); - comptime assert(@typeOf(p) == promise->void); + comptime assertOrPanic(@typeOf(p) == promise->void); cancel p; - assert(x == 2); + assertOrPanic(x == 2); } async fn simpleAsyncFn() void { x += 1; @@ -31,7 +31,7 @@ test "coroutine suspend, resume, cancel" { cancel p; seq('g'); - assert(std.mem.eql(u8, points, "abcdefg")); + assertOrPanic(std.mem.eql(u8, points, "abcdefg")); } async fn testAsyncSeq() void { defer seq('e'); @@ -53,9 +53,9 @@ test "coroutine suspend with block" { defer da.deinit(); const p = try async<&da.allocator> testSuspendBlock(); - std.debug.assert(!result); + std.debug.assertOrPanic(!result); resume a_promise; - std.debug.assert(result); + std.debug.assertOrPanic(result); cancel p; } @@ -63,13 +63,13 @@ var a_promise: promise = undefined; var result = false; async fn testSuspendBlock() void { suspend { - comptime assert(@typeOf(@handle()) == promise->void); + 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(); - assert( a_promise == @handle() ); + assertOrPanic(a_promise == @handle()); result = true; } @@ -86,8 +86,8 @@ test "coroutine await" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + assertOrPanic(await_final_result == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); @@ -123,8 +123,8 @@ test "coroutine await early return" { 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")); + assertOrPanic(early_final_result == 1234); + assertOrPanic(std.mem.eql(u8, early_points, "abcdef")); } async fn early_amain() void { early_seq('b'); @@ -170,7 +170,7 @@ test "async function with dot syntax" { defer da.deinit(); const p = try async<&da.allocator> S.foo(); cancel p; - assert(S.y == 2); + assertOrPanic(S.y == 2); } test "async fn pointer in a struct field" { @@ -182,9 +182,9 @@ test "async fn pointer in a struct field" { var da = std.heap.DirectAllocator.init(); defer da.deinit(); const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; - assert(data == 2); + assertOrPanic(data == 2); cancel p; - assert(data == 4); + assertOrPanic(data == 4); } async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { defer y.* += 2; @@ -199,6 +199,7 @@ test "async fn with inferred error set" { resume p; cancel p; } + async fn failing() !void { suspend; return error.Fail; @@ -220,8 +221,7 @@ test "error return trace across suspend points - async return" { cancel p2; } -// TODO https://github.com/ziglang/zig/issues/760 -fn nonFailing() promise->(anyerror!void) { +fn nonFailing() (promise->anyerror!void) { return async suspendThenFail() catch unreachable; } async fn suspendThenFail() anyerror!void { @@ -230,9 +230,9 @@ async fn suspendThenFail() anyerror!void { } async fn printTrace(p: promise->(anyerror!void)) void { (await p) catch |e| { - std.debug.assert(e == error.Fail); + std.debug.assertOrPanic(e == error.Fail); if (@errorReturnTrace()) |trace| { - assert(trace.index == 1); + assertOrPanic(trace.index == 1); } else switch (builtin.mode) { builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, @@ -246,7 +246,7 @@ test "break from suspend" { var my_result: i32 = 1; const p = try async testBreakFromSuspend(&my_result); cancel p; - std.debug.assert(my_result == 2); + std.debug.assertOrPanic(my_result == 2); } async fn testBreakFromSuspend(my_result: *i32) void { suspend { diff --git a/test/cases/defer.zig b/test/stage1/behavior/defer.zig similarity index 72% rename from test/cases/defer.zig rename to test/stage1/behavior/defer.zig index f9a2b69cd94b..1a5125d55cc9 100644 --- a/test/cases/defer.zig +++ b/test/stage1/behavior/defer.zig @@ -1,39 +1,4 @@ -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'); -} +const assertOrPanic = @import("std").debug.assertOrPanic; test "break and continue inside loop inside defer expression" { testBreakContInDefer(10); @@ -47,7 +12,7 @@ fn testBreakContInDefer(x: usize) void { if (i < 5) continue; if (i == 5) break; } - assert(i == 5); + assertOrPanic(i == 5); } } @@ -59,11 +24,11 @@ test "defer and labeled break" { break :blk; } - assert(i == 1); + assertOrPanic(i == 1); } test "errdefer does not apply to fn inside fn" { - if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assertOrPanic(e == error.Bad); } fn testNestedFnErrDefer() anyerror!void { @@ -76,3 +41,38 @@ fn testNestedFnErrDefer() anyerror!void { }; return S.baz(); } + +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'); +} diff --git a/test/cases/enum.zig b/test/stage1/behavior/enum.zig similarity index 84% rename from test/cases/enum.zig rename to test/stage1/behavior/enum.zig index 2dd552488c94..9de138ef78cd 100644 --- a/test/cases/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; test "enum type" { @@ -11,16 +11,16 @@ test "enum type" { }; 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); + 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| assert(value == 13), + Foo.One => |value| assertOrPanic(value == 13), else => unreachable, } } @@ -92,14 +92,14 @@ test "enum to int" { } fn shouldEqual(n: Number, expected: u3) void { - assert(@enumToInt(n) == expected); + assertOrPanic(@enumToInt(n) == expected); } test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) void { - assert(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); + assertOrPanic(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -110,8 +110,8 @@ const IntToEnumNumber = enum { }; test "@tagName" { - assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); - comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + comptime assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); } fn testEnumTagNameBare(n: BareNumber) []const u8 { @@ -126,8 +126,8 @@ const BareNumber = enum { test "enum alignment" { comptime { - assert(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); - assert(@alignOf(AlignTestEnum) >= @alignOf(u64)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf(u64)); } } @@ -663,10 +663,10 @@ const ValueCount257 = enum { test "enum sizes" { comptime { - assert(@sizeOf(ValueCount1) == 0); - assert(@sizeOf(ValueCount2) == 1); - assert(@sizeOf(ValueCount256) == 1); - assert(@sizeOf(ValueCount257) == 2); + assertOrPanic(@sizeOf(ValueCount1) == 0); + assertOrPanic(@sizeOf(ValueCount2) == 1); + assertOrPanic(@sizeOf(ValueCount256) == 1); + assertOrPanic(@sizeOf(ValueCount257) == 2); } } @@ -685,12 +685,12 @@ test "set enum tag type" { { var x = Small.One; x = Small.Two; - comptime assert(@TagType(Small) == u2); + comptime assertOrPanic(@TagType(Small) == u2); } { var x = Small2.One; x = Small2.Two; - comptime assert(@TagType(Small2) == u2); + comptime assertOrPanic(@TagType(Small2) == u2); } } @@ -737,17 +737,17 @@ const bit_field_1 = BitFieldOfEnums{ 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); + assertOrPanic(getA(&data) == A.Two); + assertOrPanic(getB(&data) == B.Three3); + assertOrPanic(getC(&data) == C.Four4); + comptime assertOrPanic(@sizeOf(BitFieldOfEnums) == 1); data.b = B.Four3; - assert(data.b == B.Four3); + assertOrPanic(data.b == B.Four3); data.a = A.Three; - assert(data.a == A.Three); - assert(data.b == B.Four3); + assertOrPanic(data.a == A.Three); + assertOrPanic(data.b == B.Four3); } fn getA(data: *const BitFieldOfEnums) A { @@ -768,7 +768,7 @@ test "casting enum to its tag type" { } fn testCastEnumToTagType(value: Small2) void { - assert(@enumToInt(value) == 1); + assertOrPanic(@enumToInt(value) == 1); } const MultipleChoice = enum(u32) { @@ -784,8 +784,8 @@ test "enum with specified tag values" { } fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { - assert(@enumToInt(x) == 60); - assert(1234 == switch (x) { + assertOrPanic(@enumToInt(x) == 60); + assertOrPanic(1234 == switch (x) { MultipleChoice.A => 1, MultipleChoice.B => 2, MultipleChoice.C => u32(1234), @@ -811,8 +811,8 @@ test "enum with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(x) == 1000); - assert(1234 == switch (x) { + assertOrPanic(@enumToInt(x) == 1000); + assertOrPanic(1234 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => 3, @@ -826,8 +826,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { } test "cast integer literal to enum" { - assert(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); - assert(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); + assertOrPanic(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + assertOrPanic(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); } const EnumWithOneMember = enum { @@ -865,14 +865,14 @@ const EnumWithTagValues = enum(u4) { D = 1 << 3, }; test "enum with tag values don't require parens" { - assert(@enumToInt(EnumWithTagValues.C) == 0b0100); + 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.assert(@sizeOf(Enum) == @sizeOf(u8)); + comptime @import("std").debug.assertOrPanic(@sizeOf(Enum) == @sizeOf(u8)); } test "empty extern enum with members" { @@ -881,14 +881,14 @@ test "empty extern enum with members" { B, C, }; - assert(@sizeOf(E) == @sizeOf(c_int)); + assertOrPanic(@sizeOf(E) == @sizeOf(c_int)); } -test "aoeu" { +test "tag name with assigned enum values" { const LocalFoo = enum { A = 1, B = 0, }; var b = LocalFoo.B; - assert(mem.eql(u8, @tagName(b), "B")); + assertOrPanic(mem.eql(u8, @tagName(b), "B")); } diff --git a/test/cases/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig similarity index 63% rename from test/cases/enum_with_members.zig rename to test/stage1/behavior/enum_with_members.zig index 088496bd2f41..49af1ceae78e 100644 --- a/test/cases/enum_with_members.zig +++ b/test/stage1/behavior/enum_with_members.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; const fmt = @import("std").fmt; @@ -19,9 +19,9 @@ test "enum with members" { 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")); + assertOrPanic((a.print(buf[0..]) catch unreachable) == 3); + assertOrPanic(mem.eql(u8, buf[0..3], "-42")); - assert((b.print(buf[0..]) catch unreachable) == 2); - assert(mem.eql(u8, buf[0..2], "42")); + assertOrPanic((b.print(buf[0..]) catch unreachable) == 2); + assertOrPanic(mem.eql(u8, buf[0..2], "42")); } diff --git a/test/cases/error.zig b/test/stage1/behavior/error.zig similarity index 59% rename from test/cases/error.zig rename to test/stage1/behavior/error.zig index a731f39021f7..35f8cc5d16c0 100644 --- a/test/cases/error.zig +++ b/test/stage1/behavior/error.zig @@ -1,5 +1,6 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; +const assertError = std.debug.assertError; const mem = std.mem; const builtin = @import("builtin"); @@ -18,7 +19,7 @@ pub fn baz() anyerror!i32 { } test "error wrapping" { - assert((baz() catch unreachable) == 15); + assertOrPanic((baz() catch unreachable) == 15); } fn gimmeItBroke() []const u8 { @@ -26,14 +27,14 @@ fn gimmeItBroke() []const u8 { } test "@errorName" { - assert(mem.eql(u8, @errorName(error.AnError), "AnError")); - assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); + 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); - assert(a != b); + assertOrPanic(a != b); } test "redefinition of error values allowed" { @@ -46,8 +47,8 @@ fn shouldBeNotEqual(a: anyerror, b: anyerror) void { test "error binary operator" { const a = errBinaryOperatorG(true) catch 3; const b = errBinaryOperatorG(false) catch 3; - assert(a == 3); - assert(b == 10); + assertOrPanic(a == 3); + assertOrPanic(b == 10); } fn errBinaryOperatorG(x: bool) anyerror!isize { return if (x) error.ItBroke else isize(10); @@ -55,7 +56,7 @@ fn errBinaryOperatorG(x: bool) anyerror!isize { test "unwrap simple value from error" { const i = unwrapSimpleValueFromErrorDo() catch unreachable; - assert(i == 13); + assertOrPanic(i == 13); } fn unwrapSimpleValueFromErrorDo() anyerror!isize { return 13; @@ -81,13 +82,13 @@ test "error union type " { 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); + 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 " { +test "error set type" { testErrorSetType(); comptime testErrorSetType(); } @@ -98,12 +99,12 @@ const MyErrSet = error{ }; fn testErrorSetType() void { - assert(@memberCount(MyErrSet) == 2); + assertOrPanic(@memberCount(MyErrSet) == 2); const a: MyErrSet!i32 = 5678; const b: MyErrSet!i32 = MyErrSet.OutOfMemory; - if (a) |value| assert(value == 5678) else |err| switch (err) { + if (a) |value| assertOrPanic(value == 5678) else |err| switch (err) { error.OutOfMemory => unreachable, error.FileNotFound => unreachable, } @@ -126,7 +127,7 @@ const Set2 = error{ fn testExplicitErrorSetCast(set1: Set1) void { var x = @errSetCast(Set2, set1); var y = @errSetCast(Set1, x); - assert(y == error.A); + assertOrPanic(y == error.A); } test "comptime test error for empty error set" { @@ -137,12 +138,12 @@ test "comptime test error for empty error set" { const EmptyErrorSet = error{}; fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { - if (x) |v| assert(v == 1234) else |err| @compileError("bad"); + if (x) |v| assertOrPanic(v == 1234) else |err| @compileError("bad"); } test "syntax: optional operator in front of error union operator" { comptime { - assert(?(anyerror!i32) == ?(anyerror!i32)); + assertOrPanic(?(anyerror!i32) == ?(anyerror!i32)); } } @@ -165,11 +166,17 @@ test "error union peer type resolution" { } fn testErrorUnionPeerTypeResolution(x: i32) void { - const y = switch (x) { + // TODO https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 + const y: anyerror!i32 = 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 { @@ -243,3 +250,76 @@ fn intLiteral(str: []const u8) !?i64 { 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); + // TODO uncomment this + //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); + // TODO uncomment this + //comptime assertError(S.errorable(), error.Failure); +} diff --git a/test/cases/eval.zig b/test/stage1/behavior/eval.zig similarity index 69% rename from test/cases/eval.zig rename to test/stage1/behavior/eval.zig index a9eded151ebc..7341df71993c 100644 --- a/test/cases/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); test "compile time recursion" { - assert(some_data.len == 21); + assertOrPanic(some_data.len == 21); } var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; fn fibonacci(x: i32) i32 { @@ -16,7 +16,7 @@ fn unwrapAndAddOne(blah: ?i32) i32 { } const should_be_1235 = unwrapAndAddOne(1234); test "static add one" { - assert(should_be_1235 == 1235); + assertOrPanic(should_be_1235 == 1235); } test "inlined loop" { @@ -24,7 +24,7 @@ test "inlined loop" { comptime var sum = 0; inline while (i <= 5) : (i += 1) sum += i; - assert(sum == 15); + assertOrPanic(sum == 15); } fn gimme1or2(comptime a: bool) i32 { @@ -34,12 +34,12 @@ fn gimme1or2(comptime a: bool) i32 { return z; } test "inline variable gets result of const if" { - assert(gimme1or2(true) == 1); - assert(gimme1or2(false) == 2); + assertOrPanic(gimme1or2(true) == 1); + assertOrPanic(gimme1or2(false) == 2); } test "static function evaluation" { - assert(statically_added_number == 3); + assertOrPanic(statically_added_number == 3); } const statically_added_number = staticAdd(1, 2); fn staticAdd(a: i32, b: i32) i32 { @@ -47,26 +47,28 @@ fn staticAdd(a: i32, b: i32) i32 { } test "const expr eval on single expr blocks" { - assert(constExprEvalOnSingleExprBlocksFn(1, true) == 3); + assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, false) == 3); + comptime assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, false) == 3); } fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { const literal = 3; + // TODO: See https://github.com/ziglang/zig/pull/1682#issuecomment-451303797 const result = if (b) b: { - break :b literal; - } else b: { break :b x; + } else b: { + break :b literal; }; 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); + 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, @@ -84,8 +86,8 @@ fn makePoint(x: i32, y: i32) Point { } test "static eval list init" { - assert(static_vec3.data[2] == 1.0); - assert(vec3(0.0, 0.0, 3.0).data[2] == 3.0); + 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 { @@ -101,12 +103,12 @@ pub fn vec3(x: f32, y: f32, z: f32) Vec3 { test "constant expressions" { var array: [array_size]u8 = undefined; - assert(@sizeOf(@typeOf(array)) == 20); + assertOrPanic(@sizeOf(@typeOf(array)) == 20); } const array_size: u8 = 20; test "constant struct with negation" { - assert(vertices[0].x == -0.6); + assertOrPanic(vertices[0].x == -0.6); } const Vertex = struct { x: f32, @@ -141,7 +143,7 @@ const vertices = []Vertex{ test "statically initialized struct" { st_init_str_foo.x += 1; - assert(st_init_str_foo.x == 14); + assertOrPanic(st_init_str_foo.x == 14); } const StInitStrFoo = struct { x: i32, @@ -154,7 +156,7 @@ var st_init_str_foo = StInitStrFoo{ test "statically initalized array literal" { const y: [4]u8 = st_init_arr_lit_x; - assert(y[3] == 4); + assertOrPanic(y[3] == 4); } const st_init_arr_lit_x = []u8{ 1, @@ -166,15 +168,15 @@ const st_init_arr_lit_x = []u8{ test "const slice" { comptime { const a = "1234567890"; - assert(a.len == 10); + assertOrPanic(a.len == 10); const b = a[1..2]; - assert(b.len == 1); - assert(b[0] == '2'); + assertOrPanic(b.len == 1); + assertOrPanic(b[0] == '2'); } } test "try to trick eval with runtime if" { - assert(testTryToTrickEvalWithRuntimeIf(true) == 10); + assertOrPanic(testTryToTrickEvalWithRuntimeIf(true) == 10); } fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { @@ -200,16 +202,16 @@ 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)); + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); comptime { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); } } @@ -254,14 +256,14 @@ fn performFn(comptime prefix_char: u8, start_value: i32) i32 { } test "comptime iterate over fn ptr list" { - assert(performFn('t', 1) == 6); - assert(performFn('o', 0) == 1); - assert(performFn('w', 99) == 99); + 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(); - assert(result == 1234); + assertOrPanic(result == 1234); } fn fnWithSetRuntimeSafety() i32 { @@ -271,7 +273,7 @@ fn fnWithSetRuntimeSafety() i32 { test "eval @setFloatMode at compile-time" { const result = comptime fnWithFloatMode(); - assert(result == 1234.0); + assertOrPanic(result == 1234.0); } fn fnWithFloatMode() f32 { @@ -292,15 +294,15 @@ 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); + assertOrPanic(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'); + assertOrPanic(bytes[0] == 'a'); + assertOrPanic(bytes[9] == 'b'); } } @@ -328,9 +330,9 @@ fn testCompTimeUIntComparisons(x: u32) void { } test "const ptr to variable data changes at runtime" { - assert(foo_ref.name[0] == 'a'); + assertOrPanic(foo_ref.name[0] == 'a'); foo_ref.name = "b"; - assert(foo_ref.name[0] == 'b'); + assertOrPanic(foo_ref.name[0] == 'b'); } const Foo = struct { @@ -341,8 +343,8 @@ 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); + assertOrPanic(global_array[5] == 5 * 5); + assertOrPanic(global_array[9] == 9 * 9); } const global_array = x: { @@ -357,7 +359,7 @@ test "compile-time downcast when the bits fit" { comptime { const spartan_count: u16 = 255; const byte = @intCast(u8, spartan_count); - assert(byte == 255); + assertOrPanic(byte == 255); } } @@ -365,44 +367,45 @@ 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]); + comptime assertOrPanic(&hi1[0] == &hi2[0]); } fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { - assert(ptr1 == ptr2); + assertOrPanic(ptr1 == ptr2); } test "@setEvalBranchQuota" { comptime { - // 1001 for the loop and then 1 more for the assert fn call + // 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; } - assert(sum == 500500); + assertOrPanic(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 assertOrPanic(16777216.0 + 1.0 == 16777217.0); +// TODO assertOrPanic(9007199254740992.0 + 1.0 == 9007199254740993.0); // TODO } test "f32 at compile time is lossy" { - assert(f32(1 << 24) + 1 == 1 << 24); + assertOrPanic(f32(1 << 24) + 1 == 1 << 24); } test "f64 at compile time is lossy" { - assert(f64(1 << 53) + 1 == 1 << 53); + assertOrPanic(f64(1 << 53) + 1 == 1 << 53); } test "f128 at compile time is lossy" { - assert(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); + assertOrPanic(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); } -// TODO need a better implementation of bigfloat_init_bigint -// assert(f128(1 << 113) == 10384593717069655257060992658440192); +comptime { + assertOrPanic(f128(1 << 113) == 10384593717069655257060992658440192); +} pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { return struct { @@ -413,15 +416,15 @@ pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { 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); + 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]; - assert(slice1.len == 0); + assertOrPanic(slice1.len == 0); const slice2 = ([*]i32)(undefined)[100..100]; - assert(slice2.len == 0); + assertOrPanic(slice2.len == 0); } fn copyWithPartialInline(s: []u32, b: []u8) void { @@ -443,16 +446,16 @@ test "binary math operator in partially inlined function" { 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); + 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 { - assert(MakeType(i32) == MakeType(i32)); - assert(MakeType(i32) != MakeType(f64)); + assertOrPanic(MakeType(i32) == MakeType(i32)); + assertOrPanic(MakeType(i32) != MakeType(f64)); } } @@ -468,7 +471,7 @@ test "comptime function with mutable pointer is not memoized" { const ptr = &x; increment(ptr); increment(ptr); - assert(x == 3); + assertOrPanic(x == 3); } } @@ -494,14 +497,14 @@ fn doesAlotT(comptime T: type, value: usize) T { } test "@setEvalBranchQuota at same scope as generic function call" { - assert(doesAlotT(u32, 2) == 2); + assertOrPanic(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); + assertOrPanic(buff[0..][0..][0] == 1); } } @@ -510,7 +513,7 @@ test "comptime slice of pointer preserves comptime var" { var buff: [10]u8 = undefined; var a = buff[0..].ptr; a[0..1][0] = 1; - assert(buff[0..][0..][0] == 1); + assertOrPanic(buff[0..][0..][0] == 1); } } @@ -524,9 +527,9 @@ const SingleFieldStruct = struct { test "const ptr to comptime mutable data is not memoized" { comptime { var foo = SingleFieldStruct{ .x = 1 }; - assert(foo.read_x() == 1); + assertOrPanic(foo.read_x() == 1); foo.x = 2; - assert(foo.read_x() == 2); + assertOrPanic(foo.read_x() == 2); } } @@ -535,7 +538,7 @@ test "array concat of slices gives slice" { var a: []const u8 = "aoeu"; var b: []const u8 = "asdf"; const c = a ++ b; - assert(std.mem.eql(u8, c, "aoeuasdf")); + assertOrPanic(std.mem.eql(u8, c, "aoeuasdf")); } } @@ -552,14 +555,14 @@ test "comptime shlWithOverflow" { break :amt amt; }; - assert(ct_shifted == rt_shifted); + assertOrPanic(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); + assertOrPanic(c == 15231399999); } pub const Info = struct { @@ -572,20 +575,20 @@ test "comptime modification of const struct field" { comptime { var res = diamond_info; res.version = 1; - assert(diamond_info.version == 0); - assert(res.version == 1); + assertOrPanic(diamond_info.version == 0); + assertOrPanic(res.version == 1); } } test "pointer to type" { comptime { var T: type = i32; - assert(T == i32); + assertOrPanic(T == i32); var ptr = &T; - assert(@typeOf(ptr) == *type); + assertOrPanic(@typeOf(ptr) == *type); ptr.* = f32; - assert(T == f32); - assert(*T == *f32); + assertOrPanic(T == f32); + assertOrPanic(*T == *f32); } } @@ -594,17 +597,17 @@ test "slice of type" { 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), + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), else => unreachable, } } for (types_array[0..]) |T, i| { switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), else => unreachable, } } @@ -621,7 +624,7 @@ fn wrap(comptime T: type) Wrapper { test "function which returns struct with type field causes implicit comptime" { const ty = wrap(i32).T; - assert(ty == i32); + assertOrPanic(ty == i32); } test "call method with comptime pass-by-non-copying-value self parameter" { @@ -635,12 +638,12 @@ test "call method with comptime pass-by-non-copying-value self parameter" { const s = S{ .a = 2 }; var b = s.b(); - assert(b == 2); + assertOrPanic(b == 2); } test "@tagName of @typeId" { const str = @tagName(@typeId(u8)); - assert(std.mem.eql(u8, str, "Int")); + assertOrPanic(std.mem.eql(u8, str, "Int")); } test "setting backward branch quota just before a generic fn call" { @@ -661,8 +664,8 @@ 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); + if (i == 0) assertOrPanic(x); + if (i == 1) assertOrPanic(x == 42); } } @@ -672,7 +675,7 @@ test "inline for with same type but different values" { var a: T = undefined; res += a.len; } - assert(res == 5); + assertOrPanic(res == 5); } test "refer to the type of a generic function" { @@ -686,29 +689,30 @@ 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); + assertOrPanic(zero_u1 == 0); } test "bit shift a u1" { var x: u1 = 1; var y = x << 0; - assert(y == 1); + assertOrPanic(y == 1); } test "@intCast to a u0" { var x: u8 = 0; var y: u0 = @intCast(u0, x); - assert(y == 0); + assertOrPanic(y == 0); } -test "@bytesToslice on a packed struct" { +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); + // TODO https://github.com/ziglang/zig/pull/1682#issuecomment-452813480 + var f = @bytesToSlice(F, b[0..]); + assertOrPanic(f[0].a == 9); } test "comptime pointer cast array and then slice" { @@ -720,8 +724,8 @@ test "comptime pointer cast array and then slice" { const ptrB: [*]const u8 = &array; const sliceB: []const u8 = ptrB[0..2]; - assert(sliceA[1] == 2); - assert(sliceB[1] == 2); + assertOrPanic(sliceA[1] == 2); + assertOrPanic(sliceB[1] == 2); } test "slice bounds in comptime concatenation" { @@ -730,47 +734,47 @@ test "slice bounds in comptime concatenation" { break :blk b[0..1]; }; const str = "" ++ bs; - assert(str.len == 1); - assert(std.mem.eql(u8, str, "1")); + assertOrPanic(str.len == 1); + assertOrPanic(std.mem.eql(u8, str, "1")); const str2 = bs ++ ""; - assert(str2.len == 1); - assert(std.mem.eql(u8, str2, "1")); + assertOrPanic(str2.len == 1); + assertOrPanic(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); + 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 { - assert(*align(1:0:2) u16 == *align(1) u16); + assertOrPanic(*align(1:0:2) u16 == *align(1) u16); // TODO add parsing support for this syntax - //assert(*align(:0:2) u16 == *u16); + //assertOrPanic(*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})); + assertOrPanic(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})); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 3 })); } fn oneItem(x: i32) [1]i32 { diff --git a/test/cases/field_parent_ptr.zig b/test/stage1/behavior/field_parent_ptr.zig similarity index 69% rename from test/cases/field_parent_ptr.zig rename to test/stage1/behavior/field_parent_ptr.zig index 00d4e0f36721..ed2487c02081 100644 --- a/test/cases/field_parent_ptr.zig +++ b/test/stage1/behavior/field_parent_ptr.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "@fieldParentPtr non-first field" { testParentFieldPtr(&foo.c); @@ -25,17 +25,17 @@ const foo = Foo{ }; fn testParentFieldPtr(c: *const i32) void { - assert(c == &foo.c); + assertOrPanic(c == &foo.c); const base = @fieldParentPtr(Foo, "c", c); - assert(base == &foo); - assert(&base.c == c); + assertOrPanic(base == &foo); + assertOrPanic(&base.c == c); } fn testParentFieldPtrFirst(a: *const bool) void { - assert(a == &foo.a); + assertOrPanic(a == &foo.a); const base = @fieldParentPtr(Foo, "a", a); - assert(base == &foo); - assert(&base.a == a); + assertOrPanic(base == &foo); + assertOrPanic(&base.a == a); } diff --git a/test/cases/fn.zig b/test/stage1/behavior/fn.zig similarity index 78% rename from test/cases/fn.zig rename to test/stage1/behavior/fn.zig index 8908bd785475..3011bc41d074 100644 --- a/test/cases/fn.zig +++ b/test/stage1/behavior/fn.zig @@ -1,7 +1,7 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "params" { - assert(testParamsAdd(22, 11) == 33); + assertOrPanic(testParamsAdd(22, 11) == 33); } fn testParamsAdd(a: i32, b: i32) i32 { return a + b; @@ -21,32 +21,32 @@ test "void parameters" { 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); + assertOrPanic(a + c == 3); return vv; } test "mutable local variables" { var zero: i32 = 0; - assert(zero == 0); + assertOrPanic(zero == 0); var i = i32(0); while (i != 3) { i += 1; } - assert(i == 3); + assertOrPanic(i == 3); } test "separate block scopes" { { const no_conflict: i32 = 5; - assert(no_conflict == 5); + assertOrPanic(no_conflict == 5); } const c = x: { const no_conflict = i32(10); break :x no_conflict; }; - assert(c == 10); + assertOrPanic(c == 10); } test "call function with empty string" { @@ -59,7 +59,7 @@ fn @"weird function name"() i32 { return 1234; } test "weird function name" { - assert(@"weird function name"() == 1234); + assertOrPanic(@"weird function name"() == 1234); } test "implicit cast function unreachable return" { @@ -80,7 +80,7 @@ test "function pointers" { fn4, }; for (fns) |f, i| { - assert(f() == @intCast(u32, i) + 5); + assertOrPanic(f() == @intCast(u32, i) + 5); } } fn fn1() u32 { @@ -97,7 +97,7 @@ fn fn4() u32 { } test "inline function call" { - assert(@inlineCall(add, 3, 9) == 12); + assertOrPanic(@inlineCall(add, 3, 9) == 12); } fn add(a: i32, b: i32) i32 { @@ -110,7 +110,7 @@ test "number literal as an argument" { } fn numberLiteralArg(a: var) void { - assert(a == 3); + assertOrPanic(a == 3); } test "assign inline fn to const variable" { @@ -121,7 +121,7 @@ test "assign inline fn to const variable" { inline fn inlineFn() void {} test "pass by non-copying value" { - assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); + assertOrPanic(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } const Point = struct { @@ -134,17 +134,17 @@ fn addPointCoords(pt: Point) i32 { } test "pass by non-copying value through var arg" { - assert(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); + assertOrPanic(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); } fn addPointCoordsVar(pt: var) i32 { - comptime assert(@typeOf(pt) == Point); + 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 }; - assert(pt.addPointCoords() == 3); + assertOrPanic(pt.addPointCoords() == 3); } const Point2 = struct { @@ -158,7 +158,7 @@ const Point2 = struct { test "pass by non-copying value as method, which is generic" { var pt = Point3{ .x = 1, .y = 2 }; - assert(pt.addPointCoords(i32) == 3); + assertOrPanic(pt.addPointCoords(i32) == 3); } const Point3 = struct { @@ -173,7 +173,7 @@ const Point3 = struct { test "pass by non-copying value as method, at comptime" { comptime { var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); + assertOrPanic(pt.addPointCoords() == 3); } } @@ -189,7 +189,7 @@ fn outer(y: u32) fn (u32) u32 { test "return inner function which references comptime variable of outer function" { var func = outer(10); - assert(func(3) == 7); + assertOrPanic(func(3) == 7); } test "extern struct with stdcallcc fn pointer" { @@ -203,5 +203,6 @@ test "extern struct with stdcallcc fn pointer" { var s: S = undefined; s.ptr = S.foo; - assert(s.ptr() == 1234); + assertOrPanic(s.ptr() == 1234); } + diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/stage1/behavior/fn_in_struct_in_comptime.zig similarity index 72% rename from test/cases/fn_in_struct_in_comptime.zig rename to test/stage1/behavior/fn_in_struct_in_comptime.zig index fabb57e9cbcb..0af076d40acf 100644 --- a/test/cases/fn_in_struct_in_comptime.zig +++ b/test/stage1/behavior/fn_in_struct_in_comptime.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn get_foo() fn (*u8) usize { comptime { @@ -13,5 +13,5 @@ fn get_foo() fn (*u8) usize { test "define a function in an anonymous struct in comptime" { const foo = get_foo(); - assert(foo(@intToPtr(*u8, 12345)) == 12345); + assertOrPanic(foo(@intToPtr(*u8, 12345)) == 12345); } diff --git a/test/cases/for.zig b/test/stage1/behavior/for.zig similarity index 89% rename from test/cases/for.zig rename to test/stage1/behavior/for.zig index aecd8b9a0784..b6d1ef24c42f 100644 --- a/test/cases/for.zig +++ b/test/stage1/behavior/for.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; test "continue in for loop" { @@ -26,7 +26,7 @@ test "for loop with pointer elem var" { var target: [source.len]u8 = undefined; mem.copy(u8, target[0..], source); mangleString(target[0..]); - assert(mem.eql(u8, target, "bcdefgh")); + assertOrPanic(mem.eql(u8, target, "bcdefgh")); } fn mangleString(s: []u8) void { for (s) |*c| { @@ -68,7 +68,7 @@ test "basic for loop" { buf_index += 1; } - assert(mem.eql(u8, buffer[0..buf_index], expected_result)); + assertOrPanic(mem.eql(u8, buffer[0..buf_index], expected_result)); } test "break from outer for loop" { @@ -85,7 +85,7 @@ fn testBreakOuter() void { break :outer; } } - assert(count == 1); + assertOrPanic(count == 1); } test "continue outer for loop" { @@ -102,5 +102,5 @@ fn testContinueOuter() void { continue :outer; } } - assert(counter == array.len); + assertOrPanic(counter == array.len); } diff --git a/test/cases/generics.zig b/test/stage1/behavior/generics.zig similarity index 68% rename from test/cases/generics.zig rename to test/stage1/behavior/generics.zig index 52aa013989e1..a0928634a7c3 100644 --- a/test/cases/generics.zig +++ b/test/stage1/behavior/generics.zig @@ -1,9 +1,9 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "simple generic fn" { - assert(max(i32, 3, -1) == 3); - assert(max(f32, 0.123, 0.456) == 0.456); - assert(add(2, 3) == 5); + 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 { @@ -16,7 +16,7 @@ fn add(comptime a: i32, b: i32) i32 { const the_max = max(u32, 1234, 5678); test "compile time generic eval" { - assert(the_max == 5678); + assertOrPanic(the_max == 5678); } fn gimmeTheBigOne(a: u32, b: u32) u32 { @@ -32,19 +32,19 @@ fn sameButWithFloats(a: f64, b: f64) f64 { } test "fn with comptime args" { - assert(gimmeTheBigOne(1234, 5678) == 5678); - assert(shouldCallSameInstance(34, 12) == 34); - assert(sameButWithFloats(0.43, 0.49) == 0.49); + assertOrPanic(gimmeTheBigOne(1234, 5678) == 5678); + assertOrPanic(shouldCallSameInstance(34, 12) == 34); + assertOrPanic(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); + assertOrPanic(max_i32(12, 34) == 34); + assertOrPanic(max_f64(1.2, 3.4) == 3.4); } comptime { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); + 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) { @@ -76,8 +76,8 @@ test "function with return type type" { var list2: List(i32) = undefined; list.length = 10; list2.length = 10; - assert(list.prealloc_items.len == 8); - assert(list2.prealloc_items.len == 8); + assertOrPanic(list.prealloc_items.len == 8); + assertOrPanic(list2.prealloc_items.len == 8); } test "generic struct" { @@ -89,9 +89,9 @@ test "generic struct" { .value = true, .next = null, }; - assert(a1.value == 13); - assert(a1.value == a1.getVal()); - assert(b1.getVal()); + assertOrPanic(a1.value == 13); + assertOrPanic(a1.value == a1.getVal()); + assertOrPanic(b1.getVal()); } fn GenNode(comptime T: type) type { return struct { @@ -104,7 +104,7 @@ fn GenNode(comptime T: type) type { } test "const decls in struct" { - assert(GenericDataThing(3).count_plus_one == 4); + assertOrPanic(GenericDataThing(3).count_plus_one == 4); } fn GenericDataThing(comptime count: isize) type { return struct { @@ -113,15 +113,15 @@ fn GenericDataThing(comptime count: isize) type { } test "use generic param in generic param" { - assert(aGenericFn(i32, 3, 4) == 7); + 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" { - assert(getFirstByte(u8, []u8{13}) == 13); - assert(getFirstByte(u16, []u16{ + assertOrPanic(getFirstByte(u8, []u8{13}) == 13); + assertOrPanic(getFirstByte(u16, []u16{ 0, 13, }) == 0); @@ -146,6 +146,6 @@ fn foo2(arg: var) bool { } test "array of generic fns" { - assert(foos[0](true)); - assert(!foos[1](true)); + assertOrPanic(foos[0](true)); + assertOrPanic(!foos[1](true)); } diff --git a/test/cases/if.zig b/test/stage1/behavior/if.zig similarity index 85% rename from test/cases/if.zig rename to test/stage1/behavior/if.zig index 808936bfa5fc..58d1b8fd734f 100644 --- a/test/cases/if.zig +++ b/test/stage1/behavior/if.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "if statements" { shouldBeEqual(1, 1); @@ -24,7 +24,7 @@ fn firstEqlThird(a: i32, b: i32, c: i32) void { } test "else if expression" { - assert(elseIfExpressionF(1) == 1); + assertOrPanic(elseIfExpressionF(1) == 1); } fn elseIfExpressionF(c: u8) u8 { if (c == 0) { diff --git a/test/stage1/behavior/import.zig b/test/stage1/behavior/import.zig new file mode 100644 index 000000000000..736e4c219ddc --- /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/cases/import/a_namespace.zig b/test/stage1/behavior/import/a_namespace.zig similarity index 100% rename from test/cases/import/a_namespace.zig rename to test/stage1/behavior/import/a_namespace.zig diff --git a/test/cases/incomplete_struct_param_tld.zig b/test/stage1/behavior/incomplete_struct_param_tld.zig similarity index 78% rename from test/cases/incomplete_struct_param_tld.zig rename to test/stage1/behavior/incomplete_struct_param_tld.zig index f1ac03a29279..d062311b2e9f 100644 --- a/test/cases/incomplete_struct_param_tld.zig +++ b/test/stage1/behavior/incomplete_struct_param_tld.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const A = struct { b: B, @@ -26,5 +26,5 @@ test "incomplete struct param top level declaration" { .c = C{ .x = 13 }, }, }; - assert(foo(a) == 13); + assertOrPanic(foo(a) == 13); } diff --git a/test/cases/inttoptr.zig b/test/stage1/behavior/inttoptr.zig similarity index 99% rename from test/cases/inttoptr.zig rename to test/stage1/behavior/inttoptr.zig index ba3cc52f0905..bf657fc86a9c 100644 --- a/test/cases/inttoptr.zig +++ b/test/stage1/behavior/inttoptr.zig @@ -24,4 +24,3 @@ fn forceCompilerAnalyzeBranchHardCodedPtrDereference(x: bool) void { return; } } - diff --git a/test/cases/ir_block_deps.zig b/test/stage1/behavior/ir_block_deps.zig similarity index 64% rename from test/cases/ir_block_deps.zig rename to test/stage1/behavior/ir_block_deps.zig index 5c1b18c00e45..bc61e11df7a2 100644 --- a/test/cases/ir_block_deps.zig +++ b/test/stage1/behavior/ir_block_deps.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn foo(id: u64) !i32 { return switch (id) { @@ -16,6 +16,6 @@ fn getErrInt() anyerror!i32 { } test "ir block deps" { - assert((foo(1) catch unreachable) == 0); - assert((foo(2) catch unreachable) == 0); + assertOrPanic((foo(1) catch unreachable) == 0); + assertOrPanic((foo(2) catch unreachable) == 0); } diff --git a/test/cases/math.zig b/test/stage1/behavior/math.zig similarity index 56% rename from test/cases/math.zig rename to test/stage1/behavior/math.zig index 7d6b1bd9acbc..9d6a5a499799 100644 --- a/test/cases/math.zig +++ b/test/stage1/behavior/math.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const maxInt = std.math.maxInt; const minInt = std.math.minInt; @@ -8,57 +8,57 @@ test "division" { 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); + 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 { - assert( + assertOrPanic( 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, ); - assert( + assertOrPanic( @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, ); - assert( + assertOrPanic( 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, ); - assert( + assertOrPanic( @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, ); - assert( + assertOrPanic( @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, ); - assert( + assertOrPanic( @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, ); - assert( + assertOrPanic( 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, ); } @@ -78,9 +78,9 @@ fn divTrunc(comptime T: type, a: T, b: T) T { test "@addWithOverflow" { var result: u8 = undefined; - assert(@addWithOverflow(u8, 250, 100, &result)); - assert(!@addWithOverflow(u8, 100, 150, &result)); - assert(result == 250); + assertOrPanic(@addWithOverflow(u8, 250, 100, &result)); + assertOrPanic(!@addWithOverflow(u8, 100, 150, &result)); + assertOrPanic(result == 250); } // TODO test mulWithOverflow @@ -88,9 +88,9 @@ test "@addWithOverflow" { test "@shlWithOverflow" { var result: u16 = undefined; - assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); - assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); - assert(result == 0b1011111111111100); + assertOrPanic(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); + assertOrPanic(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); + assertOrPanic(result == 0b1011111111111100); } test "@clz" { @@ -99,11 +99,11 @@ test "@clz" { } 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); + 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 { @@ -116,9 +116,9 @@ test "@ctz" { } fn testCtz() void { - assert(ctz(u8(0b10100000)) == 5); - assert(ctz(u8(0b10001010)) == 1); - assert(ctz(u8(0b00000000)) == 8); + assertOrPanic(ctz(u8(0b10100000)) == 5); + assertOrPanic(ctz(u8(0b10001010)) == 1); + assertOrPanic(ctz(u8(0b00000000)) == 8); } fn ctz(x: var) usize { @@ -128,27 +128,27 @@ fn ctz(x: var) usize { test "assignment operators" { var i: u32 = 0; i += 5; - assert(i == 5); + assertOrPanic(i == 5); i -= 2; - assert(i == 3); + assertOrPanic(i == 3); i *= 20; - assert(i == 60); + assertOrPanic(i == 60); i /= 3; - assert(i == 20); + assertOrPanic(i == 20); i %= 11; - assert(i == 9); + assertOrPanic(i == 9); i <<= 1; - assert(i == 18); + assertOrPanic(i == 18); i >>= 2; - assert(i == 4); + assertOrPanic(i == 4); i = 6; i &= 5; - assert(i == 4); + assertOrPanic(i == 4); i ^= 6; - assert(i == 2); + assertOrPanic(i == 2); i = 6; i |= 3; - assert(i == 7); + assertOrPanic(i == 7); } test "three expr in a row" { @@ -170,14 +170,14 @@ fn testThreeExprInARow(f: bool, t: bool) void { assertFalse(i32(7) != --(i32(7))); } fn assertFalse(b: bool) void { - assert(!b); + assertOrPanic(!b); } test "const number literal" { const one = 1; const eleven = ten + one; - assert(eleven == 11); + assertOrPanic(eleven == 11); } const ten = 10; @@ -187,9 +187,9 @@ test "unsigned wrapping" { } fn testUnsignedWrappingEval(x: u32) void { const zero = x +% 1; - assert(zero == 0); + assertOrPanic(zero == 0); const orig = zero -% 1; - assert(orig == maxInt(u32)); + assertOrPanic(orig == maxInt(u32)); } test "signed wrapping" { @@ -198,9 +198,9 @@ test "signed wrapping" { } fn testSignedWrappingEval(x: i32) void { const min_val = x +% 1; - assert(min_val == minInt(i32)); + assertOrPanic(min_val == minInt(i32)); const max_val = min_val -% 1; - assert(max_val == maxInt(i32)); + assertOrPanic(max_val == maxInt(i32)); } test "negation wrapping" { @@ -208,9 +208,9 @@ test "negation wrapping" { comptime testNegationWrappingEval(minInt(i16)); } fn testNegationWrappingEval(x: i16) void { - assert(x == -32768); + assertOrPanic(x == -32768); const neg = -%x; - assert(neg == -32768); + assertOrPanic(neg == -32768); } test "unsigned 64-bit division" { @@ -219,8 +219,8 @@ test "unsigned 64-bit division" { } fn test_u64_div() void { const result = divWithResult(1152921504606846976, 34359738365); - assert(result.quotient == 33554432); - assert(result.remainder == 100663296); + assertOrPanic(result.quotient == 33554432); + assertOrPanic(result.remainder == 100663296); } fn divWithResult(a: u64, b: u64) DivResult { return DivResult{ @@ -234,36 +234,36 @@ const DivResult = struct { }; test "binary not" { - assert(comptime x: { + assertOrPanic(comptime x: { break :x ~u16(0b1010101010101010) == 0b0101010101010101; }); - assert(comptime x: { + assertOrPanic(comptime x: { break :x ~u64(2147483647) == 18446744071562067968; }); testBinaryNot(0b1010101010101010); } fn testBinaryNot(x: u16) void { - assert(~x == 0b0101010101010101); + assertOrPanic(~x == 0b0101010101010101); } test "small int addition" { var x: @IntType(false, 2) = 0; - assert(x == 0); + assertOrPanic(x == 0); x += 1; - assert(x == 1); + assertOrPanic(x == 1); x += 1; - assert(x == 2); + assertOrPanic(x == 2); x += 1; - assert(x == 3); + assertOrPanic(x == 3); var result: @typeOf(x) = 3; - assert(@addWithOverflow(@typeOf(x), x, 1, &result)); + assertOrPanic(@addWithOverflow(@typeOf(x), x, 1, &result)); - assert(result == 0); + assertOrPanic(result == 0); } test "float equality" { @@ -276,20 +276,20 @@ test "float equality" { fn testFloatEqualityImpl(x: f64, y: f64) void { const y2 = x + 1.0; - assert(y == y2); + assertOrPanic(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); + assertOrPanic(5 / 3 == 1); + assertOrPanic(-5 / -3 == 1); + assertOrPanic(-6 / 3 == -2); - assert(5 % 3 == 2); - assert(-6 % 3 == 0); + assertOrPanic(5 % 3 == 2); + assertOrPanic(-6 % 3 == 0); } test "hex float literal parsing" { - comptime assert(0x1.0 == 1.0); + comptime assertOrPanic(0x1.0 == 1.0); } test "quad hex float literal parsing in range" { @@ -304,7 +304,7 @@ test "quad hex float literal parsing accurate" { // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. const expected: u128 = 0x3fff1111222233334444555566667777; - assert(@bitCast(u128, a) == expected); + assertOrPanic(@bitCast(u128, a) == expected); } test "hex float literal within range" { @@ -319,7 +319,7 @@ test "truncating shift left" { } fn testShlTrunc(x: u16) void { const shifted = x << 1; - assert(shifted == 65534); + assertOrPanic(shifted == 65534); } test "truncating shift right" { @@ -328,7 +328,7 @@ test "truncating shift right" { } fn testShrTrunc(x: u16) void { const shifted = x >> 1; - assert(shifted == 32767); + assertOrPanic(shifted == 32767); } test "exact shift left" { @@ -337,7 +337,7 @@ test "exact shift left" { } fn testShlExact(x: u8) void { const shifted = @shlExact(x, 2); - assert(shifted == 0b11010100); + assertOrPanic(shifted == 0b11010100); } test "exact shift right" { @@ -346,22 +346,22 @@ test "exact shift right" { } fn testShrExact(x: u8) void { const shifted = @shrExact(x, 2); - assert(shifted == 0b00101101); + assertOrPanic(shifted == 0b00101101); } test "comptime_int addition" { comptime { - assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); - assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); + assertOrPanic(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); + assertOrPanic(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); } } test "comptime_int multiplication" { comptime { - assert( + assertOrPanic( 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, ); - assert( + assertOrPanic( 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, ); } @@ -369,7 +369,7 @@ test "comptime_int multiplication" { test "comptime_int shifting" { comptime { - assert((u128(1) << 127) == 0x80000000000000000000000000000000); + assertOrPanic((u128(1) << 127) == 0x80000000000000000000000000000000); } } @@ -377,16 +377,16 @@ test "comptime_int multi-limb shift and mask" { comptime { var a = 0xefffffffa0000001eeeeeeefaaaaaaab; - assert(u32(a & 0xffffffff) == 0xaaaaaaab); + assertOrPanic(u32(a & 0xffffffff) == 0xaaaaaaab); a >>= 32; - assert(u32(a & 0xffffffff) == 0xeeeeeeef); + assertOrPanic(u32(a & 0xffffffff) == 0xeeeeeeef); a >>= 32; - assert(u32(a & 0xffffffff) == 0xa0000001); + assertOrPanic(u32(a & 0xffffffff) == 0xa0000001); a >>= 32; - assert(u32(a & 0xffffffff) == 0xefffffff); + assertOrPanic(u32(a & 0xffffffff) == 0xefffffff); a >>= 32; - assert(a == 0); + assertOrPanic(a == 0); } } @@ -394,7 +394,7 @@ test "comptime_int multi-limb partial shift right" { comptime { var a = 0x1ffffffffeeeeeeee; a >>= 16; - assert(a == 0x1ffffffffeeee); + assertOrPanic(a == 0x1ffffffffeeee); } } @@ -404,23 +404,23 @@ 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); + 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 { - 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); + 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); } } @@ -434,23 +434,23 @@ fn make_f128(x: f128) f128 { } 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); + 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 { - assert(x != 0.0); + assertOrPanic(x != 0.0); } test "comptime float rem int" { comptime { var x = f32(1) % 2; - assert(x == 1.0); + assertOrPanic(x == 1.0); } } @@ -465,8 +465,8 @@ test "remainder division" { } fn remdiv(comptime T: type) void { - assert(T(1) == T(1) % T(2)); - assert(T(1) == T(7) % T(3)); + assertOrPanic(T(1) == T(1) % T(2)); + assertOrPanic(T(1) == T(7) % T(3)); } test "@sqrt" { @@ -480,19 +480,19 @@ test "@sqrt" { const x = 14.0; const y = x * x; const z = @sqrt(@typeOf(y), y); - comptime assert(z == x); + comptime assertOrPanic(z == x); } fn testSqrt(comptime T: type, x: T) void { - assert(@sqrt(T, x * x) == x); + assertOrPanic(@sqrt(T, x * x) == x); } test "comptime_int param and return" { const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); - assert(a == 137114567242441932203689521744947848950); + assertOrPanic(a == 137114567242441932203689521744947848950); const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); - assert(b == 985095453608931032642182098849559179469148836107390954364380); + assertOrPanic(b == 985095453608931032642182098849559179469148836107390954364380); } fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { diff --git a/test/cases/merge_error_sets.zig b/test/stage1/behavior/merge_error_sets.zig similarity index 100% rename from test/cases/merge_error_sets.zig rename to test/stage1/behavior/merge_error_sets.zig diff --git a/test/cases/misc.zig b/test/stage1/behavior/misc.zig similarity index 61% rename from test/cases/misc.zig rename to test/stage1/behavior/misc.zig index 1a34d54e9e15..8d2555ddddb3 100644 --- a/test/cases/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const cstr = std.cstr; const builtin = @import("builtin"); @@ -26,38 +26,38 @@ test "call disabled extern fn" { } 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); + 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" { - assert(f16.bit_count == 16); - assert(f32.bit_count == 32); - assert(f64.bit_count == 64); + assertOrPanic(f16.bit_count == 16); + assertOrPanic(f32.bit_count == 32); + assertOrPanic(f64.bit_count == 64); } test "short circuit" { @@ -72,7 +72,7 @@ fn testShortCircuit(f: bool, t: bool) void { var hit_4 = f; if (t or x: { - assert(f); + assertOrPanic(f); break :x f; }) { hit_1 = t; @@ -81,31 +81,31 @@ fn testShortCircuit(f: bool, t: bool) void { hit_2 = t; break :x f; }) { - assert(f); + assertOrPanic(f); } if (t and x: { hit_3 = t; break :x f; }) { - assert(f); + assertOrPanic(f); } if (f and x: { - assert(f); + assertOrPanic(f); break :x f; }) { - assert(f); + assertOrPanic(f); } else { hit_4 = t; } - assert(hit_1); - assert(hit_2); - assert(hit_3); - assert(hit_4); + assertOrPanic(hit_1); + assertOrPanic(hit_2); + assertOrPanic(hit_3); + assertOrPanic(hit_4); } test "truncate" { - assert(testTruncate(0x10fd) == 0xfd); + assertOrPanic(testTruncate(0x10fd) == 0xfd); } fn testTruncate(x: u32) u8 { return @truncate(u8, x); @@ -116,16 +116,16 @@ fn first4KeysOfHomeRow() []const u8 { } test "return string from function" { - assert(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); + assertOrPanic(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); } const g1: i32 = 1233 + 1; var g2: i32 = 0; test "global variables" { - assert(g2 == 0); + assertOrPanic(g2 == 0); g2 = g1; - assert(g2 == 1234); + assertOrPanic(g2 == 1234); } test "memcpy and memset intrinsics" { @@ -142,7 +142,7 @@ test "builtin static eval" { const x: i32 = comptime x: { break :x 1 + 2 + 3; }; - assert(x == comptime 6); + assertOrPanic(x == comptime 6); } test "slicing" { @@ -163,7 +163,7 @@ test "slicing" { test "constant equal function pointers" { const alias = emptyFn; - assert(comptime x: { + assertOrPanic(comptime x: { break :x emptyFn == alias; }); } @@ -171,25 +171,25 @@ test "constant equal function pointers" { fn emptyFn() void {} test "hex escape" { - assert(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); + assertOrPanic(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } test "string concatenation" { - assert(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); + assertOrPanic(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); } test "array mult operator" { - assert(mem.eql(u8, "ab" ** 5, "ababababab")); + assertOrPanic(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")); + 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" { @@ -199,7 +199,7 @@ test "multiline string" { \\three ; const s2 = "one\ntwo)\nthree"; - assert(mem.eql(u8, s1, s2)); + assertOrPanic(mem.eql(u8, s1, s2)); } test "multiline C string" { @@ -209,11 +209,11 @@ test "multiline C string" { c\\three ; const s2 = c"one\ntwo)\nthree"; - assert(cstr.cmp(s1, s2) == 0); + assertOrPanic(cstr.cmp(s1, s2) == 0); } test "type equality" { - assert(*const u8 != *u8); + assertOrPanic(*const u8 != *u8); } const global_a: i32 = 1234; @@ -221,7 +221,7 @@ 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); + assertOrPanic(d.* == 1234); } test "explicit cast maybe pointers" { @@ -247,8 +247,8 @@ test "cast undefined" { fn testCastUndefined(x: []const u8) void {} test "cast small unsigned to larger signed" { - assert(castSmallUnsignedToLargerSigned1(200) == i16(200)); - assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); + assertOrPanic(castSmallUnsignedToLargerSigned1(200) == i16(200)); + assertOrPanic(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); } fn castSmallUnsignedToLargerSigned1(x: u8) i16 { return x; @@ -258,7 +258,7 @@ fn castSmallUnsignedToLargerSigned2(x: u16) i64 { } test "implicit cast after unreachable" { - assert(outer() == 1234); + assertOrPanic(outer() == 1234); } fn inner() i32 { return 1234; @@ -273,13 +273,13 @@ test "pointer dereferencing" { y.* += 1; - assert(x == 4); - assert(y.* == 4); + assertOrPanic(x == 4); + assertOrPanic(y.* == 4); } test "call result of if else expression" { - assert(mem.eql(u8, f2(true), "a")); - assert(mem.eql(u8, f2(false), "b")); + 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)(); @@ -321,8 +321,8 @@ 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); + assertOrPanic(pt.x == 3); + assertOrPanic(pt.y == 4); }, else => unreachable, } @@ -330,14 +330,14 @@ fn test3_1(f: Test3Foo) void { fn test3_2(f: Test3Foo) void { switch (f) { Test3Foo.Two => |x| { - assert(x == 13); + assertOrPanic(x == 13); }, else => unreachable, } } test "character literals" { - assert('\'' == single_quote); + assertOrPanic('\'' == single_quote); } const single_quote = '\''; @@ -346,13 +346,13 @@ test "take address of parameter" { } fn testTakeAddressOfParameter(f: f32) void { const f_ptr = &f; - assert(f_ptr.* == 12.34); + assertOrPanic(f_ptr.* == 12.34); } test "pointer comparison" { const a = ([]const u8)("a"); const b = &a; - assert(ptrEql(b, b)); + assertOrPanic(ptrEql(b, b)); } fn ptrEql(a: *const []const u8, b: *const []const u8) bool { return a == b; @@ -367,36 +367,31 @@ test "C string concatenation" { { var i: u32 = 0; while (i < len_with_null) : (i += 1) { - assert(a[i] == b[i]); + assertOrPanic(a[i] == b[i]); } } - assert(a[len] == 0); - assert(b[len] == 0); + assertOrPanic(a[len] == 0); + assertOrPanic(b[len] == 0); } test "cast slice to u8 slice" { - assert(@sizeOf(i32) == 4); - var big_thing_array = []i32{ - 1, - 2, - 3, - 4, - }; + 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); - assert(bytes.len == 4 * 4); + assertOrPanic(bytes.len == 4 * 4); bytes[4] = 0; bytes[5] = 0; bytes[6] = 0; bytes[7] = 0; - assert(big_thing_slice[1] == 0); + assertOrPanic(big_thing_slice[1] == 0); const big_thing_again = @bytesToSlice(i32, bytes); - assert(big_thing_again[2] == 3); + assertOrPanic(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)); + 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" { @@ -413,7 +408,7 @@ fn testPointerToVoidReturnType2() *const void { test "non const ptr to aliased type" { const int = i32; - assert(?*int == ?*i32); + assertOrPanic(?*int == ?*i32); } test "array 2D const double ptr" { @@ -426,8 +421,8 @@ test "array 2D const double ptr" { fn testArray2DConstDoublePtr(ptr: *const f32) void { const ptr2 = @ptrCast([*]const f32, ptr); - assert(ptr2[0] == 1.0); - assert(ptr2[1] == 2.0); + assertOrPanic(ptr2[0] == 1.0); + assertOrPanic(ptr2[1] == 2.0); } const Tid = builtin.TypeId; @@ -449,32 +444,32 @@ const AUnion = union { 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); + 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 @@ -490,13 +485,13 @@ test "@typeName" { Unused, }; comptime { - assert(mem.eql(u8, @typeName(i64), "i64")); - assert(mem.eql(u8, @typeName(*usize), "*usize")); + assertOrPanic(mem.eql(u8, @typeName(i64), "i64")); + assertOrPanic(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")); + 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")); } } @@ -504,28 +499,16 @@ 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 "double implicit cast in same expression" { + var x = i32(u16(nine())); + assertOrPanic(x == 9); } - -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); - } +fn nine() u8 { + return 9; } test "global variable initialized to global variable array element" { - assert(global_ptr == &gdt[0]); + assertOrPanic(global_ptr == &gdt[0]); } const GDTEntry = struct { field: i32, @@ -543,16 +526,12 @@ 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")); + 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" { @@ -581,25 +560,6 @@ test "implicit comptime while" { } } -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 { @@ -611,7 +571,7 @@ fn fnThatClosesOverLocalConst() type { test "function closes over local const" { const x = fnThatClosesOverLocalConst().g(); - assert(x == 1); + assertOrPanic(x == 1); } test "cold function" { @@ -648,21 +608,21 @@ export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: Pack 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{})); + 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 assert(addr1 == addr2); + comptime assertOrPanic(addr1 == addr2); } test "equality compare fn ptrs" { var a = emptyFn; - assert(a == a); + assertOrPanic(a == a); } test "self reference through fn ptr field" { @@ -677,5 +637,51 @@ test "self reference through fn ptr field" { }; var a: S.A = undefined; a.f = S.foo; - assert(a.f(a) == 12); + 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/cases/namespace_depends_on_compile_var/a.zig b/test/stage1/behavior/namespace_depends_on_compile_var/a.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/a.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/a.zig diff --git a/test/cases/namespace_depends_on_compile_var/b.zig b/test/stage1/behavior/namespace_depends_on_compile_var/b.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/b.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/b.zig diff --git a/test/cases/namespace_depends_on_compile_var/index.zig b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig similarity index 62% rename from test/cases/namespace_depends_on_compile_var/index.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/index.zig index ccc49d9367d8..fe3e0cc02093 100644 --- a/test/cases/namespace_depends_on_compile_var/index.zig +++ b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig @@ -1,11 +1,11 @@ const builtin = @import("builtin"); -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "namespace depends on compile var" { if (some_namespace.a_bool) { - assert(some_namespace.a_bool); + assertOrPanic(some_namespace.a_bool); } else { - assert(!some_namespace.a_bool); + assertOrPanic(!some_namespace.a_bool); } } const some_namespace = switch (builtin.os) { diff --git a/test/cases/new_stack_call.zig b/test/stage1/behavior/new_stack_call.zig similarity index 72% rename from test/cases/new_stack_call.zig rename to test/stage1/behavior/new_stack_call.zig index 5912550d545a..b9ae2d27cdb7 100644 --- a/test/cases/new_stack_call.zig +++ b/test/stage1/behavior/new_stack_call.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; var new_stack_bytes: [1024]u8 = undefined; @@ -10,17 +10,17 @@ test "calling a function with a new stack" { const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); _ = targetFunction(arg); - assert(arg == 1234); - assert(a < b); + assertOrPanic(arg == 1234); + assertOrPanic(a < b); } fn targetFunction(x: i32) usize { - assert(x == 1234); + assertOrPanic(x == 1234); var local_variable: i32 = 42; const ptr = &local_variable; ptr.* += 1; - assert(local_variable == 43); + assertOrPanic(local_variable == 43); return @ptrToInt(ptr); } diff --git a/test/cases/null.zig b/test/stage1/behavior/null.zig similarity index 80% rename from test/cases/null.zig rename to test/stage1/behavior/null.zig index 825db88b1e58..e2f86a05ba44 100644 --- a/test/cases/null.zig +++ b/test/stage1/behavior/null.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "optional type" { const x: ?bool = true; @@ -17,13 +17,13 @@ test "optional type" { const z = next_x orelse 1234; - assert(z == 1234); + assertOrPanic(z == 1234); const final_x: ?i32 = 13; const num = final_x orelse unreachable; - assert(num == 13); + assertOrPanic(num == 13); } test "test maybe object and get a pointer to the inner value" { @@ -33,7 +33,7 @@ test "test maybe object and get a pointer to the inner value" { b.* = false; } - assert(maybe_bool.? == false); + assertOrPanic(maybe_bool.? == false); } test "rhs maybe unwrap return" { @@ -47,9 +47,9 @@ test "maybe return" { } fn maybeReturnImpl() void { - assert(foo(1235).?); + assertOrPanic(foo(1235).?); if (foo(null) != null) unreachable; - assert(!foo(1234).?); + assertOrPanic(!foo(1234).?); } fn foo(x: ?i32) ?bool { @@ -58,7 +58,7 @@ fn foo(x: ?i32) ?bool { } test "if var maybe pointer" { - assert(shouldBeAPlus1(Particle{ + assertOrPanic(shouldBeAPlus1(Particle{ .a = 14, .b = 1, .c = 1, @@ -84,10 +84,10 @@ const Particle = struct { test "null literal outside function" { const is_null = here_is_a_null_literal.context == null; - assert(is_null); + assertOrPanic(is_null); const is_non_null = here_is_a_null_literal.context != null; - assert(!is_non_null); + assertOrPanic(!is_non_null); } const SillyStruct = struct { context: ?i32, @@ -98,8 +98,8 @@ test "test null runtime" { testTestNullRuntime(null); } fn testTestNullRuntime(x: ?i32) void { - assert(x == null); - assert(!(x != null)); + assertOrPanic(x == null); + assertOrPanic(!(x != null)); } test "optional void" { @@ -108,8 +108,8 @@ test "optional void" { } fn optionalVoidImpl() void { - assert(bar(null) == null); - assert(bar({}) != null); + assertOrPanic(bar(null) == null); + assertOrPanic(bar({}) != null); } fn bar(x: ?void) ?void { @@ -133,7 +133,7 @@ test "unwrap optional which is field of global var" { } struct_with_optional.field = 1234; if (struct_with_optional.field) |payload| { - assert(payload == 1234); + assertOrPanic(payload == 1234); } else { unreachable; } @@ -141,13 +141,13 @@ test "unwrap optional which is field of global var" { test "null with default unwrap" { const x: i32 = null orelse 1; - assert(x == 1); + assertOrPanic(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); + assertOrPanic(opt_type_struct.t != null and opt_type_struct.t.? == u8); } } @@ -158,5 +158,5 @@ const StructWithOptionalType = struct { test "optional pointer to 0 bit type null value at runtime" { const EmptyStruct = struct {}; var x: ?*EmptyStruct = null; - assert(x == null); + assertOrPanic(x == null); } diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig new file mode 100644 index 000000000000..a3db580e4a22 --- /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 uncomment + //comptime S.entry(); +} diff --git a/test/cases/pointers.zig b/test/stage1/behavior/pointers.zig similarity index 50% rename from test/cases/pointers.zig rename to test/stage1/behavior/pointers.zig index 47afb60a2efd..1142d89ab557 100644 --- a/test/cases/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "dereference pointer" { comptime testDerefPtr(); @@ -10,33 +10,33 @@ fn testDerefPtr() void { var x: i32 = 1234; var y = &x; y.* += 1; - assert(x == 1235); + assertOrPanic(x == 1235); } test "pointer arithmetic" { var ptr = c"abcd"; - assert(ptr[0] == 'a'); + assertOrPanic(ptr[0] == 'a'); ptr += 1; - assert(ptr[0] == 'b'); + assertOrPanic(ptr[0] == 'b'); ptr += 1; - assert(ptr[0] == 'c'); + assertOrPanic(ptr[0] == 'c'); ptr += 1; - assert(ptr[0] == 'd'); + assertOrPanic(ptr[0] == 'd'); ptr += 1; - assert(ptr[0] == 0); + assertOrPanic(ptr[0] == 0); ptr -= 1; - assert(ptr[0] == 'd'); + assertOrPanic(ptr[0] == 'd'); ptr -= 1; - assert(ptr[0] == 'c'); + assertOrPanic(ptr[0] == 'c'); ptr -= 1; - assert(ptr[0] == 'b'); + assertOrPanic(ptr[0] == 'b'); ptr -= 1; - assert(ptr[0] == 'a'); + assertOrPanic(ptr[0] == 'a'); } test "double pointer parsing" { - comptime assert(PtrOf(PtrOf(i32)) == **i32); + comptime assertOrPanic(PtrOf(PtrOf(i32)) == **i32); } fn PtrOf(comptime T: type) type { diff --git a/test/stage1/behavior/popcount.zig b/test/stage1/behavior/popcount.zig new file mode 100644 index 000000000000..f7f8bb523bf4 --- /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/cases/ptrcast.zig b/test/stage1/behavior/ptrcast.zig similarity index 100% rename from test/cases/ptrcast.zig rename to test/stage1/behavior/ptrcast.zig diff --git a/test/cases/pub_enum/index.zig b/test/stage1/behavior/pub_enum/index.zig similarity index 54% rename from test/cases/pub_enum/index.zig rename to test/stage1/behavior/pub_enum/index.zig index 7fdd07b8a384..181113f6bf19 100644 --- a/test/cases/pub_enum/index.zig +++ b/test/stage1/behavior/pub_enum/index.zig @@ -1,13 +1,13 @@ const other = @import("other.zig"); -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "pub enum" { pubEnumTest(other.APubEnum.Two); } fn pubEnumTest(foo: other.APubEnum) void { - assert(foo == other.APubEnum.Two); + assertOrPanic(foo == other.APubEnum.Two); } test "cast with imported symbol" { - assert(other.size_t(42) == 42); + assertOrPanic(other.size_t(42) == 42); } diff --git a/test/cases/pub_enum/other.zig b/test/stage1/behavior/pub_enum/other.zig similarity index 100% rename from test/cases/pub_enum/other.zig rename to test/stage1/behavior/pub_enum/other.zig diff --git a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig similarity index 77% rename from test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig rename to test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig index 3c94bb0d49bf..acbe6b245978 100644 --- a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig +++ b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig @@ -1,14 +1,14 @@ -const assert = @import("std").debug.assert; +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"); - assert(!ok); + assertOrPanic(!ok); foo(false, Num.One, false, "aoeu"); - assert(!ok); + assertOrPanic(!ok); foo(true, Num.One, false, "aoeu"); - assert(ok); + assertOrPanic(ok); } const Num = enum { @@ -32,6 +32,6 @@ fn foo(c: bool, k: Num, c2: bool, b: []const u8) void { } fn a(x: []const u8) void { - assert(mem.eql(u8, x, "aoeu")); + 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 000000000000..f4c142e0f755 --- /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 000000000000..ddaea4c24299 --- /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/cases/slice.zig b/test/stage1/behavior/slice.zig similarity index 62% rename from test/cases/slice.zig rename to test/stage1/behavior/slice.zig index b4b43bdd1933..cc29e43485b2 100644 --- a/test/cases/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -1,20 +1,20 @@ -const assert = @import("std").debug.assert; +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" { - assert(@ptrToInt(x.ptr) == 0x1000); - assert(x.len == 0x500); + assertOrPanic(@ptrToInt(x.ptr) == 0x1000); + assertOrPanic(x.len == 0x500); - assert(@ptrToInt(y.ptr) == 0x1100); - assert(y.len == 0x400); + assertOrPanic(@ptrToInt(y.ptr) == 0x1100); + assertOrPanic(y.len == 0x400); } test "slice child property" { var array: [5]i32 = undefined; var slice = array[0..]; - assert(@typeOf(slice).Child == i32); + assertOrPanic(@typeOf(slice).Child == i32); } test "runtime safety lets us slice from len..len" { @@ -23,7 +23,7 @@ test "runtime safety lets us slice from len..len" { 2, 3, }; - assert(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); + assertOrPanic(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); } fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { @@ -36,5 +36,5 @@ test "implicitly cast array of size 0 to slice" { } fn assertLenIsZero(msg: []const u8) void { - assert(msg.len == 0); + assertOrPanic(msg.len == 0); } diff --git a/test/cases/struct.zig b/test/stage1/behavior/struct.zig similarity index 64% rename from test/cases/struct.zig rename to test/stage1/behavior/struct.zig index bbbd21912c32..92ae2baa152d 100644 --- a/test/cases/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); const maxInt = std.math.maxInt; @@ -12,7 +12,7 @@ const empty_global_instance = StructWithNoFields{}; test "call struct static method" { const result = StructWithNoFields.add(3, 4); - assert(result == 7); + assertOrPanic(result == 7); } test "return empty struct instance" { @@ -24,8 +24,8 @@ fn returnEmptyStructInstance() StructWithNoFields { const should_be_11 = StructWithNoFields.add(5, 6); -test "invake static method in global scope" { - assert(should_be_11 == 11); +test "invoke static method in global scope" { + assertOrPanic(should_be_11 == 11); } test "void struct fields" { @@ -34,8 +34,8 @@ test "void struct fields" { .b = 1, .c = void{}, }; - assert(foo.b == 1); - assert(@sizeOf(VoidStructFieldsFoo) == 4); + assertOrPanic(foo.b == 1); + assertOrPanic(@sizeOf(VoidStructFieldsFoo) == 4); } const VoidStructFieldsFoo = struct { a: void, @@ -50,7 +50,7 @@ test "structs" { foo.b = foo.a == 1; testFoo(foo); testMutation(&foo); - assert(foo.c == 100); + assertOrPanic(foo.c == 100); } const StructFoo = struct { a: i32, @@ -58,7 +58,7 @@ const StructFoo = struct { c: f32, }; fn testFoo(foo: StructFoo) void { - assert(foo.b); + assertOrPanic(foo.b); } fn testMutation(foo: *StructFoo) void { foo.c = 100; @@ -83,7 +83,7 @@ test "struct point to self" { root.next = &node; - assert(node.next.next.next.val.x == 1); + assertOrPanic(node.next.next.next.val.x == 1); } test "struct byval assign" { @@ -92,18 +92,18 @@ test "struct byval assign" { foo1.a = 1234; foo2.a = 0; - assert(foo2.a == 0); + assertOrPanic(foo2.a == 0); foo2 = foo1; - assert(foo2.a == 1234); + assertOrPanic(foo2.a == 1234); } fn structInitializer() void { const val = Val{ .x = 42 }; - assert(val.x == 42); + assertOrPanic(val.x == 42); } test "fn call of struct field" { - assert(callStructField(Foo{ .ptr = aFunc }) == 13); + assertOrPanic(callStructField(Foo{ .ptr = aFunc }) == 13); } const Foo = struct { @@ -122,7 +122,7 @@ test "store member function in variable" { const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); - assert(result == 1234); + assertOrPanic(result == 1234); } const MemberFnTestFoo = struct { x: i32, @@ -134,12 +134,12 @@ const MemberFnTestFoo = struct { test "call member function directly" { const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); - assert(result == 1234); + assertOrPanic(result == 1234); } test "member functions" { const r = MemberFnRand{ .seed = 1234 }; - assert(r.getSeed() == 1234); + assertOrPanic(r.getSeed() == 1234); } const MemberFnRand = struct { seed: u32, @@ -150,7 +150,7 @@ const MemberFnRand = struct { test "return struct byval from function" { const bar = makeBar(1234, 5678); - assert(bar.y == 5678); + assertOrPanic(bar.y == 5678); } const Bar = struct { x: i32, @@ -165,7 +165,7 @@ fn makeBar(x: i32, y: i32) Bar { test "empty struct method call" { const es = EmptyStruct{}; - assert(es.method() == 1234); + assertOrPanic(es.method() == 1234); } const EmptyStruct = struct { fn method(es: *const EmptyStruct) i32 { @@ -182,7 +182,7 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { } test "pass slice of empty struct to fn" { - assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); + assertOrPanic(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { return slice.len; @@ -200,7 +200,7 @@ test "packed struct" { }; foo.y += 1; const four = foo.x + foo.y; - assert(four == 4); + assertOrPanic(four == 4); } const BitField1 = packed struct { @@ -217,17 +217,17 @@ const bit_field_1 = BitField1{ 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); + assertOrPanic(getA(&data) == 1); + assertOrPanic(getB(&data) == 2); + assertOrPanic(getC(&data) == 3); + comptime assertOrPanic(@sizeOf(BitField1) == 1); data.b += 1; - assert(data.b == 3); + assertOrPanic(data.b == 3); data.a += 1; - assert(data.a == 2); - assert(data.b == 3); + assertOrPanic(data.a == 2); + assertOrPanic(data.b == 3); } fn getA(data: *const BitField1) u3 { @@ -254,8 +254,8 @@ const Foo96Bits = packed struct { test "packed struct 24bits" { comptime { - assert(@sizeOf(Foo24Bits) == 3); - assert(@sizeOf(Foo96Bits) == 12); + assertOrPanic(@sizeOf(Foo24Bits) == 3); + assertOrPanic(@sizeOf(Foo96Bits) == 12); } var value = Foo96Bits{ @@ -265,28 +265,28 @@ test "packed struct 24bits" { .d = 0, }; value.a += 1; - assert(value.a == 1); - assert(value.b == 0); - assert(value.c == 0); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 0); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); value.b += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 0); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); value.c += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 0); value.d += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 1); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 1); } const FooArray24Bits = packed struct { @@ -297,43 +297,43 @@ const FooArray24Bits = packed struct { test "packed array 24bits" { comptime { - assert(@sizeOf([9]Foo24Bits) == 9 * 3); - assert(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); + 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]; - assert(ptr.a == 0); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 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); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + 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); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + 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); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == 0); + 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); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(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)); - assert(bytes[bytes.len - 1] == 0xaa); + assertOrPanic(bytes[bytes.len - 1] == 0xaa); } const FooStructAligned = packed struct { @@ -347,17 +347,17 @@ const FooArrayOfAligned = packed struct { test "aligned array of packed struct" { comptime { - assert(@sizeOf(FooStructAligned) == 2); - assert(@sizeOf(FooArrayOfAligned) == 2 * 2); + assertOrPanic(@sizeOf(FooStructAligned) == 2); + assertOrPanic(@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); + 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" { @@ -370,10 +370,10 @@ test "runtime struct initialization of bitfield" { .y = @intCast(u4, x2), }; - assert(s1.x == x1); - assert(s1.y == x1); - assert(s2.x == @intCast(u4, x2)); - assert(s2.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); @@ -400,18 +400,18 @@ test "native bit field understands endianness" { @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); + 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); - assert(result.len == 0); + assertOrPanic(result.len == 0); } const Expr = union(enum) { @@ -434,10 +434,10 @@ test "call method with mutable reference to struct with no fields" { }; var s = S{}; - assert(S.doC(&s)); - assert(s.doC()); - assert(S.do(&s)); - assert(s.do()); + assertOrPanic(S.doC(&s)); + assertOrPanic(s.doC()); + assertOrPanic(S.do(&s)); + assertOrPanic(s.do()); } test "implicit cast packed struct field to const ptr" { @@ -453,7 +453,7 @@ test "implicit cast packed struct field to const ptr" { var lup: LevelUpMove = undefined; lup.level = 12; const res = LevelUpMove.toInt(lup.level); - assert(res == 12); + assertOrPanic(res == 12); } test "pointer to packed struct member in a stack variable" { @@ -464,7 +464,7 @@ test "pointer to packed struct member in a stack variable" { var s = S{ .a = 2, .b = 0 }; var b_ptr = &s.b; - assert(s.b == 0); + assertOrPanic(s.b == 0); b_ptr.* = 2; - assert(s.b == 2); + assertOrPanic(s.b == 2); } diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/stage1/behavior/struct_contains_null_ptr_itself.zig similarity index 81% rename from test/cases/struct_contains_null_ptr_itself.zig rename to test/stage1/behavior/struct_contains_null_ptr_itself.zig index 21175974b3d8..4cc479f31c4a 100644 --- a/test/cases/struct_contains_null_ptr_itself.zig +++ b/test/stage1/behavior/struct_contains_null_ptr_itself.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "struct contains null pointer which contains original struct" { var x: ?*NodeLineComment = null; - assert(x == null); + assertOrPanic(x == null); } pub const Node = struct { diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/stage1/behavior/struct_contains_slice_of_itself.zig similarity index 68% rename from test/cases/struct_contains_slice_of_itself.zig rename to test/stage1/behavior/struct_contains_slice_of_itself.zig index aa3075312cc7..15780a7c4456 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/stage1/behavior/struct_contains_slice_of_itself.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Node = struct { payload: i32, @@ -39,12 +39,12 @@ test "struct contains slice of itself" { .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); + 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" { @@ -76,10 +76,10 @@ test "struct contains aligned slice of itself" { .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); + 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/cases/switch.zig b/test/stage1/behavior/switch.zig similarity index 72% rename from test/cases/switch.zig rename to test/stage1/behavior/switch.zig index 1162fdd4b2ea..4ac971397e04 100644 --- a/test/cases/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "switch with numbers" { testSwitchWithNumbers(13); @@ -10,14 +10,14 @@ fn testSwitchWithNumbers(x: u32) void { 13 => true, else => false, }; - assert(result); + assertOrPanic(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); + 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 { @@ -40,7 +40,7 @@ test "implicit comptime switch" { }; comptime { - assert(result + 1 == 14); + assertOrPanic(result + 1 == 14); } } @@ -71,7 +71,7 @@ fn nonConstSwitch(foo: SwitchStatmentFoo) void { SwitchStatmentFoo.C => 3, SwitchStatmentFoo.D => 4, }; - assert(val == 3); + assertOrPanic(val == 3); } const SwitchStatmentFoo = enum { A, @@ -93,10 +93,10 @@ const SwitchProngWithVarEnum = union(enum) { fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void { switch (a) { SwitchProngWithVarEnum.One => |x| { - assert(x == 13); + assertOrPanic(x == 13); }, SwitchProngWithVarEnum.Two => |x| { - assert(x == 13.0); + assertOrPanic(x == 13.0); }, SwitchProngWithVarEnum.Meh => |x| { const v: void = x; @@ -116,7 +116,7 @@ fn testSwitchEnumPtrCapture() void { else => unreachable, } switch (value) { - SwitchProngWithVarEnum.One => |x| assert(x == 1235), + SwitchProngWithVarEnum.One => |x| assertOrPanic(x == 1235), else => unreachable, } } @@ -127,7 +127,7 @@ test "switch with multiple expressions" { 4, 5, 6 => 2, else => i32(3), }; - assert(x == 2); + assertOrPanic(x == 2); } fn returnsFive() i32 { return 5; @@ -149,12 +149,12 @@ fn returnsFalse() bool { } } test "switch on const enum with var" { - assert(!returnsFalse()); + assertOrPanic(!returnsFalse()); } test "switch on type" { - assert(trueIfBoolFalseOtherwise(bool)); - assert(!trueIfBoolFalseOtherwise(i32)); + assertOrPanic(trueIfBoolFalseOtherwise(bool)); + assertOrPanic(!trueIfBoolFalseOtherwise(i32)); } fn trueIfBoolFalseOtherwise(comptime T: type) bool { @@ -170,16 +170,16 @@ test "switch handles all cases of number" { } fn testSwitchHandleAllCases() void { - assert(testSwitchHandleAllCasesExhaustive(0) == 3); - assert(testSwitchHandleAllCasesExhaustive(1) == 2); - assert(testSwitchHandleAllCasesExhaustive(2) == 1); - assert(testSwitchHandleAllCasesExhaustive(3) == 0); + assertOrPanic(testSwitchHandleAllCasesExhaustive(0) == 3); + assertOrPanic(testSwitchHandleAllCasesExhaustive(1) == 2); + assertOrPanic(testSwitchHandleAllCasesExhaustive(2) == 1); + assertOrPanic(testSwitchHandleAllCasesExhaustive(3) == 0); - assert(testSwitchHandleAllCasesRange(100) == 0); - assert(testSwitchHandleAllCasesRange(200) == 1); - assert(testSwitchHandleAllCasesRange(201) == 2); - assert(testSwitchHandleAllCasesRange(202) == 4); - assert(testSwitchHandleAllCasesRange(230) == 3); + 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 { @@ -207,8 +207,8 @@ test "switch all prongs unreachable" { } fn testAllProngsUnreachable() void { - assert(switchWithUnreachable(1) == 2); - assert(switchWithUnreachable(2) == 10); + assertOrPanic(switchWithUnreachable(1) == 2); + assertOrPanic(switchWithUnreachable(2) == 10); } fn switchWithUnreachable(x: i32) i32 { @@ -230,7 +230,7 @@ test "capture value of switch with all unreachable prongs" { const x = return_a_number() catch |err| switch (err) { else => unreachable, }; - assert(x == 1); + assertOrPanic(x == 1); } test "switching on booleans" { @@ -239,14 +239,14 @@ test "switching on booleans" { } fn testSwitchOnBools() void { - assert(testSwitchOnBoolsTrueAndFalse(true) == false); - assert(testSwitchOnBoolsTrueAndFalse(false) == true); + assertOrPanic(testSwitchOnBoolsTrueAndFalse(true) == false); + assertOrPanic(testSwitchOnBoolsTrueAndFalse(false) == true); - assert(testSwitchOnBoolsTrueWithElse(true) == false); - assert(testSwitchOnBoolsTrueWithElse(false) == true); + assertOrPanic(testSwitchOnBoolsTrueWithElse(true) == false); + assertOrPanic(testSwitchOnBoolsTrueWithElse(false) == true); - assert(testSwitchOnBoolsFalseWithElse(true) == false); - assert(testSwitchOnBoolsFalseWithElse(false) == true); + assertOrPanic(testSwitchOnBoolsFalseWithElse(true) == false); + assertOrPanic(testSwitchOnBoolsFalseWithElse(false) == true); } fn testSwitchOnBoolsTrueAndFalse(x: bool) bool { diff --git a/test/cases/switch_prong_err_enum.zig b/test/stage1/behavior/switch_prong_err_enum.zig similarity index 79% rename from test/cases/switch_prong_err_enum.zig rename to test/stage1/behavior/switch_prong_err_enum.zig index 89060690fc01..6ac1919f0d5a 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/stage1/behavior/switch_prong_err_enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; var read_count: u64 = 0; @@ -22,9 +22,9 @@ fn doThing(form_id: u64) anyerror!FormValue { test "switch prong returns error enum" { switch (doThing(17) catch unreachable) { FormValue.Address => |payload| { - assert(payload == 1); + assertOrPanic(payload == 1); }, else => unreachable, } - assert(read_count == 1); + assertOrPanic(read_count == 1); } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/stage1/behavior/switch_prong_implicit_cast.zig similarity index 82% rename from test/cases/switch_prong_implicit_cast.zig rename to test/stage1/behavior/switch_prong_implicit_cast.zig index 56d37e290f1c..4ca031e2e11b 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/stage1/behavior/switch_prong_implicit_cast.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const FormValue = union(enum) { One: void, @@ -18,5 +18,5 @@ test "switch prong implicit cast" { FormValue.One => false, FormValue.Two => |x| x, }; - assert(result); + assertOrPanic(result); } diff --git a/test/cases/syntax.zig b/test/stage1/behavior/syntax.zig similarity index 99% rename from test/cases/syntax.zig rename to test/stage1/behavior/syntax.zig index 0c8c3c5ed314..451e39614292 100644 --- a/test/cases/syntax.zig +++ b/test/stage1/behavior/syntax.zig @@ -57,3 +57,4 @@ fn asm_lists() void { :::"a","b",); } } + diff --git a/test/cases/this.zig b/test/stage1/behavior/this.zig similarity index 74% rename from test/cases/this.zig rename to test/stage1/behavior/this.zig index c7be074f36c2..0e3a7a03ae07 100644 --- a/test/cases/this.zig +++ b/test/stage1/behavior/this.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const module = @This(); @@ -20,7 +20,7 @@ fn add(x: i32, y: i32) i32 { } test "this refer to module call private fn" { - assert(module.add(1, 2) == 3); + assertOrPanic(module.add(1, 2) == 3); } test "this refer to container" { @@ -29,6 +29,7 @@ test "this refer to container" { .y = 34, }; pt.addOne(); - assert(pt.x == 13); - assert(pt.y == 35); + assertOrPanic(pt.x == 13); + assertOrPanic(pt.y == 35); } + diff --git a/test/cases/truncate.zig b/test/stage1/behavior/truncate.zig similarity index 65% rename from test/cases/truncate.zig rename to test/stage1/behavior/truncate.zig index 02b5085ccd25..b7904bc7fbca 100644 --- a/test/cases/truncate.zig +++ b/test/stage1/behavior/truncate.zig @@ -1,8 +1,8 @@ const std = @import("std"); -const assert = std.debug.assert; +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 assert(y == 0); + comptime assertOrPanic(y == 0); } diff --git a/test/cases/try.zig b/test/stage1/behavior/try.zig similarity index 66% rename from test/cases/try.zig rename to test/stage1/behavior/try.zig index 450a9af6ac0b..fb802d02c626 100644 --- a/test/cases/try.zig +++ b/test/stage1/behavior/try.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "try on error union" { tryOnErrorUnionImpl(); @@ -11,7 +11,7 @@ fn tryOnErrorUnionImpl() void { error.CrappedOut => i32(2), else => unreachable, }; - assert(x == 11); + assertOrPanic(x == 11); } fn returnsTen() anyerror!i32 { @@ -19,11 +19,11 @@ fn returnsTen() anyerror!i32 { } test "try without vars" { - const result1 = if (failIfTrue(true)) 1 else |_| i32(2); - assert(result1 == 2); + const result1: i32 = if (failIfTrue(true)) 1 else |_| 2; + assertOrPanic(result1 == 2); - const result2 = if (failIfTrue(false)) 1 else |_| i32(2); - assert(result2 == 1); + const result2: i32 = if (failIfTrue(false)) 1 else |_| 2; + assertOrPanic(result2 == 1); } fn failIfTrue(ok: bool) anyerror!void { @@ -38,6 +38,6 @@ test "try then not executed with assignment" { if (failIfTrue(true)) { unreachable; } else |err| { - assert(err == error.ItBroke); + 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 000000000000..5ad80e06e178 --- /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/cases/undefined.zig b/test/stage1/behavior/undefined.zig similarity index 59% rename from test/cases/undefined.zig rename to test/stage1/behavior/undefined.zig index 83c620d21159..333e217d493b 100644 --- a/test/cases/undefined.zig +++ b/test/stage1/behavior/undefined.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; fn initStaticArray() [10]i32 { @@ -11,16 +11,16 @@ fn initStaticArray() [10]i32 { } 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); + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(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); + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(static_array[9] == 4); } } @@ -40,12 +40,12 @@ test "assign undefined to struct" { comptime { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + assertOrPanic(foo.x == 2); } { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + assertOrPanic(foo.x == 2); } } @@ -53,16 +53,17 @@ test "assign undefined to struct with method" { comptime { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + assertOrPanic(foo.x == 3); } { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + assertOrPanic(foo.x == 3); } } test "type name of undefined" { const x = undefined; - assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); + assertOrPanic(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); } + diff --git a/test/cases/underscore.zig b/test/stage1/behavior/underscore.zig similarity index 91% rename from test/cases/underscore.zig rename to test/stage1/behavior/underscore.zig index da1c97659cf3..7443319336b0 100644 --- a/test/cases/underscore.zig +++ b/test/stage1/behavior/underscore.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "ignore lval with underscore" { _ = false; diff --git a/test/cases/union.zig b/test/stage1/behavior/union.zig similarity index 77% rename from test/cases/union.zig rename to test/stage1/behavior/union.zig index 019a7012daf0..c8e8feb11ea6 100644 --- a/test/cases/union.zig +++ b/test/stage1/behavior/union.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Value = union(enum) { Int: u64, @@ -27,11 +27,11 @@ const array = []Value{ test "unions embedded in aggregate types" { switch (array[1]) { - Value.Array => |arr| assert(arr[4] == 3), + Value.Array => |arr| assertOrPanic(arr[4] == 3), else => unreachable, } switch ((err catch unreachable).val1) { - Value.Int => |x| assert(x == 1234), + Value.Int => |x| assertOrPanic(x == 1234), else => unreachable, } } @@ -43,18 +43,18 @@ const Foo = union { test "basic unions" { var foo = Foo{ .int = 1 }; - assert(foo.int == 1); + assertOrPanic(foo.int == 1); foo = Foo{ .float = 12.34 }; - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); } test "comptime union field access" { comptime { var foo = Foo{ .int = 0 }; - assert(foo.int == 0); + assertOrPanic(foo.int == 0); foo = Foo{ .float = 42.42 }; - assert(foo.float == 42.42); + assertOrPanic(foo.float == 42.42); } } @@ -62,10 +62,10 @@ test "init union with runtime value" { var foo: Foo = undefined; setFloat(&foo, 12.34); - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); setInt(&foo, 42); - assert(foo.int == 42); + assertOrPanic(foo.int == 42); } fn setFloat(foo: *Foo, x: f64) void { @@ -83,9 +83,9 @@ const FooExtern = extern union { test "basic extern unions" { var foo = FooExtern{ .int = 1 }; - assert(foo.int == 1); + assertOrPanic(foo.int == 1); foo.float = 12.34; - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); } const Letter = enum { @@ -105,11 +105,11 @@ test "union with specified enum tag" { } fn doTest() void { - assert(bar(Payload{ .A = 1234 }) == -10); + assertOrPanic(bar(Payload{ .A = 1234 }) == -10); } fn bar(value: Payload) i32 { - assert(Letter(value) == Letter.A); + 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, @@ -125,8 +125,8 @@ const MultipleChoice = union(enum(u32)) { }; test "simple union(enum(u32))" { var x = MultipleChoice.C; - assert(x == MultipleChoice.C); - assert(@enumToInt(@TagType(MultipleChoice)(x)) == 60); + assertOrPanic(x == MultipleChoice.C); + assertOrPanic(@enumToInt(@TagType(MultipleChoice)(x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -142,14 +142,14 @@ const MultipleChoice2 = union(enum(u32)) { }; test "union(enum(u32)) with specified and unspecified tag values" { - comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); + comptime assertOrPanic(@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) { + assertOrPanic(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); + assertOrPanic(1123 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => |v| i32(1000) + v, @@ -167,7 +167,7 @@ const ExternPtrOrInt = extern union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(ExternPtrOrInt) == 8); + comptime assertOrPanic(@sizeOf(ExternPtrOrInt) == 8); } const PackedPtrOrInt = packed union { @@ -175,14 +175,14 @@ const PackedPtrOrInt = packed union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(PackedPtrOrInt) == 8); + comptime assertOrPanic(@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); + comptime assertOrPanic(@sizeOf(ZeroBits) == 0); } const TheTag = enum { @@ -196,9 +196,9 @@ const TheUnion = union(TheTag) { 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); + assertOrPanic(TheUnion.A == TheTag.A); + assertOrPanic(TheUnion.B == TheTag.B); + assertOrPanic(TheUnion.C == TheTag.C); } test "cast union to tag type of union" { @@ -207,12 +207,12 @@ test "cast union to tag type of union" { } fn testCastUnionToTagType(x: TheUnion) void { - assert(TheTag(x) == TheTag.B); + assertOrPanic(TheTag(x) == TheTag.B); } test "cast tag type of union to union" { var x: Value2 = Letter2.B; - assert(Letter2(x) == Letter2.B); + assertOrPanic(Letter2(x) == Letter2.B); } const Letter2 = enum { A, @@ -227,11 +227,11 @@ const Value2 = union(Letter2) { test "implicit cast union to its tag type" { var x: Value2 = Letter2.B; - assert(x == Letter2.B); + assertOrPanic(x == Letter2.B); giveMeLetterB(x); } fn giveMeLetterB(x: Letter2) void { - assert(x == Value2.B); + assertOrPanic(x == Value2.B); } pub const PackThis = union(enum) { @@ -244,7 +244,7 @@ test "constant packed union" { } fn testConstPackedUnion(expected_tokens: []const PackThis) void { - assert(expected_tokens[0].StringLiteral == 1); + assertOrPanic(expected_tokens[0].StringLiteral == 1); } test "switch on union with only 1 field" { @@ -256,7 +256,7 @@ test "switch on union with only 1 field" { z = PartialInstWithPayload{ .Compiled = 1234 }; switch (z) { PartialInstWithPayload.Compiled => |x| { - assert(x == 1234); + assertOrPanic(x == 1234); return; }, } @@ -282,11 +282,11 @@ test "access a member of tagged union with conflicting enum tag name" { const B = void; }; - comptime assert(Bar.A == u8); + comptime assertOrPanic(Bar.A == u8); } test "tagged union initialization with runtime void" { - assert(testTaggedUnionInit({})); + assertOrPanic(testTaggedUnionInit({})); } const TaggedUnionWithAVoid = union(enum) { @@ -324,9 +324,9 @@ test "union with only 1 field casted to its enum type" { var e = Expr{ .Literal = Literal{ .Bool = true } }; const Tag = @TagType(Expr); - comptime assert(@TagType(Tag) == comptime_int); + comptime assertOrPanic(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); + assertOrPanic(t == Expr.Literal); } test "union with only 1 field casted to its enum type which has enum value specified" { @@ -344,9 +344,9 @@ test "union with only 1 field casted to its enum type which has enum value speci }; var e = Expr{ .Literal = Literal{ .Bool = true } }; - comptime assert(@TagType(Tag) == comptime_int); + comptime assertOrPanic(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); - assert(@enumToInt(t) == 33); - comptime assert(@enumToInt(t) == 33); + assertOrPanic(t == Expr.Literal); + assertOrPanic(@enumToInt(t) == 33); + comptime assertOrPanic(@enumToInt(t) == 33); } diff --git a/test/cases/var_args.zig b/test/stage1/behavior/var_args.zig similarity index 56% rename from test/cases/var_args.zig rename to test/stage1/behavior/var_args.zig index 3eb6e304486b..1f782a3bb3a3 100644 --- a/test/cases/var_args.zig +++ b/test/stage1/behavior/var_args.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn add(args: ...) i32 { var sum = i32(0); @@ -12,9 +12,9 @@ fn add(args: ...) i32 { } test "add arbitrary args" { - assert(add(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(add(i32(1234)) == 1234); - assert(add() == 0); + assertOrPanic(add(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(add(i32(1234)) == 1234); + assertOrPanic(add() == 0); } fn readFirstVarArg(args: ...) void { @@ -26,9 +26,9 @@ test "send void arg to var args" { } test "pass args directly" { - assert(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(addSomeStuff(i32(1234)) == 1234); - assert(addSomeStuff() == 0); + assertOrPanic(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(addSomeStuff(i32(1234)) == 1234); + assertOrPanic(addSomeStuff() == 0); } fn addSomeStuff(args: ...) i32 { @@ -36,24 +36,24 @@ fn addSomeStuff(args: ...) i32 { } test "runtime parameter before var args" { - assert(extraFn(10) == 0); - assert(extraFn(10, false) == 1); - assert(extraFn(10, false, true) == 2); + assertOrPanic(extraFn(10) == 0); + assertOrPanic(extraFn(10, false) == 1); + assertOrPanic(extraFn(10, false, true) == 2); // TODO issue #313 //comptime { - // assert(extraFn(10) == 0); - // assert(extraFn(10, false) == 1); - // assert(extraFn(10, false, true) == 2); + // 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) { - assert(args[0] == false); + assertOrPanic(args[0] == false); } if (args.len >= 2) { - assert(args[1] == true); + assertOrPanic(args[1] == true); } return args.len; } @@ -71,8 +71,8 @@ fn foo2(args: ...) bool { } test "array of var args functions" { - assert(foos[0]()); - assert(!foos[1]()); + assertOrPanic(foos[0]()); + assertOrPanic(!foos[1]()); } test "pass zero length array to var args param" { diff --git a/test/cases/void.zig b/test/stage1/behavior/void.zig similarity index 67% rename from test/cases/void.zig rename to test/stage1/behavior/void.zig index 7121ac664b94..431d3f4eb103 100644 --- a/test/cases/void.zig +++ b/test/stage1/behavior/void.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Foo = struct { a: void, @@ -13,14 +13,14 @@ test "compare void with void compile time known" { .b = 1, .c = {}, }; - assert(foo.a == {}); + assertOrPanic(foo.a == {}); } } test "iterate over a void slice" { var j: usize = 0; for (times(10)) |_, i| { - assert(i == j); + assertOrPanic(i == j); j += 1; } } @@ -28,3 +28,8 @@ test "iterate over a void slice" { fn times(n: usize) []const void { return ([*]void)(undefined)[0..n]; } + +test "void optional" { + var x: ?void = {}; + assertOrPanic(x != null); +} diff --git a/test/cases/while.zig b/test/stage1/behavior/while.zig similarity index 83% rename from test/cases/while.zig rename to test/stage1/behavior/while.zig index f774e0ec6b32..579b4e4db852 100644 --- a/test/cases/while.zig +++ b/test/stage1/behavior/while.zig @@ -1,12 +1,12 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "while loop" { var i: i32 = 0; while (i < 4) { i += 1; } - assert(i == 4); - assert(whileLoop1() == 1); + assertOrPanic(i == 4); + assertOrPanic(whileLoop1() == 1); } fn whileLoop1() i32 { return whileLoop2(); @@ -16,8 +16,9 @@ fn whileLoop2() i32 { return 1; } } + test "static eval while" { - assert(static_eval_while_number == 1); + assertOrPanic(static_eval_while_number == 1); } const static_eval_while_number = staticWhileLoop1(); fn staticWhileLoop1() i32 { @@ -31,7 +32,7 @@ fn staticWhileLoop2() i32 { test "continue and break" { runContinueAndBreakTest(); - assert(continue_and_break_counter == 8); + assertOrPanic(continue_and_break_counter == 8); } var continue_and_break_counter: i32 = 0; fn runContinueAndBreakTest() void { @@ -44,7 +45,7 @@ fn runContinueAndBreakTest() void { } break; } - assert(i == 4); + assertOrPanic(i == 4); } test "return with implicit cast from while loop" { @@ -65,7 +66,7 @@ test "while with continue expression" { sum += i; } } - assert(sum == 40); + assertOrPanic(sum == 40); } test "while with else" { @@ -77,8 +78,8 @@ test "while with else" { } else { got_else += 1; } - assert(sum == 10); - assert(got_else == 1); + assertOrPanic(sum == 10); + assertOrPanic(got_else == 1); } test "while with optional as condition" { @@ -87,7 +88,7 @@ test "while with optional as condition" { while (getNumberOrNull()) |value| { sum += value; } - assert(sum == 45); + assertOrPanic(sum == 45); } test "while with optional as condition with else" { @@ -96,12 +97,12 @@ test "while with optional as condition with else" { var got_else: i32 = 0; while (getNumberOrNull()) |value| { sum += value; - assert(got_else == 0); + assertOrPanic(got_else == 0); } else { got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); } test "while with error union condition" { @@ -111,11 +112,11 @@ test "while with error union condition" { while (getNumberOrErr()) |value| { sum += value; } else |err| { - assert(err == error.OutOfNumbers); + assertOrPanic(err == error.OutOfNumbers); got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); } var numbers_left: i32 = undefined; @@ -137,7 +138,7 @@ test "while on optional with else result follow else prong" { break value; } else i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on optional with else result follow break prong" { @@ -145,7 +146,7 @@ test "while on optional with else result follow break prong" { break value; } else i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "while on error union with else result follow else prong" { @@ -153,7 +154,7 @@ test "while on error union with else result follow else prong" { break value; } else |err| i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on error union with else result follow break prong" { @@ -161,7 +162,7 @@ test "while on error union with else result follow break prong" { break value; } else |err| i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "while on bool with else result follow else prong" { @@ -169,7 +170,7 @@ test "while on bool with else result follow else prong" { break i32(10); } else i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on bool with else result follow break prong" { @@ -177,7 +178,7 @@ test "while on bool with else result follow break prong" { break i32(10); } else i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "break from outer while loop" { diff --git a/test/cases/widening.zig b/test/stage1/behavior/widening.zig similarity index 75% rename from test/cases/widening.zig rename to test/stage1/behavior/widening.zig index cf6ab4ca0fb4..7577868aff54 100644 --- a/test/cases/widening.zig +++ b/test/stage1/behavior/widening.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; test "integer widening" { @@ -9,13 +9,13 @@ test "integer widening" { var d: u64 = c; var e: u64 = d; var f: u128 = e; - assert(f == a); + assertOrPanic(f == a); } test "implicit unsigned integer to signed integer" { var a: u8 = 250; var b: i16 = a; - assert(b == 250); + assertOrPanic(b == 250); } test "float widening" { @@ -23,5 +23,6 @@ test "float widening" { var b: f32 = a; var c: f64 = b; var d: f128 = c; - assert(d == a); + assertOrPanic(d == a); } +