| author | |
| committer | |
| log | 634e8713c394bacfe080d03256d1dd4f9a43dd8c |
| tree | 8fa396aa96acfa556217f83eb90ecb6a14d88c4d |
| parent | f0dafd3f209a342f055850108651545bea9b065b |
see #383
there is a plan to unify most of the reflection into 2
builtin functions, as outlined in the above issue,
but this gives us needed features for now, and we can
iterate on the design in future commits8 files changed, 440 insertions(+), 111 deletions(-)
src-self-hosted/main.zig-1| ... | ... | @@ -5,7 +5,6 @@ const heap = @import("std").mem; |
| 5 | 5 | |
| 6 | 6 | // TODO: sync up CLI with c++ code |
| 7 | 7 | // TODO: concurrency |
| 8 | // TODO: ability to iterate over enums at compile time (for listing targets) | |
| 9 | 8 | |
| 10 | 9 | error InvalidArgument; |
| 11 | 10 | error MissingArg0; |
src/all_types.hpp+18| ... | ... | @@ -1211,6 +1211,8 @@ enum BuiltinFnId { |
| 1211 | 1211 | BuiltinFnIdMaxValue, |
| 1212 | 1212 | BuiltinFnIdMinValue, |
| 1213 | 1213 | BuiltinFnIdMemberCount, |
| 1214 | BuiltinFnIdMemberType, | |
| 1215 | BuiltinFnIdMemberName, | |
| 1214 | 1216 | BuiltinFnIdTypeof, |
| 1215 | 1217 | BuiltinFnIdAddWithOverflow, |
| 1216 | 1218 | BuiltinFnIdSubWithOverflow, |
| ... | ... | @@ -1845,6 +1847,8 @@ enum IrInstructionId { |
| 1845 | 1847 | IrInstructionIdMemcpy, |
| 1846 | 1848 | IrInstructionIdSlice, |
| 1847 | 1849 | IrInstructionIdMemberCount, |
| 1850 | IrInstructionIdMemberType, | |
| 1851 | IrInstructionIdMemberName, | |
| 1848 | 1852 | IrInstructionIdBreakpoint, |
| 1849 | 1853 | IrInstructionIdReturnAddress, |
| 1850 | 1854 | IrInstructionIdFrameAddress, |
| ... | ... | @@ -2408,6 +2412,20 @@ struct IrInstructionMemberCount { |
| 2408 | 2412 | IrInstruction *container; |
| 2409 | 2413 | }; |
| 2410 | 2414 | |
| 2415 | struct IrInstructionMemberType { | |
| 2416 | IrInstruction base; | |
| 2417 | ||
| 2418 | IrInstruction *container_type; | |
| 2419 | IrInstruction *member_index; | |
| 2420 | }; | |
| 2421 | ||
| 2422 | struct IrInstructionMemberName { | |
| 2423 | IrInstruction base; | |
| 2424 | ||
| 2425 | IrInstruction *container_type; | |
| 2426 | IrInstruction *member_index; | |
| 2427 | }; | |
| 2428 | ||
| 2411 | 2429 | struct IrInstructionBreakpoint { |
| 2412 | 2430 | IrInstruction base; |
| 2413 | 2431 | }; |
src/analyze.cpp+133-110| ... | ... | @@ -1366,119 +1366,140 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { |
| 1366 | 1366 | enum_type->data.enumeration.union_size_bytes = biggest_size_in_bits / 8; |
| 1367 | 1367 | enum_type->data.enumeration.most_aligned_union_member = most_aligned_union_member; |
| 1368 | 1368 | |
| 1369 | if (!enum_type->data.enumeration.is_invalid) { | |
| 1370 | TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); | |
| 1371 | TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); | |
| 1372 | enum_type->data.enumeration.tag_type = tag_type_entry; | |
| 1373 | ||
| 1374 | uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); | |
| 1375 | ||
| 1376 | if (most_aligned_union_member) { | |
| 1377 | // create llvm type for union | |
| 1378 | uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; | |
| 1379 | LLVMTypeRef union_type_ref; | |
| 1380 | if (padding_in_bits > 0) { | |
| 1381 | TypeTableEntry *u8_type = get_int_type(g, false, 8); | |
| 1382 | TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); | |
| 1383 | LLVMTypeRef union_element_types[] = { | |
| 1384 | most_aligned_union_member->type_ref, | |
| 1385 | padding_array->type_ref, | |
| 1386 | }; | |
| 1387 | union_type_ref = LLVMStructType(union_element_types, 2, false); | |
| 1388 | } else { | |
| 1389 | union_type_ref = most_aligned_union_member->type_ref; | |
| 1390 | } | |
| 1391 | enum_type->data.enumeration.union_type_ref = union_type_ref; | |
| 1369 | if (enum_type->data.enumeration.is_invalid) | |
| 1370 | return; | |
| 1392 | 1371 | |
| 1393 | assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits); | |
| 1394 | assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits); | |
| 1372 | if (enum_type->zero_bits) { | |
| 1373 | enum_type->type_ref = LLVMVoidType(); | |
| 1395 | 1374 | |
| 1396 | if (align_of_tag_in_bits >= biggest_align_in_bits) { | |
| 1397 | enum_type->data.enumeration.gen_tag_index = 0; | |
| 1398 | enum_type->data.enumeration.gen_union_index = 1; | |
| 1399 | } else { | |
| 1400 | enum_type->data.enumeration.gen_union_index = 0; | |
| 1401 | enum_type->data.enumeration.gen_tag_index = 1; | |
| 1402 | } | |
| 1375 | uint64_t debug_size_in_bits = 0; | |
| 1376 | uint64_t debug_align_in_bits = 0; | |
| 1377 | ZigLLVMDIType **di_root_members = nullptr; | |
| 1378 | size_t debug_member_count = 0; | |
| 1379 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, | |
| 1380 | ZigLLVMFileToScope(import->di_file), | |
| 1381 | buf_ptr(&enum_type->name), | |
| 1382 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1383 | debug_size_in_bits, | |
| 1384 | debug_align_in_bits, | |
| 1385 | 0, nullptr, di_root_members, (int)debug_member_count, 0, nullptr, ""); | |
| 1403 | 1386 | |
| 1404 | // create llvm type for root struct | |
| 1405 | LLVMTypeRef root_struct_element_types[2]; | |
| 1406 | root_struct_element_types[enum_type->data.enumeration.gen_tag_index] = tag_type_entry->type_ref; | |
| 1407 | root_struct_element_types[enum_type->data.enumeration.gen_union_index] = union_type_ref; | |
| 1408 | LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false); | |
| 1409 | ||
| 1410 | // create debug type for tag | |
| 1411 | uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 1412 | uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 1413 | ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, | |
| 1414 | ZigLLVMTypeToScope(enum_type->di_type), "AnonEnum", | |
| 1415 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1416 | tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, | |
| 1417 | tag_type_entry->di_type, ""); | |
| 1418 | ||
| 1419 | // create debug type for union | |
| 1420 | ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, | |
| 1421 | ZigLLVMTypeToScope(enum_type->di_type), "AnonUnion", | |
| 1422 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1423 | biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, | |
| 1424 | gen_field_count, 0, ""); | |
| 1425 | ||
| 1426 | // create debug types for members of root struct | |
| 1427 | uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, | |
| 1428 | enum_type->data.enumeration.gen_tag_index); | |
| 1429 | ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, | |
| 1430 | ZigLLVMTypeToScope(enum_type->di_type), "tag_field", | |
| 1431 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1432 | tag_debug_size_in_bits, | |
| 1433 | tag_debug_align_in_bits, | |
| 1434 | tag_offset_in_bits, | |
| 1435 | 0, tag_di_type); | |
| 1436 | ||
| 1437 | uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, | |
| 1438 | enum_type->data.enumeration.gen_union_index); | |
| 1439 | ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, | |
| 1440 | ZigLLVMTypeToScope(enum_type->di_type), "union_field", | |
| 1441 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1442 | biggest_size_in_bits, | |
| 1443 | biggest_align_in_bits, | |
| 1444 | union_offset_in_bits, | |
| 1445 | 0, union_di_type); | |
| 1446 | ||
| 1447 | // create debug type for root struct | |
| 1448 | ZigLLVMDIType *di_root_members[2]; | |
| 1449 | di_root_members[enum_type->data.enumeration.gen_tag_index] = tag_member_di_type; | |
| 1450 | di_root_members[enum_type->data.enumeration.gen_union_index] = union_member_di_type; | |
| 1451 | ||
| 1452 | uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref); | |
| 1453 | uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref); | |
| 1454 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, | |
| 1455 | ZigLLVMFileToScope(import->di_file), | |
| 1456 | buf_ptr(&enum_type->name), | |
| 1457 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1458 | debug_size_in_bits, | |
| 1459 | debug_align_in_bits, | |
| 1460 | 0, nullptr, di_root_members, 2, 0, nullptr, ""); | |
| 1461 | ||
| 1462 | ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type); | |
| 1463 | enum_type->di_type = replacement_di_type; | |
| 1387 | ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type); | |
| 1388 | enum_type->di_type = replacement_di_type; | |
| 1389 | return; | |
| 1390 | } | |
| 1391 | ||
| 1392 | TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); | |
| 1393 | TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); | |
| 1394 | enum_type->data.enumeration.tag_type = tag_type_entry; | |
| 1395 | ||
| 1396 | uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); | |
| 1397 | ||
| 1398 | if (most_aligned_union_member) { | |
| 1399 | // create llvm type for union | |
| 1400 | uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; | |
| 1401 | LLVMTypeRef union_type_ref; | |
| 1402 | if (padding_in_bits > 0) { | |
| 1403 | TypeTableEntry *u8_type = get_int_type(g, false, 8); | |
| 1404 | TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); | |
| 1405 | LLVMTypeRef union_element_types[] = { | |
| 1406 | most_aligned_union_member->type_ref, | |
| 1407 | padding_array->type_ref, | |
| 1408 | }; | |
| 1409 | union_type_ref = LLVMStructType(union_element_types, 2, false); | |
| 1464 | 1410 | } else { |
| 1465 | // create llvm type for root struct | |
| 1466 | enum_type->type_ref = tag_type_entry->type_ref; | |
| 1467 | ||
| 1468 | // create debug type for tag | |
| 1469 | uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 1470 | uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 1471 | ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, | |
| 1472 | ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), | |
| 1473 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1474 | tag_debug_size_in_bits, | |
| 1475 | tag_debug_align_in_bits, | |
| 1476 | di_enumerators, field_count, | |
| 1477 | tag_type_entry->di_type, ""); | |
| 1411 | union_type_ref = most_aligned_union_member->type_ref; | |
| 1412 | } | |
| 1413 | enum_type->data.enumeration.union_type_ref = union_type_ref; | |
| 1478 | 1414 | |
| 1479 | ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); | |
| 1480 | enum_type->di_type = tag_di_type; | |
| 1415 | assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits); | |
| 1416 | assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits); | |
| 1417 | ||
| 1418 | if (align_of_tag_in_bits >= biggest_align_in_bits) { | |
| 1419 | enum_type->data.enumeration.gen_tag_index = 0; | |
| 1420 | enum_type->data.enumeration.gen_union_index = 1; | |
| 1421 | } else { | |
| 1422 | enum_type->data.enumeration.gen_union_index = 0; | |
| 1423 | enum_type->data.enumeration.gen_tag_index = 1; | |
| 1481 | 1424 | } |
| 1425 | ||
| 1426 | // create llvm type for root struct | |
| 1427 | LLVMTypeRef root_struct_element_types[2]; | |
| 1428 | root_struct_element_types[enum_type->data.enumeration.gen_tag_index] = tag_type_entry->type_ref; | |
| 1429 | root_struct_element_types[enum_type->data.enumeration.gen_union_index] = union_type_ref; | |
| 1430 | LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false); | |
| 1431 | ||
| 1432 | // create debug type for tag | |
| 1433 | uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 1434 | uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 1435 | ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, | |
| 1436 | ZigLLVMTypeToScope(enum_type->di_type), "AnonEnum", | |
| 1437 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1438 | tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, | |
| 1439 | tag_type_entry->di_type, ""); | |
| 1440 | ||
| 1441 | // create debug type for union | |
| 1442 | ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, | |
| 1443 | ZigLLVMTypeToScope(enum_type->di_type), "AnonUnion", | |
| 1444 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1445 | biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, | |
| 1446 | gen_field_count, 0, ""); | |
| 1447 | ||
| 1448 | // create debug types for members of root struct | |
| 1449 | uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, | |
| 1450 | enum_type->data.enumeration.gen_tag_index); | |
| 1451 | ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, | |
| 1452 | ZigLLVMTypeToScope(enum_type->di_type), "tag_field", | |
| 1453 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1454 | tag_debug_size_in_bits, | |
| 1455 | tag_debug_align_in_bits, | |
| 1456 | tag_offset_in_bits, | |
| 1457 | 0, tag_di_type); | |
| 1458 | ||
| 1459 | uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, | |
| 1460 | enum_type->data.enumeration.gen_union_index); | |
| 1461 | ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, | |
| 1462 | ZigLLVMTypeToScope(enum_type->di_type), "union_field", | |
| 1463 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1464 | biggest_size_in_bits, | |
| 1465 | biggest_align_in_bits, | |
| 1466 | union_offset_in_bits, | |
| 1467 | 0, union_di_type); | |
| 1468 | ||
| 1469 | // create debug type for root struct | |
| 1470 | ZigLLVMDIType *di_root_members[2]; | |
| 1471 | di_root_members[enum_type->data.enumeration.gen_tag_index] = tag_member_di_type; | |
| 1472 | di_root_members[enum_type->data.enumeration.gen_union_index] = union_member_di_type; | |
| 1473 | ||
| 1474 | uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref); | |
| 1475 | uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref); | |
| 1476 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, | |
| 1477 | ZigLLVMFileToScope(import->di_file), | |
| 1478 | buf_ptr(&enum_type->name), | |
| 1479 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1480 | debug_size_in_bits, | |
| 1481 | debug_align_in_bits, | |
| 1482 | 0, nullptr, di_root_members, 2, 0, nullptr, ""); | |
| 1483 | ||
| 1484 | ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type); | |
| 1485 | enum_type->di_type = replacement_di_type; | |
| 1486 | } else { | |
| 1487 | // create llvm type for root struct | |
| 1488 | enum_type->type_ref = tag_type_entry->type_ref; | |
| 1489 | ||
| 1490 | // create debug type for tag | |
| 1491 | uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 1492 | uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); | |
| 1493 | ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, | |
| 1494 | ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), | |
| 1495 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1496 | tag_debug_size_in_bits, | |
| 1497 | tag_debug_align_in_bits, | |
| 1498 | di_enumerators, field_count, | |
| 1499 | tag_type_entry->di_type, ""); | |
| 1500 | ||
| 1501 | ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); | |
| 1502 | enum_type->di_type = tag_di_type; | |
| 1482 | 1503 | } |
| 1483 | 1504 | } |
| 1484 | 1505 | |
| ... | ... | @@ -1875,9 +1896,11 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { |
| 1875 | 1896 | enum_type->data.enumeration.zero_bits_known = true; |
| 1876 | 1897 | |
| 1877 | 1898 | // also compute abi_alignment |
| 1878 | TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); | |
| 1879 | uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); | |
| 1880 | enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes); | |
| 1899 | if (!enum_type->zero_bits) { | |
| 1900 | TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); | |
| 1901 | uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); | |
| 1902 | enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes); | |
| 1903 | } | |
| 1881 | 1904 | } |
| 1882 | 1905 | |
| 1883 | 1906 | static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { |
src/codegen.cpp+4| ... | ... | @@ -3384,6 +3384,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 3384 | 3384 | case IrInstructionIdEmbedFile: |
| 3385 | 3385 | case IrInstructionIdIntType: |
| 3386 | 3386 | case IrInstructionIdMemberCount: |
| 3387 | case IrInstructionIdMemberType: | |
| 3388 | case IrInstructionIdMemberName: | |
| 3387 | 3389 | case IrInstructionIdAlignOf: |
| 3388 | 3390 | case IrInstructionIdFnProto: |
| 3389 | 3391 | case IrInstructionIdTestComptime: |
| ... | ... | @@ -4867,6 +4869,8 @@ static void define_builtin_fns(CodeGen *g) { |
| 4867 | 4869 | create_builtin_fn(g, BuiltinFnIdMaxValue, "maxValue", 1); |
| 4868 | 4870 | create_builtin_fn(g, BuiltinFnIdMinValue, "minValue", 1); |
| 4869 | 4871 | create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1); |
| 4872 | create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2); | |
| 4873 | create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); | |
| 4870 | 4874 | create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf |
| 4871 | 4875 | create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); |
| 4872 | 4876 | create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); |
src/ir.cpp+174| ... | ... | @@ -411,6 +411,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberCount *) { |
| 411 | 411 | return IrInstructionIdMemberCount; |
| 412 | 412 | } |
| 413 | 413 | |
| 414 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberType *) { | |
| 415 | return IrInstructionIdMemberType; | |
| 416 | } | |
| 417 | ||
| 418 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberName *) { | |
| 419 | return IrInstructionIdMemberName; | |
| 420 | } | |
| 421 | ||
| 414 | 422 | static constexpr IrInstructionId ir_instruction_id(IrInstructionBreakpoint *) { |
| 415 | 423 | return IrInstructionIdBreakpoint; |
| 416 | 424 | } |
| ... | ... | @@ -1783,6 +1791,32 @@ static IrInstruction *ir_build_member_count(IrBuilder *irb, Scope *scope, AstNod |
| 1783 | 1791 | return &instruction->base; |
| 1784 | 1792 | } |
| 1785 | 1793 | |
| 1794 | static IrInstruction *ir_build_member_type(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 1795 | IrInstruction *container_type, IrInstruction *member_index) | |
| 1796 | { | |
| 1797 | IrInstructionMemberType *instruction = ir_build_instruction<IrInstructionMemberType>(irb, scope, source_node); | |
| 1798 | instruction->container_type = container_type; | |
| 1799 | instruction->member_index = member_index; | |
| 1800 | ||
| 1801 | ir_ref_instruction(container_type, irb->current_basic_block); | |
| 1802 | ir_ref_instruction(member_index, irb->current_basic_block); | |
| 1803 | ||
| 1804 | return &instruction->base; | |
| 1805 | } | |
| 1806 | ||
| 1807 | static IrInstruction *ir_build_member_name(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 1808 | IrInstruction *container_type, IrInstruction *member_index) | |
| 1809 | { | |
| 1810 | IrInstructionMemberName *instruction = ir_build_instruction<IrInstructionMemberName>(irb, scope, source_node); | |
| 1811 | instruction->container_type = container_type; | |
| 1812 | instruction->member_index = member_index; | |
| 1813 | ||
| 1814 | ir_ref_instruction(container_type, irb->current_basic_block); | |
| 1815 | ir_ref_instruction(member_index, irb->current_basic_block); | |
| 1816 | ||
| 1817 | return &instruction->base; | |
| 1818 | } | |
| 1819 | ||
| 1786 | 1820 | static IrInstruction *ir_build_breakpoint(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 1787 | 1821 | IrInstructionBreakpoint *instruction = ir_build_instruction<IrInstructionBreakpoint>(irb, scope, source_node); |
| 1788 | 1822 | return &instruction->base; |
| ... | ... | @@ -2727,6 +2761,22 @@ static IrInstruction *ir_instruction_membercount_get_dep(IrInstructionMemberCoun |
| 2727 | 2761 | } |
| 2728 | 2762 | } |
| 2729 | 2763 | |
| 2764 | static IrInstruction *ir_instruction_membertype_get_dep(IrInstructionMemberType *instruction, size_t index) { | |
| 2765 | switch (index) { | |
| 2766 | case 0: return instruction->container_type; | |
| 2767 | case 1: return instruction->member_index; | |
| 2768 | default: return nullptr; | |
| 2769 | } | |
| 2770 | } | |
| 2771 | ||
| 2772 | static IrInstruction *ir_instruction_membername_get_dep(IrInstructionMemberName *instruction, size_t index) { | |
| 2773 | switch (index) { | |
| 2774 | case 0: return instruction->container_type; | |
| 2775 | case 1: return instruction->member_index; | |
| 2776 | default: return nullptr; | |
| 2777 | } | |
| 2778 | } | |
| 2779 | ||
| 2730 | 2780 | static IrInstruction *ir_instruction_breakpoint_get_dep(IrInstructionBreakpoint *instruction, size_t index) { |
| 2731 | 2781 | return nullptr; |
| 2732 | 2782 | } |
| ... | ... | @@ -3143,6 +3193,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 3143 | 3193 | return ir_instruction_slice_get_dep((IrInstructionSlice *) instruction, index); |
| 3144 | 3194 | case IrInstructionIdMemberCount: |
| 3145 | 3195 | return ir_instruction_membercount_get_dep((IrInstructionMemberCount *) instruction, index); |
| 3196 | case IrInstructionIdMemberType: | |
| 3197 | return ir_instruction_membertype_get_dep((IrInstructionMemberType *) instruction, index); | |
| 3198 | case IrInstructionIdMemberName: | |
| 3199 | return ir_instruction_membername_get_dep((IrInstructionMemberName *) instruction, index); | |
| 3146 | 3200 | case IrInstructionIdBreakpoint: |
| 3147 | 3201 | return ir_instruction_breakpoint_get_dep((IrInstructionBreakpoint *) instruction, index); |
| 3148 | 3202 | case IrInstructionIdReturnAddress: |
| ... | ... | @@ -4379,6 +4433,36 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4379 | 4433 | |
| 4380 | 4434 | return ir_build_member_count(irb, scope, node, arg0_value); |
| 4381 | 4435 | } |
| 4436 | case BuiltinFnIdMemberType: | |
| 4437 | { | |
| 4438 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4439 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 4440 | if (arg0_value == irb->codegen->invalid_instruction) | |
| 4441 | return arg0_value; | |
| 4442 | ||
| 4443 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4444 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | |
| 4445 | if (arg1_value == irb->codegen->invalid_instruction) | |
| 4446 | return arg1_value; | |
| 4447 | ||
| 4448 | ||
| 4449 | return ir_build_member_type(irb, scope, node, arg0_value, arg1_value); | |
| 4450 | } | |
| 4451 | case BuiltinFnIdMemberName: | |
| 4452 | { | |
| 4453 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4454 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 4455 | if (arg0_value == irb->codegen->invalid_instruction) | |
| 4456 | return arg0_value; | |
| 4457 | ||
| 4458 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4459 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | |
| 4460 | if (arg1_value == irb->codegen->invalid_instruction) | |
| 4461 | return arg1_value; | |
| 4462 | ||
| 4463 | ||
| 4464 | return ir_build_member_name(irb, scope, node, arg0_value, arg1_value); | |
| 4465 | } | |
| 4382 | 4466 | case BuiltinFnIdBreakpoint: |
| 4383 | 4467 | return ir_build_breakpoint(irb, scope, node); |
| 4384 | 4468 | case BuiltinFnIdReturnAddress: |
| ... | ... | @@ -14337,6 +14421,90 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns |
| 14337 | 14421 | return ira->codegen->builtin_types.entry_num_lit_int; |
| 14338 | 14422 | } |
| 14339 | 14423 | |
| 14424 | static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) { | |
| 14425 | IrInstruction *container_type_value = instruction->container_type->other; | |
| 14426 | TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value); | |
| 14427 | if (type_is_invalid(container_type)) | |
| 14428 | return ira->codegen->builtin_types.entry_invalid; | |
| 14429 | ||
| 14430 | uint64_t member_index; | |
| 14431 | IrInstruction *index_value = instruction->member_index->other; | |
| 14432 | if (!ir_resolve_usize(ira, index_value, &member_index)) | |
| 14433 | return ira->codegen->builtin_types.entry_invalid; | |
| 14434 | ||
| 14435 | if (container_type->id == TypeTableEntryIdStruct) { | |
| 14436 | if (member_index >= container_type->data.structure.src_field_count) { | |
| 14437 | ir_add_error(ira, index_value, | |
| 14438 | buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", | |
| 14439 | member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count)); | |
| 14440 | return ira->codegen->builtin_types.entry_invalid; | |
| 14441 | } | |
| 14442 | TypeStructField *field = &container_type->data.structure.fields[member_index]; | |
| 14443 | ||
| 14444 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | |
| 14445 | out_val->data.x_type = field->type_entry; | |
| 14446 | return ira->codegen->builtin_types.entry_type; | |
| 14447 | } else if (container_type->id == TypeTableEntryIdEnum) { | |
| 14448 | if (member_index >= container_type->data.enumeration.src_field_count) { | |
| 14449 | ir_add_error(ira, index_value, | |
| 14450 | buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", | |
| 14451 | member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count)); | |
| 14452 | return ira->codegen->builtin_types.entry_invalid; | |
| 14453 | } | |
| 14454 | TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; | |
| 14455 | ||
| 14456 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | |
| 14457 | out_val->data.x_type = field->type_entry; | |
| 14458 | return ira->codegen->builtin_types.entry_type; | |
| 14459 | } else { | |
| 14460 | ir_add_error(ira, container_type_value, | |
| 14461 | buf_sprintf("type '%s' does not support @memberType", buf_ptr(&container_type->name))); | |
| 14462 | return ira->codegen->builtin_types.entry_invalid; | |
| 14463 | } | |
| 14464 | } | |
| 14465 | ||
| 14466 | static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) { | |
| 14467 | IrInstruction *container_type_value = instruction->container_type->other; | |
| 14468 | TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value); | |
| 14469 | if (type_is_invalid(container_type)) | |
| 14470 | return ira->codegen->builtin_types.entry_invalid; | |
| 14471 | ||
| 14472 | uint64_t member_index; | |
| 14473 | IrInstruction *index_value = instruction->member_index->other; | |
| 14474 | if (!ir_resolve_usize(ira, index_value, &member_index)) | |
| 14475 | return ira->codegen->builtin_types.entry_invalid; | |
| 14476 | ||
| 14477 | if (container_type->id == TypeTableEntryIdStruct) { | |
| 14478 | if (member_index >= container_type->data.structure.src_field_count) { | |
| 14479 | ir_add_error(ira, index_value, | |
| 14480 | buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", | |
| 14481 | member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count)); | |
| 14482 | return ira->codegen->builtin_types.entry_invalid; | |
| 14483 | } | |
| 14484 | TypeStructField *field = &container_type->data.structure.fields[member_index]; | |
| 14485 | ||
| 14486 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | |
| 14487 | init_const_str_lit(ira->codegen, out_val, field->name); | |
| 14488 | return out_val->type; | |
| 14489 | } else if (container_type->id == TypeTableEntryIdEnum) { | |
| 14490 | if (member_index >= container_type->data.enumeration.src_field_count) { | |
| 14491 | ir_add_error(ira, index_value, | |
| 14492 | buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", | |
| 14493 | member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count)); | |
| 14494 | return ira->codegen->builtin_types.entry_invalid; | |
| 14495 | } | |
| 14496 | TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; | |
| 14497 | ||
| 14498 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | |
| 14499 | init_const_str_lit(ira->codegen, out_val, field->name); | |
| 14500 | return out_val->type; | |
| 14501 | } else { | |
| 14502 | ir_add_error(ira, container_type_value, | |
| 14503 | buf_sprintf("type '%s' does not support @memberName", buf_ptr(&container_type->name))); | |
| 14504 | return ira->codegen->builtin_types.entry_invalid; | |
| 14505 | } | |
| 14506 | } | |
| 14507 | ||
| 14340 | 14508 | static TypeTableEntry *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) { |
| 14341 | 14509 | ir_build_breakpoint_from(&ira->new_irb, &instruction->base); |
| 14342 | 14510 | return ira->codegen->builtin_types.entry_void; |
| ... | ... | @@ -15606,6 +15774,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 15606 | 15774 | return ir_analyze_instruction_slice(ira, (IrInstructionSlice *)instruction); |
| 15607 | 15775 | case IrInstructionIdMemberCount: |
| 15608 | 15776 | return ir_analyze_instruction_member_count(ira, (IrInstructionMemberCount *)instruction); |
| 15777 | case IrInstructionIdMemberType: | |
| 15778 | return ir_analyze_instruction_member_type(ira, (IrInstructionMemberType *)instruction); | |
| 15779 | case IrInstructionIdMemberName: | |
| 15780 | return ir_analyze_instruction_member_name(ira, (IrInstructionMemberName *)instruction); | |
| 15609 | 15781 | case IrInstructionIdBreakpoint: |
| 15610 | 15782 | return ir_analyze_instruction_breakpoint(ira, (IrInstructionBreakpoint *)instruction); |
| 15611 | 15783 | case IrInstructionIdReturnAddress: |
| ... | ... | @@ -15815,6 +15987,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 15815 | 15987 | case IrInstructionIdBoolNot: |
| 15816 | 15988 | case IrInstructionIdSlice: |
| 15817 | 15989 | case IrInstructionIdMemberCount: |
| 15990 | case IrInstructionIdMemberType: | |
| 15991 | case IrInstructionIdMemberName: | |
| 15818 | 15992 | case IrInstructionIdAlignOf: |
| 15819 | 15993 | case IrInstructionIdReturnAddress: |
| 15820 | 15994 | case IrInstructionIdFrameAddress: |
src/ir_print.cpp+22| ... | ... | @@ -658,6 +658,22 @@ static void ir_print_member_count(IrPrint *irp, IrInstructionMemberCount *instru |
| 658 | 658 | fprintf(irp->f, ")"); |
| 659 | 659 | } |
| 660 | 660 | |
| 661 | static void ir_print_member_type(IrPrint *irp, IrInstructionMemberType *instruction) { | |
| 662 | fprintf(irp->f, "@memberType("); | |
| 663 | ir_print_other_instruction(irp, instruction->container_type); | |
| 664 | fprintf(irp->f, ", "); | |
| 665 | ir_print_other_instruction(irp, instruction->member_index); | |
| 666 | fprintf(irp->f, ")"); | |
| 667 | } | |
| 668 | ||
| 669 | static void ir_print_member_name(IrPrint *irp, IrInstructionMemberName *instruction) { | |
| 670 | fprintf(irp->f, "@memberName("); | |
| 671 | ir_print_other_instruction(irp, instruction->container_type); | |
| 672 | fprintf(irp->f, ", "); | |
| 673 | ir_print_other_instruction(irp, instruction->member_index); | |
| 674 | fprintf(irp->f, ")"); | |
| 675 | } | |
| 676 | ||
| 661 | 677 | static void ir_print_breakpoint(IrPrint *irp, IrInstructionBreakpoint *instruction) { |
| 662 | 678 | fprintf(irp->f, "@breakpoint()"); |
| 663 | 679 | } |
| ... | ... | @@ -1148,6 +1164,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1148 | 1164 | case IrInstructionIdMemberCount: |
| 1149 | 1165 | ir_print_member_count(irp, (IrInstructionMemberCount *)instruction); |
| 1150 | 1166 | break; |
| 1167 | case IrInstructionIdMemberType: | |
| 1168 | ir_print_member_type(irp, (IrInstructionMemberType *)instruction); | |
| 1169 | break; | |
| 1170 | case IrInstructionIdMemberName: | |
| 1171 | ir_print_member_name(irp, (IrInstructionMemberName *)instruction); | |
| 1172 | break; | |
| 1151 | 1173 | case IrInstructionIdBreakpoint: |
| 1152 | 1174 | ir_print_breakpoint(irp, (IrInstructionBreakpoint *)instruction); |
| 1153 | 1175 | break; |
test/cases/reflection.zig+43| ... | ... | @@ -25,3 +25,46 @@ test "reflection: function return type, var args, and param types" { |
| 25 | 25 | fn dummy(a: bool, b: i32, c: f32) -> i32 { 1234 } |
| 26 | 26 | fn dummy_varargs(args: ...) {} |
| 27 | 27 | |
| 28 | test "reflection: struct member types and names" { | |
| 29 | comptime { | |
| 30 | assert(@memberCount(Foo) == 3); | |
| 31 | ||
| 32 | assert(@memberType(Foo, 0) == i32); | |
| 33 | assert(@memberType(Foo, 1) == bool); | |
| 34 | assert(@memberType(Foo, 2) == void); | |
| 35 | ||
| 36 | assert(mem.eql(u8, @memberName(Foo, 0), "one")); | |
| 37 | assert(mem.eql(u8, @memberName(Foo, 1), "two")); | |
| 38 | assert(mem.eql(u8, @memberName(Foo, 2), "three")); | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | test "reflection: enum member types and names" { | |
| 43 | comptime { | |
| 44 | assert(@memberCount(Bar) == 4); | |
| 45 | ||
| 46 | assert(@memberType(Bar, 0) == void); | |
| 47 | assert(@memberType(Bar, 1) == i32); | |
| 48 | assert(@memberType(Bar, 2) == bool); | |
| 49 | assert(@memberType(Bar, 3) == f64); | |
| 50 | ||
| 51 | assert(mem.eql(u8, @memberName(Bar, 0), "One")); | |
| 52 | assert(mem.eql(u8, @memberName(Bar, 1), "Two")); | |
| 53 | assert(mem.eql(u8, @memberName(Bar, 2), "Three")); | |
| 54 | assert(mem.eql(u8, @memberName(Bar, 3), "Four")); | |
| 55 | } | |
| 56 | ||
| 57 | } | |
| 58 | ||
| 59 | const Foo = struct { | |
| 60 | one: i32, | |
| 61 | two: bool, | |
| 62 | three: void, | |
| 63 | }; | |
| 64 | ||
| 65 | const Bar = enum { | |
| 66 | One, | |
| 67 | Two: i32, | |
| 68 | Three: bool, | |
| 69 | Four: f64, | |
| 70 | }; |
test/compile_errors.zig+46| ... | ... | @@ -2289,4 +2289,50 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 2289 | 2289 | \\fn add(a: i32, b: i32) -> i32 { return a + b; } |
| 2290 | 2290 | , |
| 2291 | 2291 | ".tmp_source.zig:2:32: error: arg index 2 out of bounds; 'fn(i32, i32) -> i32' has 2 arguments"); |
| 2292 | ||
| 2293 | cases.add("@memberType on unsupported type", | |
| 2294 | \\comptime { | |
| 2295 | \\ _ = @memberType(i32, 0); | |
| 2296 | \\} | |
| 2297 | , | |
| 2298 | ".tmp_source.zig:2:21: error: type 'i32' does not support @memberType"); | |
| 2299 | ||
| 2300 | cases.add("@memberType struct out of bounds", | |
| 2301 | \\comptime { | |
| 2302 | \\ _ = @memberType(Foo, 0); | |
| 2303 | \\} | |
| 2304 | \\const Foo = struct {}; | |
| 2305 | , | |
| 2306 | ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); | |
| 2307 | ||
| 2308 | cases.add("@memberType enum out of bounds", | |
| 2309 | \\comptime { | |
| 2310 | \\ _ = @memberType(Foo, 0); | |
| 2311 | \\} | |
| 2312 | \\const Foo = enum {}; | |
| 2313 | , | |
| 2314 | ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); | |
| 2315 | ||
| 2316 | cases.add("@memberName on unsupported type", | |
| 2317 | \\comptime { | |
| 2318 | \\ _ = @memberName(i32, 0); | |
| 2319 | \\} | |
| 2320 | , | |
| 2321 | ".tmp_source.zig:2:21: error: type 'i32' does not support @memberName"); | |
| 2322 | ||
| 2323 | cases.add("@memberName struct out of bounds", | |
| 2324 | \\comptime { | |
| 2325 | \\ _ = @memberName(Foo, 0); | |
| 2326 | \\} | |
| 2327 | \\const Foo = struct {}; | |
| 2328 | , | |
| 2329 | ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); | |
| 2330 | ||
| 2331 | cases.add("@memberName enum out of bounds", | |
| 2332 | \\comptime { | |
| 2333 | \\ _ = @memberName(Foo, 0); | |
| 2334 | \\} | |
| 2335 | \\const Foo = enum {}; | |
| 2336 | , | |
| 2337 | ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); | |
| 2292 | 2338 | } |