| author | |
| committer | |
| log | d1086893829d91d0e1335e14b0434a23ff0e8999 |
| tree | 17e55b50e624ae06a93522a38aba3c0e83edf37e |
| parent | 7a74dbadd79d2b26a449027dd83753ae4d2a8032 |
| parent | 1473eb9ae0196db729716aa0d29f5ce263412307 |
closes #14412 files changed, 799 insertions(+), 26 deletions(-)
doc/langref.html.in+7| ... | ... | @@ -75,6 +75,7 @@ |
| 75 | 75 | <li><a href="#slices">Slices</a></li> |
| 76 | 76 | <li><a href="#struct">struct</a></li> |
| 77 | 77 | <li><a href="#enum">enum</a></li> |
| 78 | <li><a href="#union">union</a></li> | |
| 78 | 79 | <li><a href="#switch">switch</a></li> |
| 79 | 80 | <li><a href="#while">while</a></li> |
| 80 | 81 | <li><a href="#for">for</a></li> |
| ... | ... | @@ -209,6 +210,7 @@ |
| 209 | 210 | <li><a href="#undef-invalid-error-code">Invalid Error Code</a></li> |
| 210 | 211 | <li><a href="#undef-invalid-enum-cast">Invalid Enum Cast</a></li> |
| 211 | 212 | <li><a href="#undef-incorrect-pointer-alignment">Incorrect Pointer Alignment</a></li> |
| 213 | <li><a href="#undef-bad-union-field">Wrong Union Field Access</a></li> | |
| 212 | 214 | </ul> |
| 213 | 215 | </li> |
| 214 | 216 | <li><a href="#memory">Memory</a></li> |
| ... | ... | @@ -2189,6 +2191,8 @@ Test 4/4 enum builtins...OK</code></pre> |
| 2189 | 2191 | <li><a href="#builtin-enumTagName">@enumTagName</a></li> |
| 2190 | 2192 | <li><a href="#builtin-memberCount">@memberCount</a></li> |
| 2191 | 2193 | </ul> |
| 2194 | <h2 id="union">union</h2> | |
| 2195 | <p>TODO union documentation</p> | |
| 2192 | 2196 | <h2 id="switch">switch</h2> |
| 2193 | 2197 | <pre><code class="zig">const assert = @import("std").debug.assert; |
| 2194 | 2198 | const builtin = @import("builtin"); |
| ... | ... | @@ -5117,6 +5121,9 @@ comptime { |
| 5117 | 5121 | <h3 id="undef-incorrect-pointer-alignment">Incorrect Pointer Alignment</h3> |
| 5118 | 5122 | <p>TODO</p> |
| 5119 | 5123 | |
| 5124 | <h3 id="undef-bad-union-field">Wrong Union Field Access</h3> | |
| 5125 | <p>TODO</p> | |
| 5126 | ||
| 5120 | 5127 | <h2 id="memory">Memory</h2> |
| 5121 | 5128 | <p>TODO: explain no default allocator in zig</p> |
| 5122 | 5129 | <p>TODO: show how to use the allocator interface</p> |
src/all_types.hpp+52-2| ... | ... | @@ -73,6 +73,7 @@ enum ConstParentId { |
| 73 | 73 | ConstParentIdNone, |
| 74 | 74 | ConstParentIdStruct, |
| 75 | 75 | ConstParentIdArray, |
| 76 | ConstParentIdUnion, | |
| 76 | 77 | }; |
| 77 | 78 | |
| 78 | 79 | struct ConstParent { |
| ... | ... | @@ -87,6 +88,9 @@ struct ConstParent { |
| 87 | 88 | ConstExprValue *struct_val; |
| 88 | 89 | size_t field_index; |
| 89 | 90 | } p_struct; |
| 91 | struct { | |
| 92 | ConstExprValue *union_val; | |
| 93 | } p_union; | |
| 90 | 94 | } data; |
| 91 | 95 | }; |
| 92 | 96 | |
| ... | ... | @@ -100,6 +104,12 @@ struct ConstStructValue { |
| 100 | 104 | ConstParent parent; |
| 101 | 105 | }; |
| 102 | 106 | |
| 107 | struct ConstUnionValue { | |
| 108 | uint64_t tag; | |
| 109 | ConstExprValue *payload; | |
| 110 | ConstParent parent; | |
| 111 | }; | |
| 112 | ||
| 103 | 113 | enum ConstArraySpecial { |
| 104 | 114 | ConstArraySpecialNone, |
| 105 | 115 | ConstArraySpecialUndef, |
| ... | ... | @@ -238,6 +248,7 @@ struct ConstExprValue { |
| 238 | 248 | ErrorTableEntry *x_pure_err; |
| 239 | 249 | ConstEnumValue x_enum; |
| 240 | 250 | ConstStructValue x_struct; |
| 251 | ConstUnionValue x_union; | |
| 241 | 252 | ConstArrayValue x_array; |
| 242 | 253 | ConstPtrValue x_ptr; |
| 243 | 254 | ImportTableEntry *x_import; |
| ... | ... | @@ -336,6 +347,13 @@ struct TypeEnumField { |
| 336 | 347 | uint32_t gen_index; |
| 337 | 348 | }; |
| 338 | 349 | |
| 350 | struct TypeUnionField { | |
| 351 | Buf *name; | |
| 352 | TypeTableEntry *type_entry; | |
| 353 | uint32_t value; | |
| 354 | uint32_t gen_index; | |
| 355 | }; | |
| 356 | ||
| 339 | 357 | enum NodeType { |
| 340 | 358 | NodeTypeRoot, |
| 341 | 359 | NodeTypeFnProto, |
| ... | ... | @@ -1021,14 +1039,19 @@ struct TypeTableEntryEnumTag { |
| 1021 | 1039 | LLVMValueRef name_table; |
| 1022 | 1040 | }; |
| 1023 | 1041 | |
| 1042 | uint32_t type_ptr_hash(const TypeTableEntry *ptr); | |
| 1043 | bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b); | |
| 1044 | ||
| 1024 | 1045 | struct TypeTableEntryUnion { |
| 1025 | 1046 | AstNode *decl_node; |
| 1026 | 1047 | ContainerLayout layout; |
| 1027 | 1048 | uint32_t src_field_count; |
| 1028 | 1049 | uint32_t gen_field_count; |
| 1029 | TypeStructField *fields; | |
| 1030 | uint64_t size_bytes; | |
| 1050 | TypeUnionField *fields; | |
| 1031 | 1051 | bool is_invalid; // true if any fields are invalid |
| 1052 | TypeTableEntry *tag_type; | |
| 1053 | LLVMTypeRef union_type_ref; | |
| 1054 | ||
| 1032 | 1055 | ScopeDecls *decls_scope; |
| 1033 | 1056 | |
| 1034 | 1057 | // set this flag temporarily to detect infinite loops |
| ... | ... | @@ -1039,6 +1062,13 @@ struct TypeTableEntryUnion { |
| 1039 | 1062 | |
| 1040 | 1063 | bool zero_bits_loop_flag; |
| 1041 | 1064 | bool zero_bits_known; |
| 1065 | uint32_t abi_alignment; // also figured out with zero_bits pass | |
| 1066 | ||
| 1067 | size_t gen_union_index; | |
| 1068 | size_t gen_tag_index; | |
| 1069 | ||
| 1070 | uint32_t union_size_bytes; | |
| 1071 | TypeTableEntry *most_aligned_union_member; | |
| 1042 | 1072 | }; |
| 1043 | 1073 | |
| 1044 | 1074 | struct FnGenParamInfo { |
| ... | ... | @@ -1287,6 +1317,7 @@ enum PanicMsgId { |
| 1287 | 1317 | PanicMsgIdUnwrapMaybeFail, |
| 1288 | 1318 | PanicMsgIdInvalidErrorCode, |
| 1289 | 1319 | PanicMsgIdIncorrectAlignment, |
| 1320 | PanicMsgIdBadUnionField, | |
| 1290 | 1321 | |
| 1291 | 1322 | PanicMsgIdCount, |
| 1292 | 1323 | }; |
| ... | ... | @@ -1796,6 +1827,7 @@ enum IrInstructionId { |
| 1796 | 1827 | IrInstructionIdFieldPtr, |
| 1797 | 1828 | IrInstructionIdStructFieldPtr, |
| 1798 | 1829 | IrInstructionIdEnumFieldPtr, |
| 1830 | IrInstructionIdUnionFieldPtr, | |
| 1799 | 1831 | IrInstructionIdElemPtr, |
| 1800 | 1832 | IrInstructionIdVarPtr, |
| 1801 | 1833 | IrInstructionIdCall, |
| ... | ... | @@ -1805,6 +1837,7 @@ enum IrInstructionId { |
| 1805 | 1837 | IrInstructionIdContainerInitList, |
| 1806 | 1838 | IrInstructionIdContainerInitFields, |
| 1807 | 1839 | IrInstructionIdStructInit, |
| 1840 | IrInstructionIdUnionInit, | |
| 1808 | 1841 | IrInstructionIdUnreachable, |
| 1809 | 1842 | IrInstructionIdTypeOf, |
| 1810 | 1843 | IrInstructionIdToPtrType, |
| ... | ... | @@ -2060,6 +2093,14 @@ struct IrInstructionEnumFieldPtr { |
| 2060 | 2093 | bool is_const; |
| 2061 | 2094 | }; |
| 2062 | 2095 | |
| 2096 | struct IrInstructionUnionFieldPtr { | |
| 2097 | IrInstruction base; | |
| 2098 | ||
| 2099 | IrInstruction *union_ptr; | |
| 2100 | TypeUnionField *field; | |
| 2101 | bool is_const; | |
| 2102 | }; | |
| 2103 | ||
| 2063 | 2104 | struct IrInstructionElemPtr { |
| 2064 | 2105 | IrInstruction base; |
| 2065 | 2106 | |
| ... | ... | @@ -2150,6 +2191,15 @@ struct IrInstructionStructInit { |
| 2150 | 2191 | LLVMValueRef tmp_ptr; |
| 2151 | 2192 | }; |
| 2152 | 2193 | |
| 2194 | struct IrInstructionUnionInit { | |
| 2195 | IrInstruction base; | |
| 2196 | ||
| 2197 | TypeTableEntry *union_type; | |
| 2198 | TypeUnionField *field; | |
| 2199 | IrInstruction *init_value; | |
| 2200 | LLVMValueRef tmp_ptr; | |
| 2201 | }; | |
| 2202 | ||
| 2153 | 2203 | struct IrInstructionUnreachable { |
| 2154 | 2204 | IrInstruction base; |
| 2155 | 2205 | }; |
src/analyze.cpp+331-6| ... | ... | @@ -1008,11 +1008,12 @@ TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKi |
| 1008 | 1008 | } |
| 1009 | 1009 | |
| 1010 | 1010 | size_t line = decl_node ? decl_node->line : 0; |
| 1011 | unsigned dwarf_kind = ZigLLVMTag_DW_structure_type(); | |
| 1011 | 1012 | |
| 1012 | 1013 | ImportTableEntry *import = get_scope_import(scope); |
| 1013 | 1014 | entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name); |
| 1014 | 1015 | entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, |
| 1015 | ZigLLVMTag_DW_structure_type(), name, | |
| 1016 | dwarf_kind, name, | |
| 1016 | 1017 | ZigLLVMFileToScope(import->di_file), import->di_file, (unsigned)(line + 1)); |
| 1017 | 1018 | |
| 1018 | 1019 | buf_init_from_str(&entry->name, name); |
| ... | ... | @@ -1285,7 +1286,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { |
| 1285 | 1286 | return; |
| 1286 | 1287 | |
| 1287 | 1288 | resolve_enum_zero_bits(g, enum_type); |
| 1288 | if (enum_type->data.enumeration.is_invalid) | |
| 1289 | if (type_is_invalid(enum_type)) | |
| 1289 | 1290 | return; |
| 1290 | 1291 | |
| 1291 | 1292 | AstNode *decl_node = enum_type->data.enumeration.decl_node; |
| ... | ... | @@ -1834,7 +1835,246 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { |
| 1834 | 1835 | } |
| 1835 | 1836 | |
| 1836 | 1837 | static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { |
| 1837 | zig_panic("TODO"); | |
| 1838 | assert(union_type->id == TypeTableEntryIdUnion); | |
| 1839 | ||
| 1840 | if (union_type->data.unionation.complete) | |
| 1841 | return; | |
| 1842 | ||
| 1843 | resolve_union_zero_bits(g, union_type); | |
| 1844 | if (type_is_invalid(union_type)) | |
| 1845 | return; | |
| 1846 | ||
| 1847 | AstNode *decl_node = union_type->data.unionation.decl_node; | |
| 1848 | ||
| 1849 | if (union_type->data.unionation.embedded_in_current) { | |
| 1850 | if (!union_type->data.unionation.reported_infinite_err) { | |
| 1851 | union_type->data.unionation.reported_infinite_err = true; | |
| 1852 | add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name))); | |
| 1853 | } | |
| 1854 | return; | |
| 1855 | } | |
| 1856 | ||
| 1857 | assert(!union_type->data.unionation.zero_bits_loop_flag); | |
| 1858 | assert(decl_node->type == NodeTypeContainerDecl); | |
| 1859 | assert(union_type->di_type); | |
| 1860 | ||
| 1861 | uint32_t field_count = union_type->data.unionation.src_field_count; | |
| 1862 | ||
| 1863 | assert(union_type->data.unionation.fields); | |
| 1864 | ||
| 1865 | uint32_t gen_field_count = union_type->data.unionation.gen_field_count; | |
| 1866 | ZigLLVMDIType **union_inner_di_types = allocate<ZigLLVMDIType*>(gen_field_count); | |
| 1867 | ||
| 1868 | TypeTableEntry *most_aligned_union_member = nullptr; | |
| 1869 | uint64_t size_of_most_aligned_member_in_bits = 0; | |
| 1870 | uint64_t biggest_align_in_bits = 0; | |
| 1871 | uint64_t biggest_size_in_bits = 0; | |
| 1872 | ||
| 1873 | bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); | |
| 1874 | ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count); | |
| 1875 | ||
| 1876 | Scope *scope = &union_type->data.unionation.decls_scope->base; | |
| 1877 | ImportTableEntry *import = get_scope_import(scope); | |
| 1878 | ||
| 1879 | // set temporary flag | |
| 1880 | union_type->data.unionation.embedded_in_current = true; | |
| 1881 | ||
| 1882 | for (uint32_t i = 0; i < field_count; i += 1) { | |
| 1883 | AstNode *field_node = decl_node->data.container_decl.fields.at(i); | |
| 1884 | TypeUnionField *type_union_field = &union_type->data.unionation.fields[i]; | |
| 1885 | TypeTableEntry *field_type = type_union_field->type_entry; | |
| 1886 | ||
| 1887 | ensure_complete_type(g, field_type); | |
| 1888 | if (type_is_invalid(field_type)) { | |
| 1889 | union_type->data.unionation.is_invalid = true; | |
| 1890 | continue; | |
| 1891 | } | |
| 1892 | ||
| 1893 | if (!type_has_bits(field_type)) | |
| 1894 | continue; | |
| 1895 | ||
| 1896 | di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_union_field->name), i); | |
| 1897 | ||
| 1898 | uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); | |
| 1899 | uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); | |
| 1900 | ||
| 1901 | assert(store_size_in_bits > 0); | |
| 1902 | assert(abi_align_in_bits > 0); | |
| 1903 | ||
| 1904 | union_inner_di_types[type_union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, | |
| 1905 | ZigLLVMTypeToScope(union_type->di_type), buf_ptr(type_union_field->name), | |
| 1906 | import->di_file, (unsigned)(field_node->line + 1), | |
| 1907 | store_size_in_bits, | |
| 1908 | abi_align_in_bits, | |
| 1909 | 0, | |
| 1910 | 0, field_type->di_type); | |
| 1911 | ||
| 1912 | biggest_size_in_bits = max(biggest_size_in_bits, store_size_in_bits); | |
| 1913 | ||
| 1914 | if (!most_aligned_union_member || abi_align_in_bits > biggest_align_in_bits) { | |
| 1915 | most_aligned_union_member = field_type; | |
| 1916 | biggest_align_in_bits = abi_align_in_bits; | |
| 1917 | size_of_most_aligned_member_in_bits = store_size_in_bits; | |
| 1918 | } | |
| 1919 | } | |
| 1920 | ||
| 1921 | // unset temporary flag | |
| 1922 | union_type->data.unionation.embedded_in_current = false; | |
| 1923 | union_type->data.unionation.complete = true; | |
| 1924 | union_type->data.unionation.union_size_bytes = biggest_size_in_bits / 8; | |
| 1925 | union_type->data.unionation.most_aligned_union_member = most_aligned_union_member; | |
| 1926 | ||
| 1927 | if (union_type->data.unionation.is_invalid) | |
| 1928 | return; | |
| 1929 | ||
| 1930 | if (union_type->zero_bits) { | |
| 1931 | union_type->type_ref = LLVMVoidType(); | |
| 1932 | ||
| 1933 | uint64_t debug_size_in_bits = 0; | |
| 1934 | uint64_t debug_align_in_bits = 0; | |
| 1935 | ZigLLVMDIType **di_root_members = nullptr; | |
| 1936 | size_t debug_member_count = 0; | |
| 1937 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, | |
| 1938 | ZigLLVMFileToScope(import->di_file), | |
| 1939 | buf_ptr(&union_type->name), | |
| 1940 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1941 | debug_size_in_bits, | |
| 1942 | debug_align_in_bits, | |
| 1943 | 0, di_root_members, (int)debug_member_count, 0, ""); | |
| 1944 | ||
| 1945 | ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type); | |
| 1946 | union_type->di_type = replacement_di_type; | |
| 1947 | return; | |
| 1948 | } | |
| 1949 | ||
| 1950 | assert(most_aligned_union_member != nullptr); | |
| 1951 | ||
| 1952 | bool want_safety = auto_layout && (field_count >= 2); | |
| 1953 | uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; | |
| 1954 | ||
| 1955 | ||
| 1956 | if (!want_safety) { | |
| 1957 | if (padding_in_bits > 0) { | |
| 1958 | TypeTableEntry *u8_type = get_int_type(g, false, 8); | |
| 1959 | TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); | |
| 1960 | LLVMTypeRef union_element_types[] = { | |
| 1961 | most_aligned_union_member->type_ref, | |
| 1962 | padding_array->type_ref, | |
| 1963 | }; | |
| 1964 | LLVMStructSetBody(union_type->type_ref, union_element_types, 2, false); | |
| 1965 | } else { | |
| 1966 | LLVMStructSetBody(union_type->type_ref, &most_aligned_union_member->type_ref, 1, false); | |
| 1967 | } | |
| 1968 | union_type->data.unionation.union_type_ref = union_type->type_ref; | |
| 1969 | union_type->data.unionation.gen_tag_index = SIZE_MAX; | |
| 1970 | union_type->data.unionation.gen_union_index = SIZE_MAX; | |
| 1971 | ||
| 1972 | assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type->type_ref) >= biggest_align_in_bits); | |
| 1973 | assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref) >= biggest_size_in_bits); | |
| 1974 | ||
| 1975 | // create debug type for union | |
| 1976 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, | |
| 1977 | ZigLLVMFileToScope(import->di_file), buf_ptr(&union_type->name), | |
| 1978 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1979 | biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, | |
| 1980 | gen_field_count, 0, ""); | |
| 1981 | ||
| 1982 | ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type); | |
| 1983 | union_type->di_type = replacement_di_type; | |
| 1984 | return; | |
| 1985 | } | |
| 1986 | ||
| 1987 | LLVMTypeRef union_type_ref; | |
| 1988 | if (padding_in_bits > 0) { | |
| 1989 | TypeTableEntry *u8_type = get_int_type(g, false, 8); | |
| 1990 | TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); | |
| 1991 | LLVMTypeRef union_element_types[] = { | |
| 1992 | most_aligned_union_member->type_ref, | |
| 1993 | padding_array->type_ref, | |
| 1994 | }; | |
| 1995 | union_type_ref = LLVMStructType(union_element_types, 2, false); | |
| 1996 | } else { | |
| 1997 | union_type_ref = most_aligned_union_member->type_ref; | |
| 1998 | } | |
| 1999 | union_type->data.unionation.union_type_ref = union_type_ref; | |
| 2000 | ||
| 2001 | assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits); | |
| 2002 | assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits); | |
| 2003 | ||
| 2004 | // create llvm type for root struct | |
| 2005 | TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); | |
| 2006 | TypeTableEntry *tag_type_entry = tag_int_type; | |
| 2007 | union_type->data.unionation.tag_type = tag_type_entry; | |
| 2008 | uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); | |
| 2009 | ||
| 2010 | if (align_of_tag_in_bits >= biggest_align_in_bits) { | |
| 2011 | union_type->data.unionation.gen_tag_index = 0; | |
| 2012 | union_type->data.unionation.gen_union_index = 1; | |
| 2013 | } else { | |
| 2014 | union_type->data.unionation.gen_union_index = 0; | |
| 2015 | union_type->data.unionation.gen_tag_index = 1; | |
| 2016 | } | |
| 2017 | ||
| 2018 | LLVMTypeRef root_struct_element_types[2]; | |
| 2019 | root_struct_element_types[union_type->data.unionation.gen_tag_index] = tag_type_entry->type_ref; | |
| 2020 | root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref; | |
| 2021 | LLVMStructSetBody(union_type->type_ref, root_struct_element_types, 2, false); | |
| 2022 | ||
| 2023 | ||
| 2024 | // create debug type for root struct | |
| 2025 | ||
| 2026 | // create debug type for tag | |
| 2027 | uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 2028 | uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 2029 | ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, | |
| 2030 | ZigLLVMTypeToScope(union_type->di_type), "AnonEnum", | |
| 2031 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2032 | tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, | |
| 2033 | tag_type_entry->di_type, ""); | |
| 2034 | ||
| 2035 | // create debug type for union | |
| 2036 | ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, | |
| 2037 | ZigLLVMTypeToScope(union_type->di_type), "AnonUnion", | |
| 2038 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2039 | biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, | |
| 2040 | gen_field_count, 0, ""); | |
| 2041 | ||
| 2042 | uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref, | |
| 2043 | union_type->data.unionation.gen_union_index); | |
| 2044 | uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref, | |
| 2045 | union_type->data.unionation.gen_tag_index); | |
| 2046 | ||
| 2047 | ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, | |
| 2048 | ZigLLVMTypeToScope(union_type->di_type), "union_field", | |
| 2049 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2050 | biggest_size_in_bits, | |
| 2051 | biggest_align_in_bits, | |
| 2052 | union_offset_in_bits, | |
| 2053 | 0, union_di_type); | |
| 2054 | ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, | |
| 2055 | ZigLLVMTypeToScope(union_type->di_type), "tag_field", | |
| 2056 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2057 | tag_debug_size_in_bits, | |
| 2058 | tag_debug_align_in_bits, | |
| 2059 | tag_offset_in_bits, | |
| 2060 | 0, tag_di_type); | |
| 2061 | ||
| 2062 | ZigLLVMDIType *di_root_members[2]; | |
| 2063 | di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type; | |
| 2064 | di_root_members[union_type->data.unionation.gen_union_index] = union_member_di_type; | |
| 2065 | ||
| 2066 | uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref); | |
| 2067 | uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, union_type->type_ref); | |
| 2068 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, | |
| 2069 | ZigLLVMFileToScope(import->di_file), | |
| 2070 | buf_ptr(&union_type->name), | |
| 2071 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2072 | debug_size_in_bits, | |
| 2073 | debug_align_in_bits, | |
| 2074 | 0, nullptr, di_root_members, 2, 0, nullptr, ""); | |
| 2075 | ||
| 2076 | ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type); | |
| 2077 | union_type->di_type = replacement_di_type; | |
| 1838 | 2078 | } |
| 1839 | 2079 | |
| 1840 | 2080 | static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { |
| ... | ... | @@ -1873,7 +2113,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { |
| 1873 | 2113 | type_enum_field->value = i; |
| 1874 | 2114 | |
| 1875 | 2115 | type_ensure_zero_bits_known(g, field_type); |
| 1876 | if (field_type->id == TypeTableEntryIdInvalid) { | |
| 2116 | if (type_is_invalid(field_type)) { | |
| 1877 | 2117 | enum_type->data.enumeration.is_invalid = true; |
| 1878 | 2118 | continue; |
| 1879 | 2119 | } |
| ... | ... | @@ -1980,7 +2220,69 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { |
| 1980 | 2220 | } |
| 1981 | 2221 | |
| 1982 | 2222 | static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { |
| 1983 | zig_panic("TODO resolve_union_zero_bits"); | |
| 2223 | assert(union_type->id == TypeTableEntryIdUnion); | |
| 2224 | ||
| 2225 | if (union_type->data.unionation.zero_bits_known) | |
| 2226 | return; | |
| 2227 | ||
| 2228 | if (union_type->data.unionation.zero_bits_loop_flag) { | |
| 2229 | union_type->data.unionation.zero_bits_known = true; | |
| 2230 | return; | |
| 2231 | } | |
| 2232 | ||
| 2233 | union_type->data.unionation.zero_bits_loop_flag = true; | |
| 2234 | ||
| 2235 | AstNode *decl_node = union_type->data.unionation.decl_node; | |
| 2236 | assert(decl_node->type == NodeTypeContainerDecl); | |
| 2237 | assert(union_type->di_type); | |
| 2238 | ||
| 2239 | assert(!union_type->data.unionation.fields); | |
| 2240 | uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; | |
| 2241 | union_type->data.unionation.src_field_count = field_count; | |
| 2242 | union_type->data.unionation.fields = allocate<TypeUnionField>(field_count); | |
| 2243 | ||
| 2244 | uint32_t biggest_align_bytes = 0; | |
| 2245 | ||
| 2246 | Scope *scope = &union_type->data.unionation.decls_scope->base; | |
| 2247 | ||
| 2248 | uint32_t gen_field_index = 0; | |
| 2249 | for (uint32_t i = 0; i < field_count; i += 1) { | |
| 2250 | AstNode *field_node = decl_node->data.container_decl.fields.at(i); | |
| 2251 | TypeUnionField *type_union_field = &union_type->data.unionation.fields[i]; | |
| 2252 | type_union_field->name = field_node->data.struct_field.name; | |
| 2253 | TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); | |
| 2254 | type_union_field->type_entry = field_type; | |
| 2255 | type_union_field->value = i; | |
| 2256 | ||
| 2257 | type_ensure_zero_bits_known(g, field_type); | |
| 2258 | if (type_is_invalid(field_type)) { | |
| 2259 | union_type->data.unionation.is_invalid = true; | |
| 2260 | continue; | |
| 2261 | } | |
| 2262 | ||
| 2263 | if (!type_has_bits(field_type)) | |
| 2264 | continue; | |
| 2265 | ||
| 2266 | type_union_field->gen_index = gen_field_index; | |
| 2267 | gen_field_index += 1; | |
| 2268 | ||
| 2269 | uint32_t field_align_bytes = get_abi_alignment(g, field_type); | |
| 2270 | if (field_align_bytes > biggest_align_bytes) { | |
| 2271 | biggest_align_bytes = field_align_bytes; | |
| 2272 | } | |
| 2273 | } | |
| 2274 | ||
| 2275 | bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); | |
| 2276 | ||
| 2277 | union_type->data.unionation.zero_bits_loop_flag = false; | |
| 2278 | union_type->data.unionation.gen_field_count = gen_field_index; | |
| 2279 | union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !auto_layout)); | |
| 2280 | union_type->data.unionation.zero_bits_known = true; | |
| 2281 | ||
| 2282 | // also compute abi_alignment | |
| 2283 | if (!union_type->zero_bits) { | |
| 2284 | union_type->data.unionation.abi_alignment = biggest_align_bytes; | |
| 2285 | } | |
| 1984 | 2286 | } |
| 1985 | 2287 | |
| 1986 | 2288 | static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) { |
| ... | ... | @@ -2851,6 +3153,18 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { |
| 2851 | 3153 | return nullptr; |
| 2852 | 3154 | } |
| 2853 | 3155 | |
| 3156 | TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { | |
| 3157 | assert(type_entry->id == TypeTableEntryIdUnion); | |
| 3158 | assert(type_entry->data.unionation.complete); | |
| 3159 | for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { | |
| 3160 | TypeUnionField *field = &type_entry->data.unionation.fields[i]; | |
| 3161 | if (buf_eql_buf(field->name, name)) { | |
| 3162 | return field; | |
| 3163 | } | |
| 3164 | } | |
| 3165 | return nullptr; | |
| 3166 | } | |
| 3167 | ||
| 2854 | 3168 | static bool is_container(TypeTableEntry *type_entry) { |
| 2855 | 3169 | switch (type_entry->id) { |
| 2856 | 3170 | case TypeTableEntryIdInvalid: |
| ... | ... | @@ -4703,6 +5017,8 @@ ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) { |
| 4703 | 5017 | return &value->data.x_array.s_none.parent; |
| 4704 | 5018 | } else if (type_entry->id == TypeTableEntryIdStruct) { |
| 4705 | 5019 | return &value->data.x_struct.parent; |
| 5020 | } else if (type_entry->id == TypeTableEntryIdUnion) { | |
| 5021 | return &value->data.x_union.parent; | |
| 4706 | 5022 | } |
| 4707 | 5023 | return nullptr; |
| 4708 | 5024 | } |
| ... | ... | @@ -4914,7 +5230,8 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { |
| 4914 | 5230 | assert(type_entry->data.enumeration.abi_alignment != 0); |
| 4915 | 5231 | return type_entry->data.enumeration.abi_alignment; |
| 4916 | 5232 | } else if (type_entry->id == TypeTableEntryIdUnion) { |
| 4917 | zig_panic("TODO"); | |
| 5233 | assert(type_entry->data.unionation.abi_alignment != 0); | |
| 5234 | return type_entry->data.unionation.abi_alignment; | |
| 4918 | 5235 | } else if (type_entry->id == TypeTableEntryIdOpaque) { |
| 4919 | 5236 | return 1; |
| 4920 | 5237 | } else { |
| ... | ... | @@ -4929,3 +5246,11 @@ TypeTableEntry *get_align_amt_type(CodeGen *g) { |
| 4929 | 5246 | } |
| 4930 | 5247 | return g->align_amt_type; |
| 4931 | 5248 | } |
| 5249 | ||
| 5250 | uint32_t type_ptr_hash(const TypeTableEntry *ptr) { | |
| 5251 | return hash_ptr((void*)ptr); | |
| 5252 | } | |
| 5253 | ||
| 5254 | bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) { | |
| 5255 | return a == b; | |
| 5256 | } |
src/analyze.hpp+1| ... | ... | @@ -63,6 +63,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry); |
| 63 | 63 | TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name); |
| 64 | 64 | ScopeDecls *get_container_scope(TypeTableEntry *type_entry); |
| 65 | 65 | TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name); |
| 66 | TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name); | |
| 66 | 67 | bool is_container_ref(TypeTableEntry *type_entry); |
| 67 | 68 | void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); |
| 68 | 69 | void scan_import(CodeGen *g, ImportTableEntry *import); |
src/codegen.cpp+153-4| ... | ... | @@ -810,6 +810,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 810 | 810 | return buf_create_from_str("invalid error code"); |
| 811 | 811 | case PanicMsgIdIncorrectAlignment: |
| 812 | 812 | return buf_create_from_str("incorrect alignment"); |
| 813 | case PanicMsgIdBadUnionField: | |
| 814 | return buf_create_from_str("access of inactive union field"); | |
| 813 | 815 | } |
| 814 | 816 | zig_unreachable(); |
| 815 | 817 | } |
| ... | ... | @@ -2393,6 +2395,50 @@ static LLVMValueRef ir_render_enum_field_ptr(CodeGen *g, IrExecutable *executabl |
| 2393 | 2395 | return bitcasted_union_field_ptr; |
| 2394 | 2396 | } |
| 2395 | 2397 | |
| 2398 | static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executable, | |
| 2399 | IrInstructionUnionFieldPtr *instruction) | |
| 2400 | { | |
| 2401 | TypeTableEntry *union_ptr_type = instruction->union_ptr->value.type; | |
| 2402 | assert(union_ptr_type->id == TypeTableEntryIdPointer); | |
| 2403 | TypeTableEntry *union_type = union_ptr_type->data.pointer.child_type; | |
| 2404 | assert(union_type->id == TypeTableEntryIdUnion); | |
| 2405 | ||
| 2406 | TypeUnionField *field = instruction->field; | |
| 2407 | ||
| 2408 | if (!type_has_bits(field->type_entry)) | |
| 2409 | return nullptr; | |
| 2410 | ||
| 2411 | LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); | |
| 2412 | LLVMTypeRef field_type_ref = LLVMPointerType(field->type_entry->type_ref, 0); | |
| 2413 | ||
| 2414 | if (union_type->data.unionation.gen_tag_index == SIZE_MAX) { | |
| 2415 | LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, 0, ""); | |
| 2416 | LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); | |
| 2417 | return bitcasted_union_field_ptr; | |
| 2418 | } | |
| 2419 | ||
| 2420 | if (ir_want_debug_safety(g, &instruction->base)) { | |
| 2421 | LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, ""); | |
| 2422 | LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, ""); | |
| 2423 | LLVMValueRef expected_tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref, | |
| 2424 | field->value, false); | |
| 2425 | ||
| 2426 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk"); | |
| 2427 | LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail"); | |
| 2428 | LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, ""); | |
| 2429 | LLVMBuildCondBr(g->builder, ok_val, ok_block, bad_block); | |
| 2430 | ||
| 2431 | LLVMPositionBuilderAtEnd(g->builder, bad_block); | |
| 2432 | gen_debug_safety_crash(g, PanicMsgIdBadUnionField); | |
| 2433 | ||
| 2434 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 2435 | } | |
| 2436 | ||
| 2437 | LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, ""); | |
| 2438 | LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); | |
| 2439 | return bitcasted_union_field_ptr; | |
| 2440 | } | |
| 2441 | ||
| 2396 | 2442 | static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) { |
| 2397 | 2443 | const char *ptr = buf_ptr(node->data.asm_expr.asm_template) + tok->start + 2; |
| 2398 | 2444 | size_t len = tok->end - tok->start - 2; |
| ... | ... | @@ -3365,6 +3411,42 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, |
| 3365 | 3411 | return instruction->tmp_ptr; |
| 3366 | 3412 | } |
| 3367 | 3413 | |
| 3414 | static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) { | |
| 3415 | TypeUnionField *type_union_field = instruction->field; | |
| 3416 | ||
| 3417 | if (!type_has_bits(type_union_field->type_entry)) | |
| 3418 | return nullptr; | |
| 3419 | ||
| 3420 | uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry); | |
| 3421 | TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry, | |
| 3422 | false, false, field_align_bytes, | |
| 3423 | 0, 0); | |
| 3424 | ||
| 3425 | LLVMValueRef uncasted_union_ptr; | |
| 3426 | // Even if safety is off in this block, if the union type has the safety field, we have to populate it | |
| 3427 | // correctly. Otherwise safety code somewhere other than here could fail. | |
| 3428 | TypeTableEntry *union_type = instruction->union_type; | |
| 3429 | if (union_type->data.unionation.gen_tag_index != SIZE_MAX) { | |
| 3430 | LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, | |
| 3431 | union_type->data.unionation.gen_tag_index, ""); | |
| 3432 | LLVMValueRef tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref, | |
| 3433 | type_union_field->value, false); | |
| 3434 | gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); | |
| 3435 | ||
| 3436 | uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, | |
| 3437 | (unsigned)union_type->data.unionation.gen_union_index, ""); | |
| 3438 | } else { | |
| 3439 | uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, (unsigned)0, ""); | |
| 3440 | } | |
| 3441 | ||
| 3442 | LLVMValueRef field_ptr = LLVMBuildBitCast(g->builder, uncasted_union_ptr, ptr_type->type_ref, ""); | |
| 3443 | LLVMValueRef value = ir_llvm_value(g, instruction->init_value); | |
| 3444 | ||
| 3445 | gen_assign_raw(g, field_ptr, ptr_type, value); | |
| 3446 | ||
| 3447 | return instruction->tmp_ptr; | |
| 3448 | } | |
| 3449 | ||
| 3368 | 3450 | static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *executable, |
| 3369 | 3451 | IrInstructionContainerInitList *instruction) |
| 3370 | 3452 | { |
| ... | ... | @@ -3486,6 +3568,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 3486 | 3568 | return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction); |
| 3487 | 3569 | case IrInstructionIdEnumFieldPtr: |
| 3488 | 3570 | return ir_render_enum_field_ptr(g, executable, (IrInstructionEnumFieldPtr *)instruction); |
| 3571 | case IrInstructionIdUnionFieldPtr: | |
| 3572 | return ir_render_union_field_ptr(g, executable, (IrInstructionUnionFieldPtr *)instruction); | |
| 3489 | 3573 | case IrInstructionIdAsm: |
| 3490 | 3574 | return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); |
| 3491 | 3575 | case IrInstructionIdTestNonNull: |
| ... | ... | @@ -3544,6 +3628,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 3544 | 3628 | return ir_render_init_enum(g, executable, (IrInstructionInitEnum *)instruction); |
| 3545 | 3629 | case IrInstructionIdStructInit: |
| 3546 | 3630 | return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); |
| 3631 | case IrInstructionIdUnionInit: | |
| 3632 | return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction); | |
| 3547 | 3633 | case IrInstructionIdPtrCast: |
| 3548 | 3634 | return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction); |
| 3549 | 3635 | case IrInstructionIdBitCast: |
| ... | ... | @@ -3595,6 +3681,7 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) { |
| 3595 | 3681 | |
| 3596 | 3682 | static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index); |
| 3597 | 3683 | static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index); |
| 3684 | static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *array_const_val); | |
| 3598 | 3685 | |
| 3599 | 3686 | static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { |
| 3600 | 3687 | switch (parent->id) { |
| ... | ... | @@ -3608,6 +3695,8 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent |
| 3608 | 3695 | case ConstParentIdArray: |
| 3609 | 3696 | return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val, |
| 3610 | 3697 | parent->data.p_array.elem_index); |
| 3698 | case ConstParentIdUnion: | |
| 3699 | return gen_const_ptr_union_recursive(g, parent->data.p_union.union_val); | |
| 3611 | 3700 | } |
| 3612 | 3701 | zig_unreachable(); |
| 3613 | 3702 | } |
| ... | ... | @@ -3637,6 +3726,18 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s |
| 3637 | 3726 | return LLVMConstInBoundsGEP(base_ptr, indices, 2); |
| 3638 | 3727 | } |
| 3639 | 3728 | |
| 3729 | static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) { | |
| 3730 | ConstParent *parent = &union_const_val->data.x_union.parent; | |
| 3731 | LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); | |
| 3732 | ||
| 3733 | TypeTableEntry *u32 = g->builtin_types.entry_u32; | |
| 3734 | LLVMValueRef indices[] = { | |
| 3735 | LLVMConstNull(u32->type_ref), | |
| 3736 | LLVMConstInt(u32->type_ref, 0, false), | |
| 3737 | }; | |
| 3738 | return LLVMConstInBoundsGEP(base_ptr, indices, 2); | |
| 3739 | } | |
| 3740 | ||
| 3640 | 3741 | static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) { |
| 3641 | 3742 | switch (const_val->special) { |
| 3642 | 3743 | case ConstValSpecialRuntime: |
| ... | ... | @@ -3872,10 +3973,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { |
| 3872 | 3973 | return LLVMConstNamedStruct(type_entry->type_ref, fields, type_entry->data.structure.gen_field_count); |
| 3873 | 3974 | } |
| 3874 | 3975 | } |
| 3875 | case TypeTableEntryIdUnion: | |
| 3876 | { | |
| 3877 | zig_panic("TODO"); | |
| 3878 | } | |
| 3879 | 3976 | case TypeTableEntryIdArray: |
| 3880 | 3977 | { |
| 3881 | 3978 | uint64_t len = type_entry->data.array.len; |
| ... | ... | @@ -3898,6 +3995,55 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { |
| 3898 | 3995 | return LLVMConstArray(element_type_ref, values, (unsigned)len); |
| 3899 | 3996 | } |
| 3900 | 3997 | } |
| 3998 | case TypeTableEntryIdUnion: | |
| 3999 | { | |
| 4000 | LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref; | |
| 4001 | ConstExprValue *payload_value = const_val->data.x_union.payload; | |
| 4002 | assert(payload_value != nullptr); | |
| 4003 | ||
| 4004 | if (!type_has_bits(payload_value->type)) { | |
| 4005 | return LLVMGetUndef(union_type_ref); | |
| 4006 | } | |
| 4007 | ||
| 4008 | uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref); | |
| 4009 | uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes; | |
| 4010 | LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value); | |
| 4011 | bool make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) || | |
| 4012 | payload_value->type != type_entry->data.unionation.most_aligned_union_member; | |
| 4013 | ||
| 4014 | LLVMValueRef union_value_ref; | |
| 4015 | { | |
| 4016 | if (pad_bytes == 0) { | |
| 4017 | union_value_ref = correctly_typed_value; | |
| 4018 | } else { | |
| 4019 | LLVMValueRef fields[2]; | |
| 4020 | fields[0] = correctly_typed_value; | |
| 4021 | fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); | |
| 4022 | if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) { | |
| 4023 | union_value_ref = LLVMConstStruct(fields, 2, false); | |
| 4024 | } else { | |
| 4025 | union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2); | |
| 4026 | } | |
| 4027 | } | |
| 4028 | } | |
| 4029 | ||
| 4030 | if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { | |
| 4031 | return union_value_ref; | |
| 4032 | } | |
| 4033 | ||
| 4034 | LLVMValueRef tag_value = LLVMConstInt(type_entry->data.unionation.tag_type->type_ref, const_val->data.x_union.tag, false); | |
| 4035 | ||
| 4036 | LLVMValueRef fields[2]; | |
| 4037 | fields[type_entry->data.unionation.gen_union_index] = union_value_ref; | |
| 4038 | fields[type_entry->data.unionation.gen_tag_index] = tag_value; | |
| 4039 | ||
| 4040 | if (make_unnamed_struct) { | |
| 4041 | return LLVMConstStruct(fields, 2, false); | |
| 4042 | } else { | |
| 4043 | return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); | |
| 4044 | } | |
| 4045 | ||
| 4046 | } | |
| 3901 | 4047 | case TypeTableEntryIdEnum: |
| 3902 | 4048 | { |
| 3903 | 4049 | LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref; |
| ... | ... | @@ -4376,6 +4522,9 @@ static void do_code_gen(CodeGen *g) { |
| 4376 | 4522 | } else if (instruction->id == IrInstructionIdStructInit) { |
| 4377 | 4523 | IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction; |
| 4378 | 4524 | slot = &struct_init_instruction->tmp_ptr; |
| 4525 | } else if (instruction->id == IrInstructionIdUnionInit) { | |
| 4526 | IrInstructionUnionInit *union_init_instruction = (IrInstructionUnionInit *)instruction; | |
| 4527 | slot = &union_init_instruction->tmp_ptr; | |
| 4379 | 4528 | } else if (instruction->id == IrInstructionIdCall) { |
| 4380 | 4529 | IrInstructionCall *call_instruction = (IrInstructionCall *)instruction; |
| 4381 | 4530 | slot = &call_instruction->tmp_ptr; |
src/ir.cpp+161-11| ... | ... | @@ -227,6 +227,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumFieldPtr *) |
| 227 | 227 | return IrInstructionIdEnumFieldPtr; |
| 228 | 228 | } |
| 229 | 229 | |
| 230 | static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionFieldPtr *) { | |
| 231 | return IrInstructionIdUnionFieldPtr; | |
| 232 | } | |
| 233 | ||
| 230 | 234 | static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) { |
| 231 | 235 | return IrInstructionIdElemPtr; |
| 232 | 236 | } |
| ... | ... | @@ -351,6 +355,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) { |
| 351 | 355 | return IrInstructionIdStructInit; |
| 352 | 356 | } |
| 353 | 357 | |
| 358 | static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) { | |
| 359 | return IrInstructionIdUnionInit; | |
| 360 | } | |
| 361 | ||
| 354 | 362 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMinValue *) { |
| 355 | 363 | return IrInstructionIdMinValue; |
| 356 | 364 | } |
| ... | ... | @@ -922,6 +930,27 @@ static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction |
| 922 | 930 | return new_instruction; |
| 923 | 931 | } |
| 924 | 932 | |
| 933 | static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 934 | IrInstruction *union_ptr, TypeUnionField *field) | |
| 935 | { | |
| 936 | IrInstructionUnionFieldPtr *instruction = ir_build_instruction<IrInstructionUnionFieldPtr>(irb, scope, source_node); | |
| 937 | instruction->union_ptr = union_ptr; | |
| 938 | instruction->field = field; | |
| 939 | ||
| 940 | ir_ref_instruction(union_ptr, irb->current_basic_block); | |
| 941 | ||
| 942 | return &instruction->base; | |
| 943 | } | |
| 944 | ||
| 945 | static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, | |
| 946 | IrInstruction *union_ptr, TypeUnionField *type_union_field) | |
| 947 | { | |
| 948 | IrInstruction *new_instruction = ir_build_union_field_ptr(irb, old_instruction->scope, | |
| 949 | old_instruction->source_node, union_ptr, type_union_field); | |
| 950 | ir_link_new_instruction(new_instruction, old_instruction); | |
| 951 | return new_instruction; | |
| 952 | } | |
| 953 | ||
| 925 | 954 | static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 926 | 955 | FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, |
| 927 | 956 | bool is_comptime, bool is_inline) |
| ... | ... | @@ -1112,6 +1141,28 @@ static IrInstruction *ir_build_struct_init_from(IrBuilder *irb, IrInstruction *o |
| 1112 | 1141 | return new_instruction; |
| 1113 | 1142 | } |
| 1114 | 1143 | |
| 1144 | static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 1145 | TypeTableEntry *union_type, TypeUnionField *field, IrInstruction *init_value) | |
| 1146 | { | |
| 1147 | IrInstructionUnionInit *union_init_instruction = ir_build_instruction<IrInstructionUnionInit>(irb, scope, source_node); | |
| 1148 | union_init_instruction->union_type = union_type; | |
| 1149 | union_init_instruction->field = field; | |
| 1150 | union_init_instruction->init_value = init_value; | |
| 1151 | ||
| 1152 | ir_ref_instruction(init_value, irb->current_basic_block); | |
| 1153 | ||
| 1154 | return &union_init_instruction->base; | |
| 1155 | } | |
| 1156 | ||
| 1157 | static IrInstruction *ir_build_union_init_from(IrBuilder *irb, IrInstruction *old_instruction, | |
| 1158 | TypeTableEntry *union_type, TypeUnionField *field, IrInstruction *init_value) | |
| 1159 | { | |
| 1160 | IrInstruction *new_instruction = ir_build_union_init(irb, old_instruction->scope, | |
| 1161 | old_instruction->source_node, union_type, field, init_value); | |
| 1162 | ir_link_new_instruction(new_instruction, old_instruction); | |
| 1163 | return new_instruction; | |
| 1164 | } | |
| 1165 | ||
| 1115 | 1166 | static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 1116 | 1167 | IrInstructionUnreachable *unreachable_instruction = |
| 1117 | 1168 | ir_build_instruction<IrInstructionUnreachable>(irb, scope, source_node); |
| ... | ... | @@ -2422,6 +2473,13 @@ static IrInstruction *ir_instruction_enumfieldptr_get_dep(IrInstructionEnumField |
| 2422 | 2473 | } |
| 2423 | 2474 | } |
| 2424 | 2475 | |
| 2476 | static IrInstruction *ir_instruction_unionfieldptr_get_dep(IrInstructionUnionFieldPtr *instruction, size_t index) { | |
| 2477 | switch (index) { | |
| 2478 | case 0: return instruction->union_ptr; | |
| 2479 | default: return nullptr; | |
| 2480 | } | |
| 2481 | } | |
| 2482 | ||
| 2425 | 2483 | static IrInstruction *ir_instruction_elemptr_get_dep(IrInstructionElemPtr *instruction, size_t index) { |
| 2426 | 2484 | switch (index) { |
| 2427 | 2485 | case 0: return instruction->array_ptr; |
| ... | ... | @@ -2485,6 +2543,13 @@ static IrInstruction *ir_instruction_structinit_get_dep(IrInstructionStructInit |
| 2485 | 2543 | return nullptr; |
| 2486 | 2544 | } |
| 2487 | 2545 | |
| 2546 | static IrInstruction *ir_instruction_unioninit_get_dep(IrInstructionUnionInit *instruction, size_t index) { | |
| 2547 | switch (index) { | |
| 2548 | case 0: return instruction->init_value; | |
| 2549 | default: return nullptr; | |
| 2550 | } | |
| 2551 | } | |
| 2552 | ||
| 2488 | 2553 | static IrInstruction *ir_instruction_unreachable_get_dep(IrInstructionUnreachable *instruction, size_t index) { |
| 2489 | 2554 | return nullptr; |
| 2490 | 2555 | } |
| ... | ... | @@ -3099,6 +3164,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 3099 | 3164 | return ir_instruction_structfieldptr_get_dep((IrInstructionStructFieldPtr *) instruction, index); |
| 3100 | 3165 | case IrInstructionIdEnumFieldPtr: |
| 3101 | 3166 | return ir_instruction_enumfieldptr_get_dep((IrInstructionEnumFieldPtr *) instruction, index); |
| 3167 | case IrInstructionIdUnionFieldPtr: | |
| 3168 | return ir_instruction_unionfieldptr_get_dep((IrInstructionUnionFieldPtr *) instruction, index); | |
| 3102 | 3169 | case IrInstructionIdElemPtr: |
| 3103 | 3170 | return ir_instruction_elemptr_get_dep((IrInstructionElemPtr *) instruction, index); |
| 3104 | 3171 | case IrInstructionIdVarPtr: |
| ... | ... | @@ -3117,6 +3184,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 3117 | 3184 | return ir_instruction_containerinitfields_get_dep((IrInstructionContainerInitFields *) instruction, index); |
| 3118 | 3185 | case IrInstructionIdStructInit: |
| 3119 | 3186 | return ir_instruction_structinit_get_dep((IrInstructionStructInit *) instruction, index); |
| 3187 | case IrInstructionIdUnionInit: | |
| 3188 | return ir_instruction_unioninit_get_dep((IrInstructionUnionInit *) instruction, index); | |
| 3120 | 3189 | case IrInstructionIdUnreachable: |
| 3121 | 3190 | return ir_instruction_unreachable_get_dep((IrInstructionUnreachable *) instruction, index); |
| 3122 | 3191 | case IrInstructionIdTypeOf: |
| ... | ... | @@ -11417,8 +11486,20 @@ static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira, |
| 11417 | 11486 | return ir_analyze_ref(ira, &field_ptr_instruction->base, bound_fn_value, true, false); |
| 11418 | 11487 | } |
| 11419 | 11488 | } |
| 11489 | const char *prefix_name; | |
| 11490 | if (is_slice(bare_struct_type)) { | |
| 11491 | prefix_name = ""; | |
| 11492 | } else if (bare_struct_type->id == TypeTableEntryIdStruct) { | |
| 11493 | prefix_name = "struct "; | |
| 11494 | } else if (bare_struct_type->id == TypeTableEntryIdEnum) { | |
| 11495 | prefix_name = "enum "; | |
| 11496 | } else if (bare_struct_type->id == TypeTableEntryIdUnion) { | |
| 11497 | prefix_name = "union "; | |
| 11498 | } else { | |
| 11499 | prefix_name = ""; | |
| 11500 | } | |
| 11420 | 11501 | ir_add_error_node(ira, field_ptr_instruction->base.source_node, |
| 11421 | buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); | |
| 11502 | buf_sprintf("no member named '%s' in %s'%s'", buf_ptr(field_name), prefix_name, buf_ptr(&bare_struct_type->name))); | |
| 11422 | 11503 | return ira->codegen->builtin_types.entry_invalid; |
| 11423 | 11504 | } |
| 11424 | 11505 | |
| ... | ... | @@ -11428,14 +11509,13 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field |
| 11428 | 11509 | { |
| 11429 | 11510 | TypeTableEntry *bare_type = container_ref_type(container_type); |
| 11430 | 11511 | ensure_complete_type(ira->codegen, bare_type); |
| 11512 | if (type_is_invalid(bare_type)) | |
| 11513 | return ira->codegen->builtin_types.entry_invalid; | |
| 11431 | 11514 | |
| 11432 | 11515 | assert(container_ptr->value.type->id == TypeTableEntryIdPointer); |
| 11433 | 11516 | bool is_const = container_ptr->value.type->data.pointer.is_const; |
| 11434 | 11517 | bool is_volatile = container_ptr->value.type->data.pointer.is_volatile; |
| 11435 | 11518 | if (bare_type->id == TypeTableEntryIdStruct) { |
| 11436 | if (bare_type->data.structure.is_invalid) | |
| 11437 | return ira->codegen->builtin_types.entry_invalid; | |
| 11438 | ||
| 11439 | 11519 | TypeStructField *field = find_struct_type_field(bare_type, field_name); |
| 11440 | 11520 | if (field) { |
| 11441 | 11521 | bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked); |
| ... | ... | @@ -11476,9 +11556,6 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field |
| 11476 | 11556 | field_ptr_instruction, container_ptr, container_type); |
| 11477 | 11557 | } |
| 11478 | 11558 | } else if (bare_type->id == TypeTableEntryIdEnum) { |
| 11479 | if (bare_type->data.enumeration.is_invalid) | |
| 11480 | return ira->codegen->builtin_types.entry_invalid; | |
| 11481 | ||
| 11482 | 11559 | TypeEnumField *field = find_enum_type_field(bare_type, field_name); |
| 11483 | 11560 | if (field) { |
| 11484 | 11561 | ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); |
| ... | ... | @@ -11489,7 +11566,15 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field |
| 11489 | 11566 | field_ptr_instruction, container_ptr, container_type); |
| 11490 | 11567 | } |
| 11491 | 11568 | } else if (bare_type->id == TypeTableEntryIdUnion) { |
| 11492 | zig_panic("TODO"); | |
| 11569 | TypeUnionField *field = find_union_type_field(bare_type, field_name); | |
| 11570 | if (field) { | |
| 11571 | ir_build_union_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); | |
| 11572 | return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, | |
| 11573 | get_abi_alignment(ira->codegen, field->type_entry), 0, 0); | |
| 11574 | } else { | |
| 11575 | return ir_analyze_container_member_access_inner(ira, bare_type, field_name, | |
| 11576 | field_ptr_instruction, container_ptr, container_type); | |
| 11577 | } | |
| 11493 | 11578 | } else { |
| 11494 | 11579 | zig_unreachable(); |
| 11495 | 11580 | } |
| ... | ... | @@ -13033,9 +13118,71 @@ static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionR |
| 13033 | 13118 | return ir_analyze_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile); |
| 13034 | 13119 | } |
| 13035 | 13120 | |
| 13121 | static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction, | |
| 13122 | TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields) | |
| 13123 | { | |
| 13124 | assert(container_type->id == TypeTableEntryIdUnion); | |
| 13125 | ||
| 13126 | ensure_complete_type(ira->codegen, container_type); | |
| 13127 | ||
| 13128 | if (instr_field_count != 1) { | |
| 13129 | ir_add_error(ira, instruction, | |
| 13130 | buf_sprintf("union initialization expects exactly one field")); | |
| 13131 | return ira->codegen->builtin_types.entry_invalid; | |
| 13132 | } | |
| 13133 | ||
| 13134 | IrInstructionContainerInitFieldsField *field = &fields[0]; | |
| 13135 | IrInstruction *field_value = field->value->other; | |
| 13136 | if (type_is_invalid(field_value->value.type)) | |
| 13137 | return ira->codegen->builtin_types.entry_invalid; | |
| 13138 | ||
| 13139 | TypeUnionField *type_field = find_union_type_field(container_type, field->name); | |
| 13140 | if (!type_field) { | |
| 13141 | ir_add_error_node(ira, field->source_node, | |
| 13142 | buf_sprintf("no member named '%s' in union '%s'", | |
| 13143 | buf_ptr(field->name), buf_ptr(&container_type->name))); | |
| 13144 | return ira->codegen->builtin_types.entry_invalid; | |
| 13145 | } | |
| 13146 | ||
| 13147 | if (type_is_invalid(type_field->type_entry)) | |
| 13148 | return ira->codegen->builtin_types.entry_invalid; | |
| 13149 | ||
| 13150 | IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry); | |
| 13151 | if (casted_field_value == ira->codegen->invalid_instruction) | |
| 13152 | return ira->codegen->builtin_types.entry_invalid; | |
| 13153 | ||
| 13154 | bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); | |
| 13155 | if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) { | |
| 13156 | ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); | |
| 13157 | if (!field_val) | |
| 13158 | return ira->codegen->builtin_types.entry_invalid; | |
| 13159 | ||
| 13160 | ConstExprValue *out_val = ir_build_const_from(ira, instruction); | |
| 13161 | out_val->data.x_union.payload = field_val; | |
| 13162 | out_val->data.x_union.tag = type_field->value; | |
| 13163 | ||
| 13164 | ConstParent *parent = get_const_val_parent(ira->codegen, field_val); | |
| 13165 | if (parent != nullptr) { | |
| 13166 | parent->id = ConstParentIdUnion; | |
| 13167 | parent->data.p_union.union_val = out_val; | |
| 13168 | } | |
| 13169 | ||
| 13170 | return container_type; | |
| 13171 | } | |
| 13172 | ||
| 13173 | IrInstruction *new_instruction = ir_build_union_init_from(&ira->new_irb, instruction, | |
| 13174 | container_type, type_field, casted_field_value); | |
| 13175 | ||
| 13176 | ir_add_alloca(ira, new_instruction, container_type); | |
| 13177 | return container_type; | |
| 13178 | } | |
| 13179 | ||
| 13036 | 13180 | static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction, |
| 13037 | 13181 | TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields) |
| 13038 | 13182 | { |
| 13183 | if (container_type->id == TypeTableEntryIdUnion) { | |
| 13184 | return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields); | |
| 13185 | } | |
| 13039 | 13186 | if (container_type->id != TypeTableEntryIdStruct || is_slice(container_type)) { |
| 13040 | 13187 | ir_add_error(ira, instruction, |
| 13041 | 13188 | buf_sprintf("type '%s' does not support struct initialization syntax", |
| ... | ... | @@ -13043,8 +13190,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru |
| 13043 | 13190 | return ira->codegen->builtin_types.entry_invalid; |
| 13044 | 13191 | } |
| 13045 | 13192 | |
| 13046 | if (!type_is_complete(container_type)) | |
| 13047 | resolve_container_type(ira->codegen, container_type); | |
| 13193 | ensure_complete_type(ira->codegen, container_type); | |
| 13048 | 13194 | |
| 13049 | 13195 | size_t actual_field_count = container_type->data.structure.src_field_count; |
| 13050 | 13196 | |
| ... | ... | @@ -13070,7 +13216,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru |
| 13070 | 13216 | TypeStructField *type_field = find_struct_type_field(container_type, field->name); |
| 13071 | 13217 | if (!type_field) { |
| 13072 | 13218 | ir_add_error_node(ira, field->source_node, |
| 13073 | buf_sprintf("no member named '%s' in '%s'", | |
| 13219 | buf_sprintf("no member named '%s' in struct '%s'", | |
| 13074 | 13220 | buf_ptr(field->name), buf_ptr(&container_type->name))); |
| 13075 | 13221 | return ira->codegen->builtin_types.entry_invalid; |
| 13076 | 13222 | } |
| ... | ... | @@ -15657,8 +15803,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 15657 | 15803 | case IrInstructionIdIntToErr: |
| 15658 | 15804 | case IrInstructionIdErrToInt: |
| 15659 | 15805 | case IrInstructionIdStructInit: |
| 15806 | case IrInstructionIdUnionInit: | |
| 15660 | 15807 | case IrInstructionIdStructFieldPtr: |
| 15661 | 15808 | case IrInstructionIdEnumFieldPtr: |
| 15809 | case IrInstructionIdUnionFieldPtr: | |
| 15662 | 15810 | case IrInstructionIdInitEnum: |
| 15663 | 15811 | case IrInstructionIdMaybeWrap: |
| 15664 | 15812 | case IrInstructionIdErrWrapCode: |
| ... | ... | @@ -15968,6 +16116,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 15968 | 16116 | case IrInstructionIdContainerInitList: |
| 15969 | 16117 | case IrInstructionIdContainerInitFields: |
| 15970 | 16118 | case IrInstructionIdStructInit: |
| 16119 | case IrInstructionIdUnionInit: | |
| 15971 | 16120 | case IrInstructionIdFieldPtr: |
| 15972 | 16121 | case IrInstructionIdElemPtr: |
| 15973 | 16122 | case IrInstructionIdVarPtr: |
| ... | ... | @@ -15977,6 +16126,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 15977 | 16126 | case IrInstructionIdArrayLen: |
| 15978 | 16127 | case IrInstructionIdStructFieldPtr: |
| 15979 | 16128 | case IrInstructionIdEnumFieldPtr: |
| 16129 | case IrInstructionIdUnionFieldPtr: | |
| 15980 | 16130 | case IrInstructionIdArrayType: |
| 15981 | 16131 | case IrInstructionIdSliceType: |
| 15982 | 16132 | case IrInstructionIdSizeOf: |
src/ir_print.cpp+22| ... | ... | @@ -290,6 +290,15 @@ static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruct |
| 290 | 290 | fprintf(irp->f, "} // struct init"); |
| 291 | 291 | } |
| 292 | 292 | |
| 293 | static void ir_print_union_init(IrPrint *irp, IrInstructionUnionInit *instruction) { | |
| 294 | Buf *field_name = instruction->field->name; | |
| 295 | ||
| 296 | fprintf(irp->f, "%s {", buf_ptr(&instruction->union_type->name)); | |
| 297 | fprintf(irp->f, ".%s = ", buf_ptr(field_name)); | |
| 298 | ir_print_other_instruction(irp, instruction->init_value); | |
| 299 | fprintf(irp->f, "} // union init"); | |
| 300 | } | |
| 301 | ||
| 293 | 302 | static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) { |
| 294 | 303 | fprintf(irp->f, "unreachable"); |
| 295 | 304 | } |
| ... | ... | @@ -359,6 +368,13 @@ static void ir_print_enum_field_ptr(IrPrint *irp, IrInstructionEnumFieldPtr *ins |
| 359 | 368 | fprintf(irp->f, ")"); |
| 360 | 369 | } |
| 361 | 370 | |
| 371 | static void ir_print_union_field_ptr(IrPrint *irp, IrInstructionUnionFieldPtr *instruction) { | |
| 372 | fprintf(irp->f, "@UnionFieldPtr(&"); | |
| 373 | ir_print_other_instruction(irp, instruction->union_ptr); | |
| 374 | fprintf(irp->f, ".%s", buf_ptr(instruction->field->name)); | |
| 375 | fprintf(irp->f, ")"); | |
| 376 | } | |
| 377 | ||
| 362 | 378 | static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) { |
| 363 | 379 | fprintf(irp->f, "@setDebugSafety("); |
| 364 | 380 | ir_print_other_instruction(irp, instruction->scope_value); |
| ... | ... | @@ -1023,6 +1039,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1023 | 1039 | case IrInstructionIdStructInit: |
| 1024 | 1040 | ir_print_struct_init(irp, (IrInstructionStructInit *)instruction); |
| 1025 | 1041 | break; |
| 1042 | case IrInstructionIdUnionInit: | |
| 1043 | ir_print_union_init(irp, (IrInstructionUnionInit *)instruction); | |
| 1044 | break; | |
| 1026 | 1045 | case IrInstructionIdUnreachable: |
| 1027 | 1046 | ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction); |
| 1028 | 1047 | break; |
| ... | ... | @@ -1056,6 +1075,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1056 | 1075 | case IrInstructionIdEnumFieldPtr: |
| 1057 | 1076 | ir_print_enum_field_ptr(irp, (IrInstructionEnumFieldPtr *)instruction); |
| 1058 | 1077 | break; |
| 1078 | case IrInstructionIdUnionFieldPtr: | |
| 1079 | ir_print_union_field_ptr(irp, (IrInstructionUnionFieldPtr *)instruction); | |
| 1080 | break; | |
| 1059 | 1081 | case IrInstructionIdSetDebugSafety: |
| 1060 | 1082 | ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction); |
| 1061 | 1083 | break; |
src/zig_llvm.cpp+4| ... | ... | @@ -403,6 +403,10 @@ unsigned ZigLLVMTag_DW_structure_type(void) { |
| 403 | 403 | return dwarf::DW_TAG_structure_type; |
| 404 | 404 | } |
| 405 | 405 | |
| 406 | unsigned ZigLLVMTag_DW_union_type(void) { | |
| 407 | return dwarf::DW_TAG_union_type; | |
| 408 | } | |
| 409 | ||
| 406 | 410 | ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved) { |
| 407 | 411 | DIBuilder *di_builder = new DIBuilder(*unwrap(module), allow_unresolved); |
| 408 | 412 | return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder); |
src/zig_llvm.hpp+1| ... | ... | @@ -117,6 +117,7 @@ unsigned ZigLLVMEncoding_DW_ATE_signed_char(void); |
| 117 | 117 | unsigned ZigLLVMLang_DW_LANG_C99(void); |
| 118 | 118 | unsigned ZigLLVMTag_DW_variable(void); |
| 119 | 119 | unsigned ZigLLVMTag_DW_structure_type(void); |
| 120 | unsigned ZigLLVMTag_DW_union_type(void); | |
| 120 | 121 | |
| 121 | 122 | ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved); |
| 122 | 123 | void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module); |
test/cases/union.zig+44| ... | ... | @@ -31,3 +31,47 @@ test "unions embedded in aggregate types" { |
| 31 | 31 | else => unreachable, |
| 32 | 32 | } |
| 33 | 33 | } |
| 34 | ||
| 35 | ||
| 36 | const Foo = union { | |
| 37 | float: f64, | |
| 38 | int: i32, | |
| 39 | }; | |
| 40 | ||
| 41 | test "basic unions" { | |
| 42 | var foo = Foo { .int = 1 }; | |
| 43 | assert(foo.int == 1); | |
| 44 | foo = Foo {.float = 12.34}; | |
| 45 | assert(foo.float == 12.34); | |
| 46 | } | |
| 47 | ||
| 48 | test "init union with runtime value" { | |
| 49 | var foo: Foo = undefined; | |
| 50 | ||
| 51 | setFloat(&foo, 12.34); | |
| 52 | assert(foo.float == 12.34); | |
| 53 | ||
| 54 | setInt(&foo, 42); | |
| 55 | assert(foo.int == 42); | |
| 56 | } | |
| 57 | ||
| 58 | fn setFloat(foo: &Foo, x: f64) { | |
| 59 | *foo = Foo { .float = x }; | |
| 60 | } | |
| 61 | ||
| 62 | fn setInt(foo: &Foo, x: i32) { | |
| 63 | *foo = Foo { .int = x }; | |
| 64 | } | |
| 65 | ||
| 66 | const FooExtern = extern union { | |
| 67 | float: f64, | |
| 68 | int: i32, | |
| 69 | }; | |
| 70 | ||
| 71 | test "basic extern unions" { | |
| 72 | var foo = FooExtern { .int = 1 }; | |
| 73 | assert(foo.int == 1); | |
| 74 | foo.float = 12.34; | |
| 75 | assert(foo.float == 12.34); | |
| 76 | } | |
| 77 |
test/compile_errors.zig+3-3| ... | ... | @@ -389,8 +389,8 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 389 | 389 | \\ const y = a.bar; |
| 390 | 390 | \\} |
| 391 | 391 | , |
| 392 | ".tmp_source.zig:4:6: error: no member named 'foo' in 'A'", | |
| 393 | ".tmp_source.zig:5:16: error: no member named 'bar' in 'A'"); | |
| 392 | ".tmp_source.zig:4:6: error: no member named 'foo' in struct 'A'", | |
| 393 | ".tmp_source.zig:5:16: error: no member named 'bar' in struct 'A'"); | |
| 394 | 394 | |
| 395 | 395 | cases.add("redefinition of struct", |
| 396 | 396 | \\const A = struct { x : i32, }; |
| ... | ... | @@ -454,7 +454,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 454 | 454 | \\ .foo = 42, |
| 455 | 455 | \\ }; |
| 456 | 456 | \\} |
| 457 | , ".tmp_source.zig:10:9: error: no member named 'foo' in 'A'"); | |
| 457 | , ".tmp_source.zig:10:9: error: no member named 'foo' in struct 'A'"); | |
| 458 | 458 | |
| 459 | 459 | cases.add("invalid break expression", |
| 460 | 460 | \\export fn f() { |
test/debug_safety.zig+20| ... | ... | @@ -260,4 +260,24 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 260 | 260 | \\ return int_slice[0]; |
| 261 | 261 | \\} |
| 262 | 262 | ); |
| 263 | ||
| 264 | cases.addDebugSafety("bad union field access", | |
| 265 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 266 | \\ @import("std").os.exit(126); | |
| 267 | \\} | |
| 268 | \\ | |
| 269 | \\const Foo = union { | |
| 270 | \\ float: f32, | |
| 271 | \\ int: u32, | |
| 272 | \\}; | |
| 273 | \\ | |
| 274 | \\pub fn main() -> %void { | |
| 275 | \\ var f = Foo { .int = 42 }; | |
| 276 | \\ bar(&f); | |
| 277 | \\} | |
| 278 | \\ | |
| 279 | \\fn bar(f: &Foo) { | |
| 280 | \\ f.float = 12.34; | |
| 281 | \\} | |
| 282 | ); | |
| 263 | 283 | } |