diff --git a/CMakeLists.txt b/CMakeLists.txt index 72c480cd40..d8e2d9ff7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,15 +47,20 @@ option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF) option(ZIG_FORCE_EXTERNAL_LLD "If your system has the LLD patches use it instead of the embedded LLD" OFF) find_package(llvm) -include_directories(${LLVM_INCLUDE_DIRS}) - find_package(clang) -include_directories(${CLANG_INCLUDE_DIRS}) if(ZIG_FORCE_EXTERNAL_LLD) find_package(lld) + include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LLD_INCLUDE_DIRS}) + include_directories(${CLANG_INCLUDE_DIRS}) else() + # This goes first so that we find embedded LLD instead + # of system LLD. + include_directories("${CMAKE_SOURCE_DIR}/deps/lld/include") + + include_directories(${LLVM_INCLUDE_DIRS}) + include_directories(${CLANG_INCLUDE_DIRS}) set(EMBEDDED_LLD_LIB_SOURCES "${CMAKE_SOURCE_DIR}/deps/lld/lib/Driver/DarwinLdDriver.cpp" "${CMAKE_SOURCE_DIR}/deps/lld/lib/Config/Version.cpp" diff --git a/c_headers/avx512fintrin.h b/c_headers/avx512fintrin.h index 247ac879ea..f5137428ba 100644 --- a/c_headers/avx512fintrin.h +++ b/c_headers/avx512fintrin.h @@ -4543,37 +4543,6 @@ _mm512_maskz_unpacklo_epi64 (__mmask8 __U, __m512i __A, __m512i __B) (__v8di)_mm512_setzero_si512()); } -/* Bit Test */ - -static __inline __mmask16 __DEFAULT_FN_ATTRS -_mm512_test_epi32_mask(__m512i __A, __m512i __B) -{ - return (__mmask16) __builtin_ia32_ptestmd512 ((__v16si) __A, - (__v16si) __B, - (__mmask16) -1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_test_epi32_mask (__mmask16 __U, __m512i __A, __m512i __B) -{ - return (__mmask16) __builtin_ia32_ptestmd512 ((__v16si) __A, - (__v16si) __B, __U); -} - -static __inline __mmask8 __DEFAULT_FN_ATTRS -_mm512_test_epi64_mask(__m512i __A, __m512i __B) -{ - return (__mmask8) __builtin_ia32_ptestmq512 ((__v8di) __A, - (__v8di) __B, - (__mmask8) -1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_test_epi64_mask (__mmask8 __U, __m512i __A, __m512i __B) -{ - return (__mmask8) __builtin_ia32_ptestmq512 ((__v8di) __A, (__v8di) __B, __U); -} - /* SIMD load ops */ @@ -4844,293 +4813,105 @@ _mm512_knot(__mmask16 __M) /* Integer compare */ -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmpeq_epi32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_pcmpeqd512_mask((__v16si)__a, (__v16si)__b, - (__mmask16)-1); -} +#define _mm512_cmpeq_epi32_mask(A, B) \ + _mm512_cmp_epi32_mask((A), (B), _MM_CMPINT_EQ) +#define _mm512_mask_cmpeq_epi32_mask(k, A, B) \ + _mm512_mask_cmp_epi32_mask((k), (A), (B), _MM_CMPINT_EQ) +#define _mm512_cmpge_epi32_mask(A, B) \ + _mm512_cmp_epi32_mask((A), (B), _MM_CMPINT_GE) +#define _mm512_mask_cmpge_epi32_mask(k, A, B) \ + _mm512_mask_cmp_epi32_mask((k), (A), (B), _MM_CMPINT_GE) +#define _mm512_cmpgt_epi32_mask(A, B) \ + _mm512_cmp_epi32_mask((A), (B), _MM_CMPINT_GT) +#define _mm512_mask_cmpgt_epi32_mask(k, A, B) \ + _mm512_mask_cmp_epi32_mask((k), (A), (B), _MM_CMPINT_GT) +#define _mm512_cmple_epi32_mask(A, B) \ + _mm512_cmp_epi32_mask((A), (B), _MM_CMPINT_LE) +#define _mm512_mask_cmple_epi32_mask(k, A, B) \ + _mm512_mask_cmp_epi32_mask((k), (A), (B), _MM_CMPINT_LE) +#define _mm512_cmplt_epi32_mask(A, B) \ + _mm512_cmp_epi32_mask((A), (B), _MM_CMPINT_LT) +#define _mm512_mask_cmplt_epi32_mask(k, A, B) \ + _mm512_mask_cmp_epi32_mask((k), (A), (B), _MM_CMPINT_LT) +#define _mm512_cmpneq_epi32_mask(A, B) \ + _mm512_cmp_epi32_mask((A), (B), _MM_CMPINT_NE) +#define _mm512_mask_cmpneq_epi32_mask(k, A, B) \ + _mm512_mask_cmp_epi32_mask((k), (A), (B), _MM_CMPINT_NE) -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmpeq_epi32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_pcmpeqd512_mask((__v16si)__a, (__v16si)__b, - __u); -} +#define _mm512_cmpeq_epu32_mask(A, B) \ + _mm512_cmp_epu32_mask((A), (B), _MM_CMPINT_EQ) +#define _mm512_mask_cmpeq_epu32_mask(k, A, B) \ + _mm512_mask_cmp_epu32_mask((k), (A), (B), _MM_CMPINT_EQ) +#define _mm512_cmpge_epu32_mask(A, B) \ + _mm512_cmp_epu32_mask((A), (B), _MM_CMPINT_GE) +#define _mm512_mask_cmpge_epu32_mask(k, A, B) \ + _mm512_mask_cmp_epu32_mask((k), (A), (B), _MM_CMPINT_GE) +#define _mm512_cmpgt_epu32_mask(A, B) \ + _mm512_cmp_epu32_mask((A), (B), _MM_CMPINT_GT) +#define _mm512_mask_cmpgt_epu32_mask(k, A, B) \ + _mm512_mask_cmp_epu32_mask((k), (A), (B), _MM_CMPINT_GT) +#define _mm512_cmple_epu32_mask(A, B) \ + _mm512_cmp_epu32_mask((A), (B), _MM_CMPINT_LE) +#define _mm512_mask_cmple_epu32_mask(k, A, B) \ + _mm512_mask_cmp_epu32_mask((k), (A), (B), _MM_CMPINT_LE) +#define _mm512_cmplt_epu32_mask(A, B) \ + _mm512_cmp_epu32_mask((A), (B), _MM_CMPINT_LT) +#define _mm512_mask_cmplt_epu32_mask(k, A, B) \ + _mm512_mask_cmp_epu32_mask((k), (A), (B), _MM_CMPINT_LT) +#define _mm512_cmpneq_epu32_mask(A, B) \ + _mm512_cmp_epu32_mask((A), (B), _MM_CMPINT_NE) +#define _mm512_mask_cmpneq_epu32_mask(k, A, B) \ + _mm512_mask_cmp_epu32_mask((k), (A), (B), _MM_CMPINT_NE) -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmpeq_epu32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 0, - (__mmask16)-1); -} +#define _mm512_cmpeq_epi64_mask(A, B) \ + _mm512_cmp_epi64_mask((A), (B), _MM_CMPINT_EQ) +#define _mm512_mask_cmpeq_epi64_mask(k, A, B) \ + _mm512_mask_cmp_epi64_mask((k), (A), (B), _MM_CMPINT_EQ) +#define _mm512_cmpge_epi64_mask(A, B) \ + _mm512_cmp_epi64_mask((A), (B), _MM_CMPINT_GE) +#define _mm512_mask_cmpge_epi64_mask(k, A, B) \ + _mm512_mask_cmp_epi64_mask((k), (A), (B), _MM_CMPINT_GE) +#define _mm512_cmpgt_epi64_mask(A, B) \ + _mm512_cmp_epi64_mask((A), (B), _MM_CMPINT_GT) +#define _mm512_mask_cmpgt_epi64_mask(k, A, B) \ + _mm512_mask_cmp_epi64_mask((k), (A), (B), _MM_CMPINT_GT) +#define _mm512_cmple_epi64_mask(A, B) \ + _mm512_cmp_epi64_mask((A), (B), _MM_CMPINT_LE) +#define _mm512_mask_cmple_epi64_mask(k, A, B) \ + _mm512_mask_cmp_epi64_mask((k), (A), (B), _MM_CMPINT_LE) +#define _mm512_cmplt_epi64_mask(A, B) \ + _mm512_cmp_epi64_mask((A), (B), _MM_CMPINT_LT) +#define _mm512_mask_cmplt_epi64_mask(k, A, B) \ + _mm512_mask_cmp_epi64_mask((k), (A), (B), _MM_CMPINT_LT) +#define _mm512_cmpneq_epi64_mask(A, B) \ + _mm512_cmp_epi64_mask((A), (B), _MM_CMPINT_NE) +#define _mm512_mask_cmpneq_epi64_mask(k, A, B) \ + _mm512_mask_cmp_epi64_mask((k), (A), (B), _MM_CMPINT_NE) -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmpeq_epu32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 0, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmpeq_epi64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_pcmpeqq512_mask((__v8di)__a, (__v8di)__b, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmpeq_epi64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_pcmpeqq512_mask((__v8di)__a, (__v8di)__b, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmpeq_epu64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 0, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmpeq_epu64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 0, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmpge_epi32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_cmpd512_mask((__v16si)__a, (__v16si)__b, 5, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmpge_epi32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_cmpd512_mask((__v16si)__a, (__v16si)__b, 5, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmpge_epu32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 5, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmpge_epu32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 5, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmpge_epi64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_cmpq512_mask((__v8di)__a, (__v8di)__b, 5, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmpge_epi64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_cmpq512_mask((__v8di)__a, (__v8di)__b, 5, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmpge_epu64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 5, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmpge_epu64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 5, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmpgt_epi32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_pcmpgtd512_mask((__v16si)__a, (__v16si)__b, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmpgt_epi32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_pcmpgtd512_mask((__v16si)__a, (__v16si)__b, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmpgt_epu32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 6, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmpgt_epu32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 6, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmpgt_epi64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_pcmpgtq512_mask((__v8di)__a, (__v8di)__b, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmpgt_epi64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_pcmpgtq512_mask((__v8di)__a, (__v8di)__b, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmpgt_epu64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 6, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmpgt_epu64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 6, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmple_epi32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_cmpd512_mask((__v16si)__a, (__v16si)__b, 2, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmple_epi32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_cmpd512_mask((__v16si)__a, (__v16si)__b, 2, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmple_epu32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 2, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmple_epu32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 2, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmple_epi64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_cmpq512_mask((__v8di)__a, (__v8di)__b, 2, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmple_epi64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_cmpq512_mask((__v8di)__a, (__v8di)__b, 2, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmple_epu64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 2, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmple_epu64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 2, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmplt_epi32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_cmpd512_mask((__v16si)__a, (__v16si)__b, 1, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmplt_epi32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_cmpd512_mask((__v16si)__a, (__v16si)__b, 1, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmplt_epu32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 1, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmplt_epu32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 1, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmplt_epi64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_cmpq512_mask((__v8di)__a, (__v8di)__b, 1, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmplt_epi64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_cmpq512_mask((__v8di)__a, (__v8di)__b, 1, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmplt_epu64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 1, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmplt_epu64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 1, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmpneq_epi32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_cmpd512_mask((__v16si)__a, (__v16si)__b, 4, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmpneq_epi32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_cmpd512_mask((__v16si)__a, (__v16si)__b, 4, - __u); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_cmpneq_epu32_mask(__m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 4, - (__mmask16)-1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_cmpneq_epu32_mask(__mmask16 __u, __m512i __a, __m512i __b) { - return (__mmask16)__builtin_ia32_ucmpd512_mask((__v16si)__a, (__v16si)__b, 4, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmpneq_epi64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_cmpq512_mask((__v8di)__a, (__v8di)__b, 4, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmpneq_epi64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_cmpq512_mask((__v8di)__a, (__v8di)__b, 4, - __u); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_cmpneq_epu64_mask(__m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 4, - (__mmask8)-1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_cmpneq_epu64_mask(__mmask8 __u, __m512i __a, __m512i __b) { - return (__mmask8)__builtin_ia32_ucmpq512_mask((__v8di)__a, (__v8di)__b, 4, - __u); -} +#define _mm512_cmpeq_epu64_mask(A, B) \ + _mm512_cmp_epu64_mask((A), (B), _MM_CMPINT_EQ) +#define _mm512_mask_cmpeq_epu64_mask(k, A, B) \ + _mm512_mask_cmp_epu64_mask((k), (A), (B), _MM_CMPINT_EQ) +#define _mm512_cmpge_epu64_mask(A, B) \ + _mm512_cmp_epu64_mask((A), (B), _MM_CMPINT_GE) +#define _mm512_mask_cmpge_epu64_mask(k, A, B) \ + _mm512_mask_cmp_epu64_mask((k), (A), (B), _MM_CMPINT_GE) +#define _mm512_cmpgt_epu64_mask(A, B) \ + _mm512_cmp_epu64_mask((A), (B), _MM_CMPINT_GT) +#define _mm512_mask_cmpgt_epu64_mask(k, A, B) \ + _mm512_mask_cmp_epu64_mask((k), (A), (B), _MM_CMPINT_GT) +#define _mm512_cmple_epu64_mask(A, B) \ + _mm512_cmp_epu64_mask((A), (B), _MM_CMPINT_LE) +#define _mm512_mask_cmple_epu64_mask(k, A, B) \ + _mm512_mask_cmp_epu64_mask((k), (A), (B), _MM_CMPINT_LE) +#define _mm512_cmplt_epu64_mask(A, B) \ + _mm512_cmp_epu64_mask((A), (B), _MM_CMPINT_LT) +#define _mm512_mask_cmplt_epu64_mask(k, A, B) \ + _mm512_mask_cmp_epu64_mask((k), (A), (B), _MM_CMPINT_LT) +#define _mm512_cmpneq_epu64_mask(A, B) \ + _mm512_cmp_epu64_mask((A), (B), _MM_CMPINT_NE) +#define _mm512_mask_cmpneq_epu64_mask(k, A, B) \ + _mm512_mask_cmp_epu64_mask((k), (A), (B), _MM_CMPINT_NE) static __inline__ __m512i __DEFAULT_FN_ATTRS _mm512_cvtepi8_epi32(__m128i __A) @@ -6797,35 +6578,6 @@ _mm512_maskz_permutex2var_ps (__mmask16 __U, __m512 __A, __m512i __I, (__mmask16) __U); } -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_testn_epi32_mask (__m512i __A, __m512i __B) -{ - return (__mmask16) __builtin_ia32_ptestnmd512 ((__v16si) __A, - (__v16si) __B, - (__mmask16) -1); -} - -static __inline__ __mmask16 __DEFAULT_FN_ATTRS -_mm512_mask_testn_epi32_mask (__mmask16 __U, __m512i __A, __m512i __B) -{ - return (__mmask16) __builtin_ia32_ptestnmd512 ((__v16si) __A, - (__v16si) __B, __U); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_testn_epi64_mask (__m512i __A, __m512i __B) -{ - return (__mmask8) __builtin_ia32_ptestnmq512 ((__v8di) __A, - (__v8di) __B, - (__mmask8) -1); -} - -static __inline__ __mmask8 __DEFAULT_FN_ATTRS -_mm512_mask_testn_epi64_mask (__mmask8 __U, __m512i __A, __m512i __B) -{ - return (__mmask8) __builtin_ia32_ptestnmq512 ((__v8di) __A, - (__v8di) __B, __U); -} #define _mm512_cvtt_roundpd_epu32(A, R) __extension__ ({ \ (__m256i)__builtin_ia32_cvttpd2udq512_mask((__v8df)(__m512d)(A), \ @@ -7194,76 +6946,100 @@ _mm512_maskz_srai_epi64(__mmask8 __U, __m512i __A, int __B) } #define _mm512_shuffle_f32x4(A, B, imm) __extension__ ({ \ - (__m512)__builtin_ia32_shuf_f32x4_mask((__v16sf)(__m512)(A), \ - (__v16sf)(__m512)(B), (int)(imm), \ - (__v16sf)_mm512_undefined_ps(), \ - (__mmask16)-1); }) + (__m512)__builtin_shufflevector((__v16sf)(__m512)(A), \ + (__v16sf)(__m512)(B), \ + 0 + ((((imm) >> 0) & 0x3) * 4), \ + 1 + ((((imm) >> 0) & 0x3) * 4), \ + 2 + ((((imm) >> 0) & 0x3) * 4), \ + 3 + ((((imm) >> 0) & 0x3) * 4), \ + 0 + ((((imm) >> 2) & 0x3) * 4), \ + 1 + ((((imm) >> 2) & 0x3) * 4), \ + 2 + ((((imm) >> 2) & 0x3) * 4), \ + 3 + ((((imm) >> 2) & 0x3) * 4), \ + 16 + ((((imm) >> 4) & 0x3) * 4), \ + 17 + ((((imm) >> 4) & 0x3) * 4), \ + 18 + ((((imm) >> 4) & 0x3) * 4), \ + 19 + ((((imm) >> 4) & 0x3) * 4), \ + 16 + ((((imm) >> 6) & 0x3) * 4), \ + 17 + ((((imm) >> 6) & 0x3) * 4), \ + 18 + ((((imm) >> 6) & 0x3) * 4), \ + 19 + ((((imm) >> 6) & 0x3) * 4)); }) #define _mm512_mask_shuffle_f32x4(W, U, A, B, imm) __extension__ ({ \ - (__m512)__builtin_ia32_shuf_f32x4_mask((__v16sf)(__m512)(A), \ - (__v16sf)(__m512)(B), (int)(imm), \ - (__v16sf)(__m512)(W), \ - (__mmask16)(U)); }) + (__m512)__builtin_ia32_selectps_512((__mmask16)(U), \ + (__v16sf)_mm512_shuffle_f32x4((A), (B), (imm)), \ + (__v16sf)(__m512)(W)); }) #define _mm512_maskz_shuffle_f32x4(U, A, B, imm) __extension__ ({ \ - (__m512)__builtin_ia32_shuf_f32x4_mask((__v16sf)(__m512)(A), \ - (__v16sf)(__m512)(B), (int)(imm), \ - (__v16sf)_mm512_setzero_ps(), \ - (__mmask16)(U)); }) + (__m512)__builtin_ia32_selectps_512((__mmask16)(U), \ + (__v16sf)_mm512_shuffle_f32x4((A), (B), (imm)), \ + (__v16sf)_mm512_setzero_ps()); }) #define _mm512_shuffle_f64x2(A, B, imm) __extension__ ({ \ - (__m512d)__builtin_ia32_shuf_f64x2_mask((__v8df)(__m512d)(A), \ - (__v8df)(__m512d)(B), (int)(imm), \ - (__v8df)_mm512_undefined_pd(), \ - (__mmask8)-1); }) + (__m512d)__builtin_shufflevector((__v8df)(__m512d)(A), \ + (__v8df)(__m512d)(B), \ + 0 + ((((imm) >> 0) & 0x3) * 2), \ + 1 + ((((imm) >> 0) & 0x3) * 2), \ + 0 + ((((imm) >> 2) & 0x3) * 2), \ + 1 + ((((imm) >> 2) & 0x3) * 2), \ + 8 + ((((imm) >> 4) & 0x3) * 2), \ + 9 + ((((imm) >> 4) & 0x3) * 2), \ + 8 + ((((imm) >> 6) & 0x3) * 2), \ + 9 + ((((imm) >> 6) & 0x3) * 2)); }) #define _mm512_mask_shuffle_f64x2(W, U, A, B, imm) __extension__ ({ \ - (__m512d)__builtin_ia32_shuf_f64x2_mask((__v8df)(__m512d)(A), \ - (__v8df)(__m512d)(B), (int)(imm), \ - (__v8df)(__m512d)(W), \ - (__mmask8)(U)); }) + (__m512d)__builtin_ia32_selectpd_512((__mmask8)(U), \ + (__v8df)_mm512_shuffle_f64x2((A), (B), (imm)), \ + (__v8df)(__m512d)(W)); }) #define _mm512_maskz_shuffle_f64x2(U, A, B, imm) __extension__ ({ \ - (__m512d)__builtin_ia32_shuf_f64x2_mask((__v8df)(__m512d)(A), \ - (__v8df)(__m512d)(B), (int)(imm), \ - (__v8df)_mm512_setzero_pd(), \ - (__mmask8)(U)); }) + (__m512d)__builtin_ia32_selectpd_512((__mmask8)(U), \ + (__v8df)_mm512_shuffle_f64x2((A), (B), (imm)), \ + (__v8df)_mm512_setzero_pd()); }) #define _mm512_shuffle_i32x4(A, B, imm) __extension__ ({ \ - (__m512i)__builtin_ia32_shuf_i32x4_mask((__v16si)(__m512i)(A), \ - (__v16si)(__m512i)(B), (int)(imm), \ - (__v16si)_mm512_setzero_si512(), \ - (__mmask16)-1); }) + (__m512i)__builtin_shufflevector((__v8di)(__m512i)(A), \ + (__v8di)(__m512i)(B), \ + 0 + ((((imm) >> 0) & 0x3) * 2), \ + 1 + ((((imm) >> 0) & 0x3) * 2), \ + 0 + ((((imm) >> 2) & 0x3) * 2), \ + 1 + ((((imm) >> 2) & 0x3) * 2), \ + 8 + ((((imm) >> 4) & 0x3) * 2), \ + 9 + ((((imm) >> 4) & 0x3) * 2), \ + 8 + ((((imm) >> 6) & 0x3) * 2), \ + 9 + ((((imm) >> 6) & 0x3) * 2)); }) #define _mm512_mask_shuffle_i32x4(W, U, A, B, imm) __extension__ ({ \ - (__m512i)__builtin_ia32_shuf_i32x4_mask((__v16si)(__m512i)(A), \ - (__v16si)(__m512i)(B), (int)(imm), \ - (__v16si)(__m512i)(W), \ - (__mmask16)(U)); }) + (__m512i)__builtin_ia32_selectd_512((__mmask16)(U), \ + (__v16si)_mm512_shuffle_i32x4((A), (B), (imm)), \ + (__v16si)(__m512i)(W)); }) #define _mm512_maskz_shuffle_i32x4(U, A, B, imm) __extension__ ({ \ - (__m512i)__builtin_ia32_shuf_i32x4_mask((__v16si)(__m512i)(A), \ - (__v16si)(__m512i)(B), (int)(imm), \ - (__v16si)_mm512_setzero_si512(), \ - (__mmask16)(U)); }) + (__m512i)__builtin_ia32_selectd_512((__mmask16)(U), \ + (__v16si)_mm512_shuffle_i32x4((A), (B), (imm)), \ + (__v16si)_mm512_setzero_si512()); }) #define _mm512_shuffle_i64x2(A, B, imm) __extension__ ({ \ - (__m512i)__builtin_ia32_shuf_i64x2_mask((__v8di)(__m512i)(A), \ - (__v8di)(__m512i)(B), (int)(imm), \ - (__v8di)_mm512_setzero_si512(), \ - (__mmask8)-1); }) + (__m512i)__builtin_shufflevector((__v8di)(__m512i)(A), \ + (__v8di)(__m512i)(B), \ + 0 + ((((imm) >> 0) & 0x3) * 2), \ + 1 + ((((imm) >> 0) & 0x3) * 2), \ + 0 + ((((imm) >> 2) & 0x3) * 2), \ + 1 + ((((imm) >> 2) & 0x3) * 2), \ + 8 + ((((imm) >> 4) & 0x3) * 2), \ + 9 + ((((imm) >> 4) & 0x3) * 2), \ + 8 + ((((imm) >> 6) & 0x3) * 2), \ + 9 + ((((imm) >> 6) & 0x3) * 2)); }) #define _mm512_mask_shuffle_i64x2(W, U, A, B, imm) __extension__ ({ \ - (__m512i)__builtin_ia32_shuf_i64x2_mask((__v8di)(__m512i)(A), \ - (__v8di)(__m512i)(B), (int)(imm), \ - (__v8di)(__m512i)(W), \ - (__mmask8)(U)); }) + (__m512i)__builtin_ia32_selectq_512((__mmask8)(U), \ + (__v8di)_mm512_shuffle_i64x2((A), (B), (imm)), \ + (__v8di)(__m512i)(W)); }) #define _mm512_maskz_shuffle_i64x2(U, A, B, imm) __extension__ ({ \ - (__m512i)__builtin_ia32_shuf_i64x2_mask((__v8di)(__m512i)(A), \ - (__v8di)(__m512i)(B), (int)(imm), \ - (__v8di)_mm512_setzero_si512(), \ - (__mmask8)(U)); }) + (__m512i)__builtin_ia32_selectq_512((__mmask8)(U), \ + (__v8di)_mm512_shuffle_i64x2((A), (B), (imm)), \ + (__v8di)_mm512_setzero_si512()); }) #define _mm512_shuffle_pd(A, B, M) __extension__ ({ \ (__m512d)__builtin_shufflevector((__v8df)(__m512d)(A), \ @@ -9166,6 +8942,64 @@ _mm512_maskz_compress_epi32 (__mmask16 __U, __m512i __A) (__mmask8)(M), \ _MM_FROUND_CUR_DIRECTION); }) +/* Bit Test */ + +static __inline __mmask16 __DEFAULT_FN_ATTRS +_mm512_test_epi32_mask (__m512i __A, __m512i __B) +{ + return _mm512_cmpneq_epi32_mask (_mm512_and_epi32(__A, __B), + _mm512_setzero_epi32()); +} + +static __inline__ __mmask16 __DEFAULT_FN_ATTRS +_mm512_mask_test_epi32_mask (__mmask16 __U, __m512i __A, __m512i __B) +{ + return _mm512_mask_cmpneq_epi32_mask (__U, _mm512_and_epi32 (__A, __B), + _mm512_setzero_epi32()); +} + +static __inline __mmask8 __DEFAULT_FN_ATTRS +_mm512_test_epi64_mask (__m512i __A, __m512i __B) +{ + return _mm512_cmpneq_epi64_mask (_mm512_and_epi32 (__A, __B), + _mm512_setzero_epi32()); +} + +static __inline__ __mmask8 __DEFAULT_FN_ATTRS +_mm512_mask_test_epi64_mask (__mmask8 __U, __m512i __A, __m512i __B) +{ + return _mm512_mask_cmpneq_epi64_mask (__U, _mm512_and_epi32 (__A, __B), + _mm512_setzero_epi32()); +} + +static __inline__ __mmask16 __DEFAULT_FN_ATTRS +_mm512_testn_epi32_mask (__m512i __A, __m512i __B) +{ + return _mm512_cmpeq_epi32_mask (_mm512_and_epi32 (__A, __B), + _mm512_setzero_epi32()); +} + +static __inline__ __mmask16 __DEFAULT_FN_ATTRS +_mm512_mask_testn_epi32_mask (__mmask16 __U, __m512i __A, __m512i __B) +{ + return _mm512_mask_cmpeq_epi32_mask (__U, _mm512_and_epi32 (__A, __B), + _mm512_setzero_epi32()); +} + +static __inline__ __mmask8 __DEFAULT_FN_ATTRS +_mm512_testn_epi64_mask (__m512i __A, __m512i __B) +{ + return _mm512_cmpeq_epi64_mask (_mm512_and_epi32 (__A, __B), + _mm512_setzero_epi32()); +} + +static __inline__ __mmask8 __DEFAULT_FN_ATTRS +_mm512_mask_testn_epi64_mask (__mmask8 __U, __m512i __A, __m512i __B) +{ + return _mm512_mask_cmpeq_epi64_mask (__U, _mm512_and_epi32 (__A, __B), + _mm512_setzero_epi32()); +} + static __inline__ __m512 __DEFAULT_FN_ATTRS _mm512_movehdup_ps (__m512 __A) { diff --git a/deps/lld-prebuilt/lld/Config/Version.inc b/deps/lld-prebuilt/lld/Config/Version.inc index 2fb8a16222..a3f6f44588 100644 --- a/deps/lld-prebuilt/lld/Config/Version.inc +++ b/deps/lld-prebuilt/lld/Config/Version.inc @@ -1,5 +1,5 @@ -#define LLD_VERSION 5.0.0 -#define LLD_VERSION_STRING "5.0.0" +#define LLD_VERSION 5.0.1 +#define LLD_VERSION_STRING "5.0.1" #define LLD_VERSION_MAJOR 5 #define LLD_VERSION_MINOR 0 #define LLD_REVISION_STRING "" diff --git a/deps/lld/COFF/Config.h b/deps/lld/COFF/Config.h index 7f8259d016..ffbd0715cf 100644 --- a/deps/lld/COFF/Config.h +++ b/deps/lld/COFF/Config.h @@ -157,6 +157,7 @@ struct Configuration { uint32_t MinorImageVersion = 0; uint32_t MajorOSVersion = 6; uint32_t MinorOSVersion = 0; + bool CanExitEarly = false; bool DynamicBase = true; bool NxCompat = true; bool AllowIsolation = true; diff --git a/deps/lld/COFF/Driver.cpp b/deps/lld/COFF/Driver.cpp index 0dabca6e37..868ff0aa1e 100644 --- a/deps/lld/COFF/Driver.cpp +++ b/deps/lld/COFF/Driver.cpp @@ -52,15 +52,21 @@ BumpPtrAllocator BAlloc; StringSaver Saver{BAlloc}; std::vector SpecificAllocBase::Instances; -bool link(ArrayRef Args, raw_ostream &Diag) { +bool link(ArrayRef Args, bool CanExitEarly, raw_ostream &Diag) { ErrorCount = 0; ErrorOS = &Diag; Config = make(); Config->Argv = {Args.begin(), Args.end()}; Config->ColorDiagnostics = (ErrorOS == &llvm::errs() && Process::StandardErrHasColors()); + Config->CanExitEarly = CanExitEarly; Driver = make(); Driver->link(Args); + + // Call exit() if we can to avoid calling destructors. + if (CanExitEarly) + exitLld(ErrorCount ? 1 : 0); + freeArena(); return !ErrorCount; } @@ -1123,7 +1129,7 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // This is useful because MSVC link.exe can generate complete PDBs. if (Args.hasArg(OPT_msvclto)) { invokeMSVC(Args); - exit(0); + return; } // Do LTO by compiling bitcode input files to a set of native COFF files then diff --git a/deps/lld/COFF/Error.cpp b/deps/lld/COFF/Error.cpp index 34abc280f6..550d9b9696 100644 --- a/deps/lld/COFF/Error.cpp +++ b/deps/lld/COFF/Error.cpp @@ -32,7 +32,7 @@ namespace coff { uint64_t ErrorCount; raw_ostream *ErrorOS; -static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) { +LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) { // Dealloc/destroy ManagedStatic variables before calling // _exit(). In a non-LTO build, this is a nop. In an LTO // build allows us to get the output of -time-passes. @@ -78,7 +78,8 @@ void error(const Twine &Msg) { print("error: ", raw_ostream::RED); *ErrorOS << "too many errors emitted, stopping now" << " (use /ERRORLIMIT:0 to see all errors)\n"; - exitLld(1); + if (Config->CanExitEarly) + exitLld(1); } ++ErrorCount; diff --git a/deps/lld/COFF/Error.h b/deps/lld/COFF/Error.h index e1e4c1e521..1c1e2beab5 100644 --- a/deps/lld/COFF/Error.h +++ b/deps/lld/COFF/Error.h @@ -27,6 +27,8 @@ LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg); LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix); LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix); +LLVM_ATTRIBUTE_NORETURN void exitLld(int Val); + template T check(ErrorOr V, const Twine &Prefix) { if (auto EC = V.getError()) fatal(EC, Prefix); diff --git a/deps/lld/ELF/SyntheticSections.cpp b/deps/lld/ELF/SyntheticSections.cpp index 4bbec4ab34..a67b039ddf 100644 --- a/deps/lld/ELF/SyntheticSections.cpp +++ b/deps/lld/ELF/SyntheticSections.cpp @@ -427,10 +427,11 @@ CieRecord *EhFrameSection::addCie(EhSectionPiece &Piece, &Sec->template getFile()->getRelocTargetSym(Rels[FirstRelI]); // Search for an existing CIE by CIE contents/relocation target pair. - CieRecord *Cie = &CieMap[{Piece.data(), Personality}]; + CieRecord *&Cie = CieMap[{Piece.data(), Personality}]; // If not found, create a new one. - if (Cie->Piece == nullptr) { + if (!Cie) { + Cie = make(); Cie->Piece = &Piece; Cies.push_back(Cie); } @@ -522,9 +523,14 @@ template static void writeCieFde(uint8_t *Buf, ArrayRef D) { memcpy(Buf, D.data(), D.size()); + size_t Aligned = alignTo(D.size(), sizeof(typename ELFT::uint)); + + // Zero-clear trailing padding if it exists. + memset(Buf + D.size(), 0, Aligned - D.size()); + // Fix the size field. -4 since size does not include the size field itself. const endianness E = ELFT::TargetEndianness; - write32(Buf, alignTo(D.size(), sizeof(typename ELFT::uint)) - 4); + write32(Buf, Aligned - 4); } template void EhFrameSection::finalizeContents() { diff --git a/deps/lld/ELF/SyntheticSections.h b/deps/lld/ELF/SyntheticSections.h index ddd8ca99a6..ccf021ec95 100644 --- a/deps/lld/ELF/SyntheticSections.h +++ b/deps/lld/ELF/SyntheticSections.h @@ -103,7 +103,8 @@ private: std::vector Cies; // CIE records are uniquified by their contents and personality functions. - llvm::DenseMap, SymbolBody *>, CieRecord> CieMap; + llvm::DenseMap, SymbolBody *>, CieRecord *> + CieMap; }; class GotSection : public SyntheticSection { diff --git a/deps/lld/include/lld/Driver/Driver.h b/deps/lld/include/lld/Driver/Driver.h index 4ba0994e88..02c3c059d3 100644 --- a/deps/lld/include/lld/Driver/Driver.h +++ b/deps/lld/include/lld/Driver/Driver.h @@ -15,7 +15,7 @@ namespace lld { namespace coff { -bool link(llvm::ArrayRef Args, +bool link(llvm::ArrayRef Args, bool CanExitEarly, llvm::raw_ostream &Diag = llvm::errs()); } diff --git a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp index efe23abb91..b207c85523 100644 --- a/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp +++ b/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -621,7 +621,6 @@ void ArchHandler_x86_64::applyFixupFinal( // Fall into llvm_unreachable(). break; } - return; } void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, diff --git a/deps/lld/test/ELF/eh-frame-padding-no-rosegment.s b/deps/lld/test/ELF/eh-frame-padding-no-rosegment.s new file mode 100644 index 0000000000..951fed0a56 --- /dev/null +++ b/deps/lld/test/ELF/eh-frame-padding-no-rosegment.s @@ -0,0 +1,64 @@ +// REQUIRES: x86 + +.cfi_startproc +.cfi_personality 0x1b, bar +.cfi_endproc + +.global bar +.hidden bar +bar: + +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o + +// Check the size of the CIE (0x18 + 4) and FDE (0x10 + 4) +// RUN: llvm-readobj -s -section-data %t.o | FileCheck --check-prefix=OBJ %s + +// OBJ: Name: .eh_frame +// OBJ-NEXT: Type: +// OBJ-NEXT: Flags [ +// OBJ-NEXT: SHF_ALLOC +// OBJ-NEXT: ] +// OBJ-NEXT: Address: +// OBJ-NEXT: Offset: +// OBJ-NEXT: Size: +// OBJ-NEXT: Link: +// OBJ-NEXT: Info: +// OBJ-NEXT: AddressAlignment: +// OBJ-NEXT: EntrySize: +// OBJ-NEXT: SectionData ( +// OBJ-NEXT: 0000: 18000000 00000000 017A5052 00017810 +// OBJ-NEXT: 0010: 061B0000 00001B0C 07089001 10000000 +// OBJ-NEXT: 0020: 20000000 00000000 00000000 00000000 +// OBJ-NEXT: ) + +// RUN: ld.lld %t.o -no-rosegment -o %t -shared + +// Check that .eh_frame is in the same segment as .text +// RUN: llvm-readobj -l --elf-output-style=GNU %t | FileCheck --check-prefix=PHDR %s + +// PHDR: Segment Sections +// PHDR: .text +// PHDR-SAME: .eh_frame + +// Check that the CIE and FDE are padded with 0x00 and not 0xCC when the +// .eh_frame section is placed in the executable segment +// RUN: llvm-readobj -s -section-data %t | FileCheck %s + +// CHECK: Name: .eh_frame +// CHECK-NEXT: Type: +// CHECK-NEXT: Flags +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: ] +// CHECK-NEXT: Address: +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: +// CHECK-NEXT: AddressAlignment: +// CHECK-NEXT: EntrySize: +// CHECK-NEXT: SectionData ( +// CHECK-NEXT: 0000: 1C000000 00000000 017A5052 00017810 +// CHECK-NEXT: 0010: 061BBEFF FFFF1B0C 07089001 00000000 +// CHECK-NEXT: 0020: 14000000 24000000 A8FFFFFF 00000000 +// CHECK-NEXT: 0030: 00000000 00000000 +// CHECK-NEXT: ) diff --git a/deps/lld/tools/lld/lld.cpp b/deps/lld/tools/lld/lld.cpp index 09f8079010..aa81aa9712 100644 --- a/deps/lld/tools/lld/lld.cpp +++ b/deps/lld/tools/lld/lld.cpp @@ -103,7 +103,7 @@ int main(int Argc, const char **Argv) { case Gnu: return !elf::link(Args, true); case WinLink: - return !coff::link(Args); + return !coff::link(Args, true); case Darwin: return !mach_o::link(Args); default: diff --git a/doc/langref.html.in b/doc/langref.html.in index e8f76e230b..e5b896410d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -136,7 +136,8 @@
  • @divFloor
  • @divTrunc
  • @embedFile
  • -
  • @enumTagName
  • +
  • @tagName
  • +
  • @EnumTagType
  • @errorName
  • @fence
  • @fieldParentPtr
  • @@ -2095,30 +2096,38 @@ const Type = enum { NotOk, }; -// Enums are sum types, and can hold more complex data of different types. -const ComplexType = enum { - Ok: u8, - NotOk: void, -}; - // Declare a specific instance of the enum variant. -const c = ComplexType.Ok { 0 }; +const c = Type.Ok; -// The ordinal value of a simple enum with no data members can be -// retrieved by a simple cast. -// The value starts from 0, counting up for each member. -const Value = enum { +// If you want access to the ordinal value of an enum, you +// can specify the tag type. +const Value = enum(u2) { Zero, One, Two, }; + +// Now you can cast between u2 and Value. +// The ordinal value starts from 0, counting up for each member. test "enum ordinal value" { - assert(usize(Value.Zero) == 0); - assert(usize(Value.One) == 1); - assert(usize(Value.Two) == 2); + assert(u2(Value.Zero) == 0); + assert(u2(Value.One) == 1); + assert(u2(Value.Two) == 2); } -// Enums can have methods, the same as structs. +// You can override the ordinal value for an enum. +const Value2 = enum(u32) { + Hundred = 100, + Thousand = 1000, + Million = 1000000, +}; +test "set enum ordinal value" { + assert(u32(Value2.Hundred) == 100); + assert(u32(Value2.Thousand) == 1000); + assert(u32(Value2.Million) == 1000000); +} + +// Enums can have methods, the same as structs and unions. // Enum methods are not special, they are only namespaced // functions that you can call with dot syntax. const Suit = enum { @@ -2127,26 +2136,120 @@ const Suit = enum { Diamonds, Hearts, - pub fn ordinal(self: &const Suit) -> u8 { - u8(*self) + pub fn isClubs(self: Suit) -> bool { + return self == Suit.Clubs; } }; test "enum method" { const p = Suit.Spades; - assert(p.ordinal() == 1); + assert(!p.isClubs()); } // An enum variant of different types can be switched upon. -// The associated data can be retrieved using `|...|` syntax. -// -// A void type is not required on a tag-only member. const Foo = enum { - String: []const u8, - Number: u64, + String, + Number, None, }; test "enum variant switch" { - const p = Foo.Number { 54 }; + const p = Foo.Number; + const what_is_it = switch (p) { + Foo.String => "this is a string", + Foo.Number => "this is a number", + Foo.None => "this is a none", + }; + assert(mem.eql(u8, what_is_it, "this is a number")); +} + +// @TagType can be used to access the integer tag type of an enum. +const Small = enum { + One, + Two, + Three, + Four, +}; +test "@TagType" { + assert(@TagType(Small) == u2); +} + +// @memberCount tells how many fields an enum has: +test "@memberCount" { + assert(@memberCount(Small) == 4); +} + +// @memberName tells the name of a field in an enum: +test "@memberName" { + assert(mem.eql(u8, @memberName(Small, 1), "Two")); +} + +// @tagName gives a []const u8 representation of an enum value: +test "@tagName" { + assert(mem.eql(u8, @tagName(Small.Three), "Three")); +} +

    TODO extern enum

    +

    TODO packed enum

    +
    $ zig test enum.zig
    +Test 1/8 enum ordinal value...OK
    +Test 2/8 set enum ordinal value...OK
    +Test 3/8 enum method...OK
    +Test 4/8 enum variant switch...OK
    +Test 5/8 @TagType...OK
    +Test 6/8 @memberCount...OK
    +Test 7/8 @memberName...OK
    +Test 8/8 @tagName...OK
    +

    See also:

    + +

    union

    +
    const assert = @import("std").debug.assert;
    +const mem = @import("std").mem;
    +
    +// A union has only 1 active field at a time.
    +const Payload = union {
    +    Int: i64,
    +    Float: f64,
    +    Bool: bool,
    +};
    +test "simple union" {
    +    var payload = Payload {.Int = 1234};
    +    // payload.Float = 12.34; // ERROR! field not active
    +    assert(payload.Int == 1234);
    +    // You can activate another field by assigning the entire union.
    +    payload = Payload {.Float = 12.34};
    +    assert(payload.Float == 12.34);
    +}
    +
    +// Unions can be given an enum tag type:
    +const ComplexTypeTag = enum { Ok, NotOk }; 
    +const ComplexType = union(ComplexTypeTag) {
    +    Ok: u8,
    +    NotOk: void,
    +};
    +
    +// Declare a specific instance of the union variant.
    +test "declare union value" {
    +    const c = ComplexType { .Ok = 0 };
    +    assert(ComplexTypeTag(c) == ComplexTypeTag.Ok);
    +}
    +
    +// @TagType can be used to access the enum tag type of a union.
    +test "@TagType" {
    +    assert(@TagType(ComplexType) == ComplexTypeTag);
    +}
    +
    +// Unions can be made to infer the enum tag type.
    +const Foo = union(enum) {
    +    String: []const u8,
    +    Number: u64,
    +
    +    // void can be omitted when inferring enum tag type.
    +    None,
    +};
    +test "union variant switch" {
    +    const p = Foo { .Number = 54 };
         const what_is_it = switch (p) {
             // Capture by reference
             Foo.String => |*x| {
    @@ -2155,6 +2258,7 @@ test "enum variant switch" {
     
             // Capture by value
             Foo.Number => |x| {
    +            assert(x == 54);
                 "this is a number"
             },
     
    @@ -2162,37 +2266,50 @@ test "enum variant switch" {
                 "this is a none"
             }
         };
    +    assert(mem.eql(u8, what_is_it, "this is a number"));
     }
     
    -// The @enumTagName and @memberCount builtin functions can be used to
    -// the string representation and number of members respectively.
    -const BuiltinType = enum {
    -    A: f32,
    -    B: u32,
    -    C,
    +// TODO union methods
    +
    +
    +const Small = union {
    +    A: i32,
    +    B: bool,
    +    C: u8,
     };
     
    -test "enum builtins" {
    -    assert(mem.eql(u8, @enumTagName(BuiltinType.A { 0 }), "A"));
    -    assert(mem.eql(u8, @enumTagName(BuiltinType.C), "C"));
    -    assert(@memberCount(BuiltinType) == 3);
    +// @memberCount tells how many fields a union has:
    +test "@memberCount" {
    +    assert(@memberCount(Small) == 3);
    +}
    +
    +// @memberName tells the name of a field in an enum:
    +test "@memberName" {
    +    assert(mem.eql(u8, @memberName(Small, 1), "B"));
    +}
    +
    +// @tagName gives a []const u8 representation of an enum value,
    +// but only if the union has an enum tag type.
    +const Small2 = union(enum) {
    +    A: i32,
    +    B: bool,
    +    C: u8,
    +};
    +test "@tagName" {
    +    assert(mem.eql(u8, @tagName(Small2.C), "C"));
     }
    -
    $ zig test enum.zig
    -Test 1/4 enum ordinal value...OK
    -Test 2/4 enum method...OK
    -Test 3/4 enum variant switch...OK
    -Test 4/4 enum builtins...OK
    +
    $ zig test union.zig
    +Test 1/7 simple union...OK
    +Test 2/7 declare union value...OK
    +Test 3/7 @TagType...OK
    +Test 4/7 union variant switch...OK
    +Test 5/7 @memberCount...OK
    +Test 6/7 @memberName...OK
    +Test 7/7 @tagName...OK

    - Enums are generated as a struct with a tag field and union field. Zig + Unions with an enum tag are generated as a struct with a tag field and union field. Zig sorts the order of the tag and union field by the largest alignment.

    -

    See also:

    - -

    union

    -

    TODO union documentation

    switch

    const assert = @import("std").debug.assert;
     const builtin = @import("builtin");
    @@ -4251,10 +4368,15 @@ test.zig:6:2: error: found compile log statement
           
    -      

    @enumTagName

    -
    @enumTagName(value: var) -> []const u8
    +

    @tagName

    +
    @tagName(value: var) -> []const u8

    - Converts an enum tag name to a slice of bytes. + Converts an enum value or union value to a slice of bytes representing the name. +

    +

    @EnumTagType

    +
    @EnumTagType(T: type) -> type
    +

    + Returns the integer type that is used to store the enumeration value.

    @errorName

    @errorName(err: error) -> []u8
    @@ -5703,7 +5825,7 @@ VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" Typ ContainerMember = (ContainerField | FnDef | GlobalVarDecl) -ContainerField = Symbol option(":" Expression) "," +ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression "," UseDecl = "use" Expression ";" @@ -5837,7 +5959,9 @@ GroupedExpression = "(" Expression ")" KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable" -ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}"
    +ContainerDecl = option("extern" | "packed") + ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression))) + "{" many(ContainerMember) "}"

    Zen

    • Communicate intent precisely.
    • diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 20a2fefc9f..db7e38ada3 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("std"); const io = std.io; const fmt = std.fmt; @@ -15,7 +16,7 @@ pub fn main() -> %void { var seed_bytes: [@sizeOf(usize)]u8 = undefined; %%os.getRandomBytes(seed_bytes[0..]); - const seed = std.mem.readInt(seed_bytes, usize, true); + const seed = std.mem.readInt(seed_bytes, usize, builtin.Endian.Big); var rand = Rand.init(seed); const answer = rand.range(u8, 0, 100) + 1; diff --git a/src/all_types.hpp b/src/all_types.hpp index 2fccf08e88..086ad7db49 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -97,18 +97,13 @@ struct ConstParent { } data; }; -struct ConstEnumValue { - uint64_t tag; - ConstExprValue *payload; -}; - struct ConstStructValue { ConstExprValue *fields; ConstParent parent; }; struct ConstUnionValue { - uint64_t tag; + BigInt tag; ConstExprValue *payload; ConstParent parent; }; @@ -249,7 +244,7 @@ struct ConstExprValue { ConstExprValue *x_maybe; ConstErrValue x_err_union; ErrorTableEntry *x_pure_err; - ConstEnumValue x_enum; + BigInt x_enum_tag; ConstStructValue x_struct; ConstUnionValue x_union; ConstArrayValue x_array; @@ -345,15 +340,14 @@ struct TldCompTime { struct TypeEnumField { Buf *name; - TypeTableEntry *type_entry; - uint32_t value; - uint32_t gen_index; + BigInt value; + uint32_t decl_index; }; struct TypeUnionField { Buf *name; + TypeEnumField *enum_field; TypeTableEntry *type_entry; - uint32_t value; uint32_t gen_index; }; @@ -773,13 +767,15 @@ struct AstNodeContainerDecl { ZigList fields; ZigList decls; ContainerLayout layout; - AstNode *init_arg_expr; // enum(T) or struct(endianness) + AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T)) + bool auto_enum; // union(enum) }; struct AstNodeStructField { VisibMod visib_mod; Buf *name; AstNode *type; + AstNode *value; }; struct AstNodeStringLiteral { @@ -1009,12 +1005,9 @@ struct TypeTableEntryEnum { AstNode *decl_node; ContainerLayout layout; uint32_t src_field_count; - // number of fields in the union. 0 if enum with no payload - uint32_t gen_field_count; TypeEnumField *fields; bool is_invalid; // true if any fields are invalid - TypeTableEntry *tag_type; - LLVMTypeRef union_type_ref; + TypeTableEntry *tag_int_type; ScopeDecls *decls_scope; @@ -1026,18 +1019,7 @@ struct TypeTableEntryEnum { bool zero_bits_loop_flag; bool zero_bits_known; - uint32_t abi_alignment; // also figured out with zero_bits pass - size_t gen_union_index; - size_t gen_tag_index; - - uint32_t union_size_bytes; - TypeTableEntry *most_aligned_union_member; -}; - -struct TypeTableEntryEnumTag { - TypeTableEntry *enum_type; - TypeTableEntry *int_type; bool generate_name_table; LLVMValueRef name_table; }; @@ -1052,7 +1034,7 @@ struct TypeTableEntryUnion { uint32_t gen_field_count; TypeUnionField *fields; bool is_invalid; // true if any fields are invalid - TypeTableEntry *tag_type; + TypeTableEntry *tag_type; // always an enum or null LLVMTypeRef union_type_ref; ScopeDecls *decls_scope; @@ -1117,7 +1099,6 @@ enum TypeTableEntryId { TypeTableEntryIdErrorUnion, TypeTableEntryIdPureError, TypeTableEntryIdEnum, - TypeTableEntryIdEnumTag, TypeTableEntryIdUnion, TypeTableEntryIdFn, TypeTableEntryIdNamespace, @@ -1146,7 +1127,6 @@ struct TypeTableEntry { TypeTableEntryMaybe maybe; TypeTableEntryError error; TypeTableEntryEnum enumeration; - TypeTableEntryEnumTag enum_tag; TypeTableEntryUnion unionation; TypeTableEntryFn fn; TypeTableEntryBoundFn bound_fn; @@ -1285,7 +1265,8 @@ enum BuiltinFnId { BuiltinFnIdBitCast, BuiltinFnIdIntToPtr, BuiltinFnIdPtrToInt, - BuiltinFnIdEnumTagName, + BuiltinFnIdTagName, + BuiltinFnIdTagType, BuiltinFnIdFieldParentPtr, BuiltinFnIdOffsetOf, BuiltinFnIdInlineCall, @@ -1829,7 +1810,6 @@ enum IrInstructionId { IrInstructionIdStorePtr, IrInstructionIdFieldPtr, IrInstructionIdStructFieldPtr, - IrInstructionIdEnumFieldPtr, IrInstructionIdUnionFieldPtr, IrInstructionIdElemPtr, IrInstructionIdVarPtr, @@ -1854,7 +1834,7 @@ enum IrInstructionId { IrInstructionIdTestNonNull, IrInstructionIdUnwrapMaybe, IrInstructionIdMaybeWrap, - IrInstructionIdEnumTag, + IrInstructionIdUnionTag, IrInstructionIdClz, IrInstructionIdCtz, IrInstructionIdImport, @@ -1893,7 +1873,6 @@ enum IrInstructionId { IrInstructionIdErrWrapPayload, IrInstructionIdFnProto, IrInstructionIdTestComptime, - IrInstructionIdInitEnum, IrInstructionIdPtrCast, IrInstructionIdBitCast, IrInstructionIdWidenOrShorten, @@ -1910,7 +1889,8 @@ enum IrInstructionId { IrInstructionIdSetGlobalLinkage, IrInstructionIdDeclRef, IrInstructionIdPanic, - IrInstructionIdEnumTagName, + IrInstructionIdTagName, + IrInstructionIdTagType, IrInstructionIdFieldParentPtr, IrInstructionIdOffsetOf, IrInstructionIdTypeId, @@ -2088,14 +2068,6 @@ struct IrInstructionStructFieldPtr { bool is_const; }; -struct IrInstructionEnumFieldPtr { - IrInstruction base; - - IrInstruction *enum_ptr; - TypeEnumField *field; - bool is_const; -}; - struct IrInstructionUnionFieldPtr { IrInstruction base; @@ -2299,7 +2271,7 @@ struct IrInstructionClz { IrInstruction *value; }; -struct IrInstructionEnumTag { +struct IrInstructionUnionTag { IrInstruction base; IrInstruction *value; @@ -2569,15 +2541,6 @@ struct IrInstructionTestComptime { IrInstruction *value; }; -struct IrInstructionInitEnum { - IrInstruction base; - - TypeTableEntry *enum_type; - TypeEnumField *field; - IrInstruction *init_value; - LLVMValueRef tmp_ptr; -}; - struct IrInstructionPtrCast { IrInstruction base; @@ -2689,7 +2652,13 @@ struct IrInstructionPanic { IrInstruction *msg; }; -struct IrInstructionEnumTagName { +struct IrInstructionTagName { + IrInstruction base; + + IrInstruction *target; +}; + +struct IrInstructionTagType { IrInstruction base; IrInstruction *target; diff --git a/src/analyze.cpp b/src/analyze.cpp index 1c223c63f7..96f0eb44db 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -223,7 +223,6 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: return true; } @@ -260,7 +259,6 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: return true; @@ -1175,7 +1173,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: ensure_complete_type(g, type_entry); if (fn_type_id.cc == CallingConventionUnspecified && !type_is_copyable(g, type_entry)) { add_node_error(g, param_node->data.param_decl.type, @@ -1239,7 +1236,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: break; } @@ -1263,22 +1259,6 @@ bool type_is_invalid(TypeTableEntry *type_entry) { } -TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type) { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnumTag); - - buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "@enumTagType(%s)", buf_ptr(&enum_type->name)); - - entry->is_copyable = true; - entry->data.enum_tag.enum_type = enum_type; - entry->data.enum_tag.int_type = int_type; - entry->type_ref = int_type->type_ref; - entry->di_type = int_type->di_type; - entry->zero_bits = int_type->zero_bits; - - return entry; -} - static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { assert(enum_type->id == TypeTableEntryIdEnum); @@ -1308,14 +1288,6 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { assert(enum_type->data.enumeration.fields); ZigLLVMDIEnumerator **di_enumerators = allocate(field_count); - uint32_t gen_field_count = enum_type->data.enumeration.gen_field_count; - ZigLLVMDIType **union_inner_di_types = allocate(gen_field_count); - - TypeTableEntry *most_aligned_union_member = nullptr; - uint64_t size_of_most_aligned_member_in_bits = 0; - uint64_t biggest_align_in_bits = 0; - uint64_t biggest_size_in_bits = 0; - Scope *scope = &enum_type->data.enumeration.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); @@ -1323,49 +1295,17 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { enum_type->data.enumeration.embedded_in_current = true; for (uint32_t i = 0; i < field_count; i += 1) { - AstNode *field_node = decl_node->data.container_decl.fields.at(i); - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; - TypeTableEntry *field_type = type_enum_field->type_entry; + TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i]; - di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i); - - ensure_complete_type(g, field_type); - if (type_is_invalid(field_type)) { - enum_type->data.enumeration.is_invalid = true; - continue; - } - - if (!type_has_bits(field_type)) - continue; - - uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); - uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); - - assert(store_size_in_bits > 0); - assert(abi_align_in_bits > 0); - - union_inner_di_types[type_enum_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(enum_type->di_type), buf_ptr(type_enum_field->name), - import->di_file, (unsigned)(field_node->line + 1), - store_size_in_bits, - abi_align_in_bits, - 0, - 0, field_type->di_type); - - biggest_size_in_bits = max(biggest_size_in_bits, store_size_in_bits); - - if (!most_aligned_union_member || abi_align_in_bits > biggest_align_in_bits) { - most_aligned_union_member = field_type; - biggest_align_in_bits = abi_align_in_bits; - size_of_most_aligned_member_in_bits = store_size_in_bits; - } + // TODO send patch to LLVM to support APInt in createEnumerator instead of int64_t + // http://lists.llvm.org/pipermail/llvm-dev/2017-December/119456.html + di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(enum_field->name), + bigint_as_signed(&enum_field->value)); } // unset temporary flag enum_type->data.enumeration.embedded_in_current = false; enum_type->data.enumeration.complete = true; - enum_type->data.enumeration.union_size_bytes = biggest_size_in_bits / 8; - enum_type->data.enumeration.most_aligned_union_member = most_aligned_union_member; if (enum_type->data.enumeration.is_invalid) return; @@ -1390,118 +1330,21 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) { return; } - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); - TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type); - enum_type->data.enumeration.tag_type = tag_type_entry; + TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type; - uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); + // create debug type for tag + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_int_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); + ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, + ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), + import->di_file, (unsigned)(decl_node->line + 1), + tag_debug_size_in_bits, + tag_debug_align_in_bits, + di_enumerators, field_count, + tag_int_type->di_type, ""); - if (most_aligned_union_member) { - // create llvm type for union - uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; - LLVMTypeRef union_type_ref; - if (padding_in_bits > 0) { - TypeTableEntry *u8_type = get_int_type(g, false, 8); - TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); - LLVMTypeRef union_element_types[] = { - most_aligned_union_member->type_ref, - padding_array->type_ref, - }; - union_type_ref = LLVMStructType(union_element_types, 2, false); - } else { - union_type_ref = most_aligned_union_member->type_ref; - } - enum_type->data.enumeration.union_type_ref = union_type_ref; - - assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits); - assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits); - - if (align_of_tag_in_bits >= biggest_align_in_bits) { - enum_type->data.enumeration.gen_tag_index = 0; - enum_type->data.enumeration.gen_union_index = 1; - } else { - enum_type->data.enumeration.gen_union_index = 0; - enum_type->data.enumeration.gen_tag_index = 1; - } - - // create llvm type for root struct - LLVMTypeRef root_struct_element_types[2]; - root_struct_element_types[enum_type->data.enumeration.gen_tag_index] = tag_type_entry->type_ref; - root_struct_element_types[enum_type->data.enumeration.gen_union_index] = union_type_ref; - LLVMStructSetBody(enum_type->type_ref, root_struct_element_types, 2, false); - - // create debug type for tag - uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); - ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, - ZigLLVMTypeToScope(enum_type->di_type), "AnonEnum", - import->di_file, (unsigned)(decl_node->line + 1), - tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, - tag_type_entry->di_type, ""); - - // create debug type for union - ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, - ZigLLVMTypeToScope(enum_type->di_type), "AnonUnion", - import->di_file, (unsigned)(decl_node->line + 1), - biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, - gen_field_count, 0, ""); - - // create debug types for members of root struct - uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, - enum_type->data.enumeration.gen_tag_index); - ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(enum_type->di_type), "tag_field", - import->di_file, (unsigned)(decl_node->line + 1), - tag_debug_size_in_bits, - tag_debug_align_in_bits, - tag_offset_in_bits, - 0, tag_di_type); - - uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, enum_type->type_ref, - enum_type->data.enumeration.gen_union_index); - ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(enum_type->di_type), "union_field", - import->di_file, (unsigned)(decl_node->line + 1), - biggest_size_in_bits, - biggest_align_in_bits, - union_offset_in_bits, - 0, union_di_type); - - // create debug type for root struct - ZigLLVMDIType *di_root_members[2]; - di_root_members[enum_type->data.enumeration.gen_tag_index] = tag_member_di_type; - di_root_members[enum_type->data.enumeration.gen_union_index] = union_member_di_type; - - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, enum_type->type_ref); - uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, enum_type->type_ref); - ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, - ZigLLVMFileToScope(import->di_file), - buf_ptr(&enum_type->name), - import->di_file, (unsigned)(decl_node->line + 1), - debug_size_in_bits, - debug_align_in_bits, - 0, nullptr, di_root_members, 2, 0, nullptr, ""); - - ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type); - enum_type->di_type = replacement_di_type; - } else { - // create llvm type for root struct - enum_type->type_ref = tag_type_entry->type_ref; - - // create debug type for tag - uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); - ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, - ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), - import->di_file, (unsigned)(decl_node->line + 1), - tag_debug_size_in_bits, - tag_debug_align_in_bits, - di_enumerators, field_count, - tag_type_entry->di_type, ""); - - ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); - enum_type->di_type = tag_di_type; - } + ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type); + enum_type->di_type = tag_di_type; } static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { @@ -1517,8 +1360,6 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: - case TypeTableEntryIdEnum: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -1541,6 +1382,8 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { TypeTableEntry *child_type = type_entry->data.maybe.child_type; return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; } + case TypeTableEntryIdEnum: + return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr; } zig_unreachable(); } @@ -1658,7 +1501,6 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { TypeTableEntry *field_type = type_struct_field->type_entry; ensure_complete_type(g, field_type); - if (type_is_invalid(field_type)) { struct_type->data.structure.is_invalid = true; break; @@ -1849,6 +1691,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { if (union_type->data.unionation.embedded_in_current) { if (!union_type->data.unionation.reported_infinite_err) { union_type->data.unionation.reported_infinite_err = true; + union_type->data.unionation.is_invalid = true; add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name))); } return; @@ -1870,19 +1713,17 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { uint64_t biggest_align_in_bits = 0; uint64_t biggest_size_in_bits = 0; - bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); - ZigLLVMDIEnumerator **di_enumerators = allocate(field_count); - Scope *scope = &union_type->data.unionation.decls_scope->base; ImportTableEntry *import = get_scope_import(scope); // set temporary flag union_type->data.unionation.embedded_in_current = true; + for (uint32_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); - TypeUnionField *type_union_field = &union_type->data.unionation.fields[i]; - TypeTableEntry *field_type = type_union_field->type_entry; + TypeUnionField *union_field = &union_type->data.unionation.fields[i]; + TypeTableEntry *field_type = union_field->type_entry; ensure_complete_type(g, field_type); if (type_is_invalid(field_type)) { @@ -1893,16 +1734,14 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { if (!type_has_bits(field_type)) continue; - di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_union_field->name), i); - uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); assert(store_size_in_bits > 0); assert(abi_align_in_bits > 0); - union_inner_di_types[type_union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(union_type->di_type), buf_ptr(type_union_field->name), + union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, + ZigLLVMTypeToScope(union_type->di_type), buf_ptr(union_field->enum_field->name), import->di_file, (unsigned)(field_node->line + 1), store_size_in_bits, abi_align_in_bits, @@ -1918,6 +1757,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { } } + // unset temporary flag union_type->data.unionation.embedded_in_current = false; union_type->data.unionation.complete = true; @@ -1947,13 +1787,12 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { return; } - assert(most_aligned_union_member != nullptr); - - bool want_safety = auto_layout && (field_count >= 2); uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits; + TypeTableEntry *tag_type = union_type->data.unionation.tag_type; + if (tag_type == nullptr) { + assert(most_aligned_union_member != nullptr); - if (!want_safety) { if (padding_in_bits > 0) { TypeTableEntry *u8_type = get_int_type(g, false, 8); TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8); @@ -1993,6 +1832,14 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { padding_array->type_ref, }; union_type_ref = LLVMStructType(union_element_types, 2, false); + } else if (most_aligned_union_member == nullptr) { + union_type->data.unionation.gen_tag_index = SIZE_MAX; + union_type->data.unionation.gen_union_index = SIZE_MAX; + union_type->type_ref = tag_type->type_ref; + + ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, tag_type->di_type); + union_type->di_type = tag_type->di_type; + return; } else { union_type_ref = most_aligned_union_member->type_ref; } @@ -2002,9 +1849,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits); // create llvm type for root struct - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); - TypeTableEntry *tag_type_entry = tag_int_type; - union_type->data.unionation.tag_type = tag_type_entry; + TypeTableEntry *tag_int_type = tag_type->data.enumeration.tag_int_type; uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); if (align_of_tag_in_bits >= biggest_align_in_bits) { @@ -2016,22 +1861,11 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { } LLVMTypeRef root_struct_element_types[2]; - root_struct_element_types[union_type->data.unionation.gen_tag_index] = tag_type_entry->type_ref; + root_struct_element_types[union_type->data.unionation.gen_tag_index] = tag_type->type_ref; root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref; LLVMStructSetBody(union_type->type_ref, root_struct_element_types, 2, false); - // create debug type for root struct - - // create debug type for tag - uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref); - ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, - ZigLLVMTypeToScope(union_type->di_type), "AnonEnum", - import->di_file, (unsigned)(decl_node->line + 1), - tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, - tag_type_entry->di_type, ""); - // create debug type for union ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, ZigLLVMTypeToScope(union_type->di_type), "AnonUnion", @@ -2045,19 +1879,23 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) { union_type->data.unionation.gen_tag_index); ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(union_type->di_type), "union_field", + ZigLLVMTypeToScope(union_type->di_type), "payload", import->di_file, (unsigned)(decl_node->line + 1), biggest_size_in_bits, biggest_align_in_bits, union_offset_in_bits, 0, union_di_type); + + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); + ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(union_type->di_type), "tag_field", + ZigLLVMTypeToScope(union_type->di_type), "tag", import->di_file, (unsigned)(decl_node->line + 1), tag_debug_size_in_bits, tag_debug_align_in_bits, tag_offset_in_bits, - 0, tag_di_type); + 0, tag_type->di_type); ZigLLVMDIType *di_root_members[2]; di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type; @@ -2096,51 +1934,124 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { assert(!enum_type->data.enumeration.fields); uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; + if (field_count == 0) { + add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields")); + + enum_type->data.enumeration.src_field_count = field_count; + enum_type->data.enumeration.fields = nullptr; + enum_type->data.enumeration.is_invalid = true; + enum_type->data.enumeration.zero_bits_loop_flag = false; + enum_type->data.enumeration.zero_bits_known = true; + return; + } + enum_type->data.enumeration.src_field_count = field_count; enum_type->data.enumeration.fields = allocate(field_count); - uint32_t biggest_align_bytes = 0; - Scope *scope = &enum_type->data.enumeration.decls_scope->base; - uint32_t gen_field_index = 0; - for (uint32_t i = 0; i < field_count; i += 1) { - AstNode *field_node = decl_node->data.container_decl.fields.at(i); - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; - type_enum_field->name = field_node->data.struct_field.name; - TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); - type_enum_field->type_entry = field_type; - type_enum_field->value = i; + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); - type_ensure_zero_bits_known(g, field_type); - if (type_is_invalid(field_type)) { + TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + + if (decl_node->data.container_decl.init_arg_expr != nullptr) { + TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); + if (type_is_invalid(wanted_tag_int_type)) { enum_type->data.enumeration.is_invalid = true; - continue; + } else if (wanted_tag_int_type->id != TypeTableEntryIdInt) { + enum_type->data.enumeration.is_invalid = true; + add_node_error(g, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); + } else if (wanted_tag_int_type->data.integral.is_signed) { + enum_type->data.enumeration.is_invalid = true; + add_node_error(g, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("expected unsigned integer, found '%s'", buf_ptr(&wanted_tag_int_type->name))); + } else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) { + enum_type->data.enumeration.is_invalid = true; + add_node_error(g, decl_node->data.container_decl.init_arg_expr, + buf_sprintf("'%s' too small to hold all bits; must be at least '%s'", + buf_ptr(&wanted_tag_int_type->name), buf_ptr(&tag_int_type->name))); + } else { + tag_int_type = wanted_tag_int_type; + } + } + enum_type->data.enumeration.tag_int_type = tag_int_type; + enum_type->type_ref = tag_int_type->type_ref; + + for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { + AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; + type_enum_field->name = field_node->data.struct_field.name; + type_enum_field->decl_index = field_i; + + if (field_node->data.struct_field.type != nullptr) { + ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.type, + buf_sprintf("structs and unions, not enums, support field types")); + add_error_note(g, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); } - if (!type_has_bits(field_type)) - continue; + AstNode *tag_value = field_node->data.struct_field.value; - type_enum_field->gen_index = gen_field_index; - gen_field_index += 1; + // In this first pass we resolve explicit tag values. + // In a second pass we will fill in the unspecified ones. + if (tag_value != nullptr) { + IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (result_inst->value.type->id == TypeTableEntryIdInvalid) { + enum_type->data.enumeration.is_invalid = true; + continue; + } + assert(result_inst->value.special != ConstValSpecialRuntime); + assert(result_inst->value.type->id == TypeTableEntryIdInt); + auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + if (entry == nullptr) { + bigint_init_bigint(&type_enum_field->value, &result_inst->value.data.x_bigint); + } else { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); - uint32_t field_align_bytes = get_abi_alignment(g, field_type); - if (field_align_bytes > biggest_align_bytes) { - biggest_align_bytes = field_align_bytes; + ErrorMsg *msg = add_node_error(g, tag_value, + buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); + add_error_note(g, msg, entry->value, + buf_sprintf("other occurrence here")); + enum_type->data.enumeration.is_invalid = true; + continue; + } + } + } + + // Now iterate again and populate the unspecified tag values + uint32_t next_maybe_unoccupied_index = 0; + + for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { + AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; + AstNode *tag_value = field_node->data.struct_field.value; + + if (tag_value == nullptr) { + if (occupied_tag_values.size() == 0) { + bigint_init_unsigned(&type_enum_field->value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + } else { + BigInt proposed_value; + for (;;) { + bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + auto entry = occupied_tag_values.put_unique(proposed_value, field_node); + if (entry != nullptr) { + continue; + } + break; + } + bigint_init_bigint(&type_enum_field->value, &proposed_value); + } } } enum_type->data.enumeration.zero_bits_loop_flag = false; - enum_type->data.enumeration.gen_field_count = gen_field_index; - enum_type->zero_bits = (gen_field_index == 0 && field_count < 2); + enum_type->zero_bits = (field_count < 2); enum_type->data.enumeration.zero_bits_known = true; - - // also compute abi_alignment - if (!enum_type->zero_bits) { - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count); - uint32_t align_of_tag_in_bytes = LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); - enum_type->data.enumeration.abi_alignment = max(align_of_tag_in_bytes, biggest_align_bytes); - } } static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { @@ -2184,11 +2095,23 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; type_struct_field->name = field_node->data.struct_field.name; + + if (field_node->data.struct_field.type == nullptr) { + add_node_error(g, field_node, buf_sprintf("struct field missing type")); + struct_type->data.structure.is_invalid = true; + continue; + } + TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); type_struct_field->type_entry = field_type; type_struct_field->src_index = i; type_struct_field->gen_index = SIZE_MAX; + if (field_node->data.struct_field.value != nullptr) { + add_node_error(g, field_node->data.struct_field.value, + buf_sprintf("enums, not structs, support field assignment")); + } + type_ensure_zero_bits_known(g, field_type); if (type_is_invalid(field_type)) { struct_type->data.structure.is_invalid = true; @@ -2225,8 +2148,23 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { if (union_type->data.unionation.zero_bits_known) return; + if (type_is_invalid(union_type)) + return; + if (union_type->data.unionation.zero_bits_loop_flag) { + // If we get here it's due to recursion. From this we conclude that the struct is + // not zero bits, and if abi_alignment == 0 we further conclude that the first field + // is a pointer to this very struct, or a function pointer with parameters that + // reference such a type. union_type->data.unionation.zero_bits_known = true; + if (union_type->data.unionation.abi_alignment == 0) { + if (union_type->data.unionation.layout == ContainerLayoutPacked) { + union_type->data.unionation.abi_alignment = 1; + } else { + union_type->data.unionation.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, + LLVMPointerType(LLVMInt8Type(), 0)); + } + } return; } @@ -2238,6 +2176,16 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { assert(!union_type->data.unionation.fields); uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length; + if (field_count == 0) { + add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields")); + + union_type->data.unionation.src_field_count = field_count; + union_type->data.unionation.fields = nullptr; + union_type->data.unionation.is_invalid = true; + union_type->data.unionation.zero_bits_loop_flag = false; + union_type->data.unionation.zero_bits_known = true; + return; + } union_type->data.unionation.src_field_count = field_count; union_type->data.unionation.fields = allocate(field_count); @@ -2245,44 +2193,261 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { Scope *scope = &union_type->data.unionation.decls_scope->base; + HashMap occupied_tag_values = {}; + + AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr; + bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); + bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr); + TypeTableEntry *tag_type; + bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety); + bool *covered_enum_fields; + ZigLLVMDIEnumerator **di_enumerators; + if (create_enum_type) { + occupied_tag_values.init(field_count); + + di_enumerators = allocate(field_count); + + TypeTableEntry *tag_int_type; + if (enum_type_node != nullptr) { + tag_int_type = analyze_type_expr(g, scope, enum_type_node); + if (type_is_invalid(tag_int_type)) { + union_type->data.unionation.is_invalid = true; + return; + } + if (tag_int_type->id != TypeTableEntryIdInt) { + add_node_error(g, enum_type_node, + buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name))); + union_type->data.unionation.is_invalid = true; + return; + } + } else { + tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + } + union_type->data.unionation.abi_alignment = get_abi_alignment(g, tag_int_type); + + tag_type = new_type_table_entry(TypeTableEntryIdEnum); + buf_resize(&tag_type->name, 0); + buf_appendf(&tag_type->name, "@EnumTagType(%s)", buf_ptr(&union_type->name)); + tag_type->is_copyable = true; + tag_type->type_ref = tag_int_type->type_ref; + tag_type->zero_bits = tag_int_type->zero_bits; + + tag_type->data.enumeration.tag_int_type = tag_int_type; + tag_type->data.enumeration.zero_bits_known = true; + tag_type->data.enumeration.decl_node = decl_node; + tag_type->data.enumeration.layout = ContainerLayoutAuto; + tag_type->data.enumeration.src_field_count = field_count; + tag_type->data.enumeration.fields = allocate(field_count); + tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope; + tag_type->data.enumeration.complete = true; + } else if (enum_type_node != nullptr) { + TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node); + if (type_is_invalid(enum_type)) { + union_type->data.unionation.is_invalid = true; + union_type->data.unionation.embedded_in_current = false; + return; + } + if (enum_type->id != TypeTableEntryIdEnum) { + union_type->data.unionation.is_invalid = true; + union_type->data.unionation.embedded_in_current = false; + add_node_error(g, enum_type_node, + buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name))); + return; + } + tag_type = enum_type; + covered_enum_fields = allocate(enum_type->data.enumeration.src_field_count); + union_type->data.unionation.abi_alignment = get_abi_alignment(g, enum_type); + } else { + tag_type = nullptr; + } + union_type->data.unionation.tag_type = tag_type; + uint32_t gen_field_index = 0; for (uint32_t i = 0; i < field_count; i += 1) { AstNode *field_node = decl_node->data.container_decl.fields.at(i); - TypeUnionField *type_union_field = &union_type->data.unionation.fields[i]; - type_union_field->name = field_node->data.struct_field.name; - TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); - type_union_field->type_entry = field_type; - type_union_field->value = i; + Buf *field_name = field_node->data.struct_field.name; + TypeUnionField *union_field = &union_type->data.unionation.fields[i]; + union_field->name = field_node->data.struct_field.name; - type_ensure_zero_bits_known(g, field_type); - if (type_is_invalid(field_type)) { - union_type->data.unionation.is_invalid = true; - continue; + TypeTableEntry *field_type; + if (field_node->data.struct_field.type == nullptr) { + if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { + field_type = g->builtin_types.entry_void; + } else { + add_node_error(g, field_node, buf_sprintf("union field missing type")); + union_type->data.unionation.is_invalid = true; + continue; + } + } else { + field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type); + type_ensure_zero_bits_known(g, field_type); + if (type_is_invalid(field_type)) { + union_type->data.unionation.is_invalid = true; + continue; + } } + union_field->type_entry = field_type; + + if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) { + ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value, + buf_sprintf("non-enum union field assignment")); + add_error_note(g, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); + } + + if (create_enum_type) { + di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field_name), i); + union_field->enum_field = &tag_type->data.enumeration.fields[i]; + union_field->enum_field->name = field_name; + union_field->enum_field->decl_index = i; + + AstNode *tag_value = field_node->data.struct_field.value; + // In this first pass we resolve explicit tag values. + // In a second pass we will fill in the unspecified ones. + if (tag_value != nullptr) { + TypeTableEntry *tag_int_type = tag_type->data.enumeration.tag_int_type; + IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (result_inst->value.type->id == TypeTableEntryIdInvalid) { + union_type->data.unionation.is_invalid = true; + continue; + } + assert(result_inst->value.special != ConstValSpecialRuntime); + assert(result_inst->value.type->id == TypeTableEntryIdInt); + auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + if (entry == nullptr) { + bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); + } else { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + + ErrorMsg *msg = add_node_error(g, tag_value, + buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); + add_error_note(g, msg, entry->value, + buf_sprintf("other occurrence here")); + union_type->data.unionation.is_invalid = true; + continue; + } + } + } else if (enum_type_node != nullptr) { + union_field->enum_field = find_enum_type_field(tag_type, field_name); + if (union_field->enum_field == nullptr) { + ErrorMsg *msg = add_node_error(g, field_node, + buf_sprintf("enum field not found: '%s'", buf_ptr(field_name))); + add_error_note(g, msg, tag_type->data.enumeration.decl_node, + buf_sprintf("enum declared here")); + union_type->data.unionation.is_invalid = true; + continue; + } + covered_enum_fields[union_field->enum_field->decl_index] = true; + } else { + union_field->enum_field = allocate(1); + union_field->enum_field->name = field_name; + union_field->enum_field->decl_index = i; + bigint_init_unsigned(&union_field->enum_field->value, i); + } + assert(union_field->enum_field != nullptr); if (!type_has_bits(field_type)) continue; - type_union_field->gen_index = gen_field_index; + union_field->gen_index = gen_field_index; gen_field_index += 1; uint32_t field_align_bytes = get_abi_alignment(g, field_type); if (field_align_bytes > biggest_align_bytes) { biggest_align_bytes = field_align_bytes; + if (biggest_align_bytes > union_type->data.unionation.abi_alignment) { + union_type->data.unionation.abi_alignment = biggest_align_bytes; + } } } - bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); + if (union_type->data.unionation.is_invalid) + return; + + bool src_have_tag = decl_node->data.container_decl.auto_enum || + decl_node->data.container_decl.init_arg_expr != nullptr; + + if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) { + const char *qual_str; + switch (union_type->data.unionation.layout) { + case ContainerLayoutAuto: + zig_unreachable(); + case ContainerLayoutPacked: + qual_str = "packed"; + break; + case ContainerLayoutExtern: + qual_str = "extern"; + break; + } + AstNode *source_node = (decl_node->data.container_decl.init_arg_expr != nullptr) ? + decl_node->data.container_decl.init_arg_expr : decl_node; + add_node_error(g, source_node, + buf_sprintf("%s union does not support enum tag type", qual_str)); + union_type->data.unionation.is_invalid = true; + return; + } + + if (create_enum_type) { + // Now iterate again and populate the unspecified tag values + uint32_t next_maybe_unoccupied_index = 0; + + for (uint32_t field_i = 0; field_i < field_count; field_i += 1) { + AstNode *field_node = decl_node->data.container_decl.fields.at(field_i); + TypeUnionField *union_field = &union_type->data.unionation.fields[field_i]; + AstNode *tag_value = field_node->data.struct_field.value; + + if (tag_value == nullptr) { + if (occupied_tag_values.size() == 0) { + bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + } else { + BigInt proposed_value; + for (;;) { + bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index); + next_maybe_unoccupied_index += 1; + auto entry = occupied_tag_values.put_unique(proposed_value, field_node); + if (entry != nullptr) { + continue; + } + break; + } + bigint_init_bigint(&union_field->enum_field->value, &proposed_value); + } + } + } + } else if (enum_type_node != nullptr) { + for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i]; + if (!covered_enum_fields[i]) { + AstNode *enum_decl_node = tag_type->data.enumeration.decl_node; + AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i); + ErrorMsg *msg = add_node_error(g, decl_node, + buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name))); + add_error_note(g, msg, field_node, + buf_sprintf("declared here")); + union_type->data.unionation.is_invalid = true; + } + } + } + + if (create_enum_type) { + ImportTableEntry *import = get_scope_import(scope); + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); + // TODO get a more accurate debug scope + ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, + ZigLLVMFileToScope(import->di_file), buf_ptr(&tag_type->name), + import->di_file, (unsigned)(decl_node->line + 1), + tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, + tag_type->di_type, ""); + tag_type->di_type = tag_di_type; + } union_type->data.unionation.zero_bits_loop_flag = false; union_type->data.unionation.gen_field_count = gen_field_index; - union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !auto_layout)); + union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !src_have_tag)); union_type->data.unionation.zero_bits_known = true; - - // also compute abi_alignment - if (!union_type->zero_bits) { - union_type->data.unionation.abi_alignment = biggest_align_bytes; - } } static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) { @@ -2741,7 +2906,6 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: return type_entry; } zig_unreachable(); @@ -3158,13 +3322,37 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) { assert(type_entry->data.unionation.complete); for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { TypeUnionField *field = &type_entry->data.unionation.fields[i]; - if (buf_eql_buf(field->name, name)) { + if (buf_eql_buf(field->enum_field->name, name)) { return field; } } return nullptr; } +TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) { + assert(type_entry->id == TypeTableEntryIdUnion); + assert(type_entry->data.unionation.complete); + assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX); + for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { + TypeUnionField *field = &type_entry->data.unionation.fields[i]; + if (bigint_cmp(&field->enum_field->value, tag) == CmpEQ) { + return field; + } + } + return nullptr; +} + +TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag) { + for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *field = &enum_type->data.enumeration.fields[i]; + if (bigint_cmp(&field->value, tag) == CmpEQ) { + return field; + } + } + return nullptr; +} + + static bool is_container(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: @@ -3193,7 +3381,6 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: return false; @@ -3244,7 +3431,6 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdBoundFn: case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: zig_unreachable(); @@ -3698,7 +3884,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdPointer: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: + case TypeTableEntryIdEnum: return false; case TypeTableEntryIdArray: case TypeTableEntryIdStruct: @@ -3706,9 +3892,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { return type_has_bits(type_entry); case TypeTableEntryIdErrorUnion: return type_has_bits(type_entry->data.error.child_type); - case TypeTableEntryIdEnum: - assert(type_entry->data.enumeration.complete); - return type_entry->data.enumeration.gen_field_count != 0; case TypeTableEntryIdMaybe: return type_has_bits(type_entry->data.maybe.child_type) && type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && @@ -3850,7 +4033,6 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return (uint32_t)4149439618; case TypeTableEntryIdInt: case TypeTableEntryIdNumLitInt: - case TypeTableEntryIdEnumTag: { uint32_t result = 1331471175; for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) { @@ -3859,6 +4041,15 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { } return result; } + case TypeTableEntryIdEnum: + { + uint32_t result = 31643936; + for (size_t i = 0; i < const_val->data.x_enum_tag.digit_count; i += 1) { + uint64_t digit = bigint_ptr(&const_val->data.x_enum_tag)[i]; + result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result); + } + return result; + } case TypeTableEntryIdFloat: switch (const_val->type->data.floating.bit_count) { case 32: @@ -3959,9 +4150,6 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdPureError: // TODO better hashing algorithm return 2630160122; - case TypeTableEntryIdEnum: - // TODO better hashing algorithm - return 31643936; case TypeTableEntryIdFn: return 4133894920 ^ hash_ptr(const_val->data.x_fn.fn_entry); case TypeTableEntryIdNamespace: @@ -4094,7 +4282,6 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdVoid: case TypeTableEntryIdUnreachable: return false; @@ -4153,6 +4340,19 @@ ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *str) { return const_val; } +void init_const_bigint(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *bigint) { + const_val->special = ConstValSpecialStatic; + const_val->type = type; + bigint_init_bigint(&const_val->data.x_bigint, bigint); +} + +ConstExprValue *create_const_bigint(TypeTableEntry *type, const BigInt *bigint) { + ConstExprValue *const_val = create_const_vals(1); + init_const_bigint(const_val, type, bigint); + return const_val; +} + + void init_const_unsigned_negative(ConstExprValue *const_val, TypeTableEntry *type, uint64_t x, bool negative) { const_val->special = ConstValSpecialStatic; const_val->type = type; @@ -4216,18 +4416,19 @@ ConstExprValue *create_const_float(TypeTableEntry *type, double value) { return const_val; } -void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, uint64_t tag) { +void init_const_enum(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *tag) { const_val->special = ConstValSpecialStatic; const_val->type = type; - const_val->data.x_enum.tag = tag; + bigint_init_bigint(&const_val->data.x_enum_tag, tag); } -ConstExprValue *create_const_enum_tag(TypeTableEntry *type, uint64_t tag) { +ConstExprValue *create_const_enum(TypeTableEntry *type, const BigInt *tag) { ConstExprValue *const_val = create_const_vals(1); - init_const_enum_tag(const_val, type, tag); + init_const_enum(const_val, type, tag); return const_val; } + void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value) { const_val->special = ConstValSpecialStatic; const_val->type = g->builtin_types.entry_bool; @@ -4426,19 +4627,22 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdOpaque: zig_unreachable(); case TypeTableEntryIdEnum: - { - ConstEnumValue *enum1 = &a->data.x_enum; - ConstEnumValue *enum2 = &b->data.x_enum; - if (enum1->tag == enum2->tag) { - TypeEnumField *enum_field = &a->type->data.enumeration.fields[enum1->tag]; - if (type_has_bits(enum_field->type_entry)) { - zig_panic("TODO const expr analyze enum special value for equality"); - } else { - return true; - } + return bigint_cmp(&a->data.x_enum_tag, &b->data.x_enum_tag) == CmpEQ; + case TypeTableEntryIdUnion: { + ConstUnionValue *union1 = &a->data.x_union; + ConstUnionValue *union2 = &b->data.x_union; + + if (bigint_cmp(&union1->tag, &union2->tag) == CmpEQ) { + TypeUnionField *field = find_union_field_by_tag(a->type, &union1->tag); + assert(field != nullptr); + if (type_has_bits(field->type_entry)) { + zig_panic("TODO const expr analyze union field value for equality"); + } else { + return true; } - return false; } + return false; + } case TypeTableEntryIdMetaType: return a->data.x_type == b->data.x_type; case TypeTableEntryIdVoid: @@ -4465,7 +4669,6 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ; case TypeTableEntryIdInt: case TypeTableEntryIdNumLitInt: - case TypeTableEntryIdEnumTag: return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; case TypeTableEntryIdPointer: if (a->data.x_ptr.special != b->data.x_ptr.special) @@ -4519,8 +4722,6 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return false; } return true; - case TypeTableEntryIdUnion: - zig_panic("TODO"); case TypeTableEntryIdUndefLit: zig_panic("TODO"); case TypeTableEntryIdNullLit: @@ -4664,7 +4865,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name)); return; case TypeTableEntryIdUnreachable: - buf_appendf(buf, "@unreachable()"); + buf_appendf(buf, "unreachable"); return; case TypeTableEntryIdBool: { @@ -4794,7 +4995,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case TypeTableEntryIdEnum: { - buf_appendf(buf, "(enum %s constant)", buf_ptr(&type_entry->name)); + TypeEnumField *field = find_enum_field_by_tag(type_entry, &const_val->data.x_enum_tag); + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(field->name)); return; } case TypeTableEntryIdErrorUnion: @@ -4812,14 +5014,6 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "(pure error constant)"); return; } - case TypeTableEntryIdEnumTag: - { - TypeTableEntry *enum_type = type_entry->data.enum_tag.enum_type; - size_t field_index = bigint_as_unsigned(&const_val->data.x_bigint); - TypeEnumField *field = &enum_type->data.enumeration.fields[field_index]; - buf_appendf(buf, "%s.%s", buf_ptr(&enum_type->name), buf_ptr(field->name)); - return; - } case TypeTableEntryIdArgTuple: { buf_appendf(buf, "(args value)"); @@ -4830,11 +5024,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { - assert(size_in_bits > 0); - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->is_copyable = true; - entry->type_ref = LLVMIntType(size_in_bits); + entry->type_ref = (size_in_bits == 0) ? LLVMVoidType() : LLVMIntType(size_in_bits); + entry->zero_bits = (size_in_bits == 0); const char u_or_i = is_signed ? 'i' : 'u'; buf_resize(&entry->name, 0); @@ -4855,7 +5048,8 @@ TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) } } - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); + uint64_t debug_size_in_bits = (size_in_bits == 0) ? + 0 : (8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref)); entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), debug_size_in_bits, dwarf_tag); entry->data.integral.is_signed = is_signed; entry->data.integral.bit_count = size_in_bits; @@ -4881,7 +5075,6 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: @@ -4926,7 +5119,6 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: @@ -5041,7 +5233,6 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdErrorUnion, TypeTableEntryIdPureError, TypeTableEntryIdEnum, - TypeTableEntryIdEnumTag, TypeTableEntryIdUnion, TypeTableEntryIdFn, TypeTableEntryIdNamespace, @@ -5099,22 +5290,20 @@ size_t type_id_index(TypeTableEntryId id) { return 15; case TypeTableEntryIdEnum: return 16; - case TypeTableEntryIdEnumTag: - return 17; case TypeTableEntryIdUnion: - return 18; + return 17; case TypeTableEntryIdFn: - return 19; + return 18; case TypeTableEntryIdNamespace: - return 20; + return 19; case TypeTableEntryIdBlock: - return 21; + return 20; case TypeTableEntryIdBoundFn: - return 22; + return 21; case TypeTableEntryIdArgTuple: - return 23; + return 22; case TypeTableEntryIdOpaque: - return 24; + return 23; } zig_unreachable(); } @@ -5158,8 +5347,6 @@ const char *type_id_name(TypeTableEntryId id) { return "Error"; case TypeTableEntryIdEnum: return "Enum"; - case TypeTableEntryIdEnumTag: - return "EnumTag"; case TypeTableEntryIdUnion: return "Union"; case TypeTableEntryIdFn: @@ -5226,9 +5413,6 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { if (type_entry->id == TypeTableEntryIdStruct) { assert(type_entry->data.structure.abi_alignment != 0); return type_entry->data.structure.abi_alignment; - } else if (type_entry->id == TypeTableEntryIdEnum) { - assert(type_entry->data.enumeration.abi_alignment != 0); - return type_entry->data.enumeration.abi_alignment; } else if (type_entry->id == TypeTableEntryIdUnion) { assert(type_entry->data.unionation.abi_alignment != 0); return type_entry->data.unionation.abi_alignment; diff --git a/src/analyze.hpp b/src/analyze.hpp index b2464af9a0..e6100c692c 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -64,6 +64,9 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name); ScopeDecls *get_container_scope(TypeTableEntry *type_entry); TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name); TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name); +TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag); +TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag); + bool is_container_ref(TypeTableEntry *type_entry); void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); void scan_import(CodeGen *g, ImportTableEntry *import); @@ -109,6 +112,9 @@ ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str); void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *c_str); ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *c_str); +void init_const_bigint(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *bigint); +ConstExprValue *create_const_bigint(TypeTableEntry *type, const BigInt *bigint); + void init_const_unsigned_negative(ConstExprValue *const_val, TypeTableEntry *type, uint64_t x, bool negative); ConstExprValue *create_const_unsigned_negative(TypeTableEntry *type, uint64_t x, bool negative); @@ -121,8 +127,8 @@ ConstExprValue *create_const_usize(CodeGen *g, uint64_t x); void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value); ConstExprValue *create_const_float(TypeTableEntry *type, double value); -void init_const_enum_tag(ConstExprValue *const_val, TypeTableEntry *type, uint64_t tag); -ConstExprValue *create_const_enum_tag(TypeTableEntry *type, uint64_t tag); +void init_const_enum(ConstExprValue *const_val, TypeTableEntry *type, const BigInt *tag); +ConstExprValue *create_const_enum(TypeTableEntry *type, const BigInt *tag); void init_const_bool(CodeGen *g, ConstExprValue *const_val, bool value); ConstExprValue *create_const_bool(CodeGen *g, bool value); @@ -158,7 +164,6 @@ ConstExprValue *create_const_vals(size_t count); TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value); -TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, TypeTableEntry *int_type); void expand_undef_array(CodeGen *g, ConstExprValue *const_val); void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 5ffec92030..4f4dc1decd 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -660,7 +660,20 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { { const char *layout_str = layout_string(node->data.container_decl.layout); const char *container_str = container_string(node->data.container_decl.kind); - fprintf(ar->f, "%s%s {\n", layout_str, container_str); + fprintf(ar->f, "%s%s", layout_str, container_str); + if (node->data.container_decl.auto_enum) { + fprintf(ar->f, "(enum"); + } + if (node->data.container_decl.init_arg_expr != nullptr) { + fprintf(ar->f, "("); + render_node_grouped(ar, node->data.container_decl.init_arg_expr); + fprintf(ar->f, ")"); + } + if (node->data.container_decl.auto_enum) { + fprintf(ar->f, ")"); + } + + fprintf(ar->f, " {\n"); ar->indent += ar->indent_size; for (size_t field_i = 0; field_i < node->data.container_decl.fields.length; field_i += 1) { AstNode *field_node = node->data.container_decl.fields.at(field_i); @@ -671,6 +684,10 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, ": "); render_node_grouped(ar, field_node->data.struct_field.type); } + if (field_node->data.struct_field.value != nullptr) { + fprintf(ar->f, "= "); + render_node_grouped(ar, field_node->data.struct_field.value); + } fprintf(ar->f, ",\n"); } diff --git a/src/bigint.cpp b/src/bigint.cpp index d12c8d0759..f01436d232 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1224,3 +1224,35 @@ Cmp bigint_cmp_zero(const BigInt *op) { } return op->is_negative ? CmpLT : CmpGT; } + +uint32_t bigint_hash(BigInt x) { + if (x.digit_count == 0) { + return 0; + } else { + return bigint_ptr(&x)[0]; + } +} + +bool bigint_eql(BigInt a, BigInt b) { + return bigint_cmp(&a, &b) == CmpEQ; +} + +void bigint_incr(BigInt *x) { + if (x->digit_count == 0) { + bigint_init_unsigned(x, 1); + return; + } + + if (x->digit_count == 1 && x->data.digit != UINT64_MAX) { + x->data.digit += 1; + return; + } + + BigInt copy; + bigint_init_bigint(©, x); + + BigInt one; + bigint_init_unsigned(&one, 1); + + bigint_add(x, ©, &one); +} diff --git a/src/bigint.hpp b/src/bigint.hpp index a1facb5c78..9f044c8722 100644 --- a/src/bigint.hpp +++ b/src/bigint.hpp @@ -88,6 +88,11 @@ size_t bigint_bits_needed(const BigInt *op); // convenience functions Cmp bigint_cmp_zero(const BigInt *op); +void bigint_incr(BigInt *value); + bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result); +uint32_t bigint_hash(BigInt x); +bool bigint_eql(BigInt a, BigInt b); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 24d24a91e5..e10d260e6e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1362,8 +1362,12 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { if (bigint->digit_count == 0) { return LLVMConstNull(type_ref); } - LLVMValueRef unsigned_val = LLVMConstIntOfArbitraryPrecision(type_ref, - bigint->digit_count, bigint_ptr(bigint)); + LLVMValueRef unsigned_val; + if (bigint->digit_count == 1) { + unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false); + } else { + unsigned_val = LLVMConstIntOfArbitraryPrecision(type_ref, bigint->digit_count, bigint_ptr(bigint)); + } if (bigint->is_negative) { return LLVMConstNeg(unsigned_val); } else { @@ -1627,12 +1631,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, type_entry->data.integral.is_signed); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdEnum) { - if (type_entry->data.enumeration.gen_field_count == 0) { - LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); - return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else { - zig_unreachable(); - } + LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); + return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdPureError || type_entry->id == TypeTableEntryIdPointer || type_entry->id == TypeTableEntryIdBool) @@ -1916,9 +1916,7 @@ static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executa // enum_tag to the underlying int type TypeTableEntry *int_type; if (actual_type->id == TypeTableEntryIdEnum) { - TypeTableEntry *tag_type = actual_type->data.enumeration.tag_type; - assert(tag_type->id == TypeTableEntryIdEnumTag); - int_type = tag_type->data.enum_tag.int_type; + int_type = actual_type->data.enumeration.tag_int_type; } else { int_type = actual_type; } @@ -1942,19 +1940,11 @@ static LLVMValueRef ir_render_ptr_to_int(CodeGen *g, IrExecutable *executable, I static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, IrInstructionIntToEnum *instruction) { TypeTableEntry *wanted_type = instruction->base.value.type; assert(wanted_type->id == TypeTableEntryIdEnum); - TypeTableEntry *tag_type = wanted_type->data.enumeration.tag_type; - TypeTableEntry *wanted_int_type; - if (tag_type->id == TypeTableEntryIdEnumTag) { - wanted_int_type = tag_type->data.enum_tag.int_type; - } else if (tag_type->id == TypeTableEntryIdInt) { - wanted_int_type = tag_type; - } else { - zig_unreachable(); - } + TypeTableEntry *tag_int_type = wanted_type->data.enumeration.tag_int_type; LLVMValueRef target_val = ir_llvm_value(g, instruction->target); return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), - instruction->target->value.type, wanted_int_type, target_val); + instruction->target->value.type, tag_int_type, target_val); } static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) { @@ -2374,27 +2364,6 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa return LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, ""); } -static LLVMValueRef ir_render_enum_field_ptr(CodeGen *g, IrExecutable *executable, - IrInstructionEnumFieldPtr *instruction) -{ - TypeTableEntry *enum_ptr_type = instruction->enum_ptr->value.type; - assert(enum_ptr_type->id == TypeTableEntryIdPointer); - TypeTableEntry *enum_type = enum_ptr_type->data.pointer.child_type; - assert(enum_type->id == TypeTableEntryIdEnum); - - TypeEnumField *field = instruction->field; - - if (!type_has_bits(field->type_entry)) - return nullptr; - - LLVMValueRef enum_ptr = ir_llvm_value(g, instruction->enum_ptr); - LLVMTypeRef field_type_ref = LLVMPointerType(field->type_entry->type_ref, 0); - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, enum_ptr, enum_type->data.enumeration.gen_union_index, ""); - LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); - - return bitcasted_union_field_ptr; -} - static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executable, IrInstructionUnionFieldPtr *instruction) { @@ -2420,9 +2389,10 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab if (ir_want_debug_safety(g, &instruction->base)) { LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, ""); LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, ""); - LLVMValueRef expected_tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref, - field->value, false); + + LLVMValueRef expected_tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, + &field->enum_field->value); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk"); LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail"); LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, ""); @@ -2747,21 +2717,21 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI } static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable, - IrInstructionEnumTagName *instruction) + IrInstructionTagName *instruction) { - TypeTableEntry *enum_tag_type = instruction->target->value.type; - assert(enum_tag_type->data.enum_tag.generate_name_table); + TypeTableEntry *enum_type = instruction->target->value.type; + assert(enum_type->id == TypeTableEntryIdEnum); + assert(enum_type->data.enumeration.generate_name_table); + TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type; LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target); if (ir_want_debug_safety(g, &instruction->base)) { - TypeTableEntry *enum_type = enum_tag_type->data.enum_tag.enum_type; size_t field_count = enum_type->data.enumeration.src_field_count; - // if the field_count can't fit in the bits of the enum_tag_type, then it can't possibly + // if the field_count can't fit in the bits of the enum_type, then it can't possibly // be the wrong value BigInt field_bi; bigint_init_unsigned(&field_bi, field_count); - TypeTableEntry *tag_int_type = enum_tag_type->data.enum_tag.int_type; if (bigint_fits_in_bits(&field_bi, tag_int_type->data.integral.bit_count, false)) { LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(enum_tag_value), field_count, false); add_bounds_check(g, enum_tag_value, LLVMIntEQ, nullptr, LLVMIntULT, end_val); @@ -2770,10 +2740,10 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), - gen_widen_or_shorten(g, false, enum_tag_type->data.enum_tag.int_type, + gen_widen_or_shorten(g, false, tag_int_type, g->builtin_types.entry_usize, enum_tag_value), }; - return LLVMBuildInBoundsGEP(g->builder, enum_tag_type->data.enum_tag.name_table, indices, 2, ""); + return LLVMBuildInBoundsGEP(g->builder, enum_type->data.enumeration.name_table, indices, 2, ""); } static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable, @@ -3347,48 +3317,24 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa return instruction->tmp_ptr; } -static LLVMValueRef ir_render_enum_tag(CodeGen *g, IrExecutable *executable, IrInstructionEnumTag *instruction) { - TypeTableEntry *enum_type = instruction->value->value.type; - TypeTableEntry *tag_type = enum_type->data.enumeration.tag_type; +static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutable *executable, IrInstructionUnionTag *instruction) { + TypeTableEntry *union_type = instruction->value->value.type; + assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); + + TypeTableEntry *tag_type = union_type->data.unionation.tag_type; if (!type_has_bits(tag_type)) return nullptr; - LLVMValueRef enum_val = ir_llvm_value(g, instruction->value); - if (enum_type->data.enumeration.gen_field_count == 0) - return enum_val; + LLVMValueRef union_val = ir_llvm_value(g, instruction->value); + if (union_type->data.unionation.gen_field_count == 0) + return union_val; - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, enum_val, enum_type->data.enumeration.gen_tag_index, ""); + LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_val, + union_type->data.unionation.gen_tag_index, ""); TypeTableEntry *ptr_type = get_pointer_to_type(g, tag_type, false); return get_handle_value(g, tag_field_ptr, tag_type, ptr_type); } -static LLVMValueRef ir_render_init_enum(CodeGen *g, IrExecutable *executable, IrInstructionInitEnum *instruction) { - TypeTableEntry *enum_type = instruction->enum_type; - uint32_t value = instruction->field->value; - LLVMTypeRef tag_type_ref = enum_type->data.enumeration.tag_type->type_ref; - LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, value, false); - - if (enum_type->data.enumeration.gen_field_count == 0) - return tag_value; - - LLVMValueRef tmp_struct_ptr = instruction->tmp_ptr; - - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, enum_type->data.enumeration.gen_tag_index, ""); - gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - - TypeTableEntry *union_val_type = instruction->field->type_entry; - if (type_has_bits(union_val_type)) { - LLVMValueRef new_union_val = ir_llvm_value(g, instruction->init_value); - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, enum_type->data.enumeration.gen_union_index, ""); - LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, - LLVMPointerType(union_val_type->type_ref, 0), ""); - - gen_assign_raw(g, bitcasted_union_field_ptr, get_pointer_to_type(g, union_val_type, false), new_union_val); - } - - return tmp_struct_ptr; -} - static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, IrInstructionStructInit *instruction) { for (size_t i = 0; i < instruction->field_count; i += 1) { IrInstructionStructInitField *field = &instruction->fields[i]; @@ -3429,8 +3375,9 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I if (union_type->data.unionation.gen_tag_index != SIZE_MAX) { LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, union_type->data.unionation.gen_tag_index, ""); - LLVMValueRef tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref, - type_union_field->value, false); + + LLVMValueRef tag_value = bigint_to_llvm_const(union_type->data.unionation.tag_type->type_ref, + &type_union_field->enum_field->value); gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, @@ -3537,6 +3484,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdOpaqueType: case IrInstructionIdSetAlignStack: case IrInstructionIdArgType: + case IrInstructionIdTagType: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -3566,8 +3514,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_call(g, executable, (IrInstructionCall *)instruction); case IrInstructionIdStructFieldPtr: return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction); - case IrInstructionIdEnumFieldPtr: - return ir_render_enum_field_ptr(g, executable, (IrInstructionEnumFieldPtr *)instruction); case IrInstructionIdUnionFieldPtr: return ir_render_union_field_ptr(g, executable, (IrInstructionUnionFieldPtr *)instruction); case IrInstructionIdAsm: @@ -3622,10 +3568,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_err_wrap_code(g, executable, (IrInstructionErrWrapCode *)instruction); case IrInstructionIdErrWrapPayload: return ir_render_err_wrap_payload(g, executable, (IrInstructionErrWrapPayload *)instruction); - case IrInstructionIdEnumTag: - return ir_render_enum_tag(g, executable, (IrInstructionEnumTag *)instruction); - case IrInstructionIdInitEnum: - return ir_render_init_enum(g, executable, (IrInstructionInitEnum *)instruction); + case IrInstructionIdUnionTag: + return ir_render_union_tag(g, executable, (IrInstructionUnionTag *)instruction); case IrInstructionIdStructInit: return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); case IrInstructionIdUnionInit: @@ -3650,8 +3594,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction); case IrInstructionIdPanic: return ir_render_panic(g, executable, (IrInstructionPanic *)instruction); - case IrInstructionIdEnumTagName: - return ir_render_enum_tag_name(g, executable, (IrInstructionEnumTagName *)instruction); + case IrInstructionIdTagName: + return ir_render_enum_tag_name(g, executable, (IrInstructionTagName *)instruction); case IrInstructionIdFieldParentPtr: return ir_render_field_parent_ptr(g, executable, (IrInstructionFieldParentPtr *)instruction); case IrInstructionIdAlignCast: @@ -3761,8 +3705,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: - case TypeTableEntryIdEnum: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -3772,6 +3714,12 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con zig_unreachable(); case TypeTableEntryIdBool: return LLVMConstInt(big_int_type_ref, const_val->data.x_bool ? 1 : 0, false); + case TypeTableEntryIdEnum: + { + assert(type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr); + LLVMValueRef int_val = gen_const_val(g, const_val); + return LLVMConstZExt(int_val, big_int_type_ref); + } case TypeTableEntryIdInt: { LLVMValueRef int_val = gen_const_val(g, const_val); @@ -3813,6 +3761,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con } return val; } + } zig_unreachable(); } @@ -3838,7 +3787,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { switch (type_entry->id) { case TypeTableEntryIdInt: - case TypeTableEntryIdEnumTag: return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint); case TypeTableEntryIdPureError: assert(const_val->data.x_pure_err); @@ -4001,37 +3949,52 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { ConstExprValue *payload_value = const_val->data.x_union.payload; assert(payload_value != nullptr); - if (!type_has_bits(payload_value->type)) { - return LLVMGetUndef(union_type_ref); - } - - uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref); - uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes; - LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value); - bool make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) || - payload_value->type != type_entry->data.unionation.most_aligned_union_member; - - LLVMValueRef union_value_ref; - { - if (pad_bytes == 0) { - union_value_ref = correctly_typed_value; + if (type_entry->data.unionation.gen_field_count == 0) { + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { + return nullptr; } else { - LLVMValueRef fields[2]; - fields[0] = correctly_typed_value; - fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); - if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) { - union_value_ref = LLVMConstStruct(fields, 2, false); - } else { - union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2); - } + return bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref, + &const_val->data.x_union.tag); } } - if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { - return union_value_ref; + LLVMValueRef union_value_ref; + bool make_unnamed_struct; + if (!type_has_bits(payload_value->type)) { + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) + return LLVMGetUndef(type_entry->type_ref); + + union_value_ref = LLVMGetUndef(type_entry->data.unionation.most_aligned_union_member->type_ref); + make_unnamed_struct = false; + } else { + uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref); + uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes; + LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value); + make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) || + payload_value->type != type_entry->data.unionation.most_aligned_union_member; + + { + if (pad_bytes == 0) { + union_value_ref = correctly_typed_value; + } else { + LLVMValueRef fields[2]; + fields[0] = correctly_typed_value; + fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); + if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) { + union_value_ref = LLVMConstStruct(fields, 2, false); + } else { + union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2); + } + } + } + + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { + return union_value_ref; + } } - LLVMValueRef tag_value = LLVMConstInt(type_entry->data.unionation.tag_type->type_ref, const_val->data.x_union.tag, false); + LLVMValueRef tag_value = bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref, + &const_val->data.x_union.tag); LLVMValueRef fields[2]; fields[type_entry->data.unionation.gen_union_index] = union_value_ref; @@ -4044,55 +4007,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { } } + case TypeTableEntryIdEnum: - { - LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref; - LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, const_val->data.x_enum.tag, false); - if (type_entry->data.enumeration.gen_field_count == 0) { - return tag_value; - } else { - LLVMTypeRef union_type_ref = type_entry->data.enumeration.union_type_ref; - TypeEnumField *enum_field = &type_entry->data.enumeration.fields[const_val->data.x_enum.tag]; - assert(enum_field->value == const_val->data.x_enum.tag); - LLVMValueRef union_value; - - bool make_unnamed_struct; - - if (type_has_bits(enum_field->type_entry)) { - uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, - enum_field->type_entry->type_ref); - uint64_t pad_bytes = type_entry->data.enumeration.union_size_bytes - field_type_bytes; - - ConstExprValue *payload_value = const_val->data.x_enum.payload; - LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value); - - make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) || - payload_value->type != type_entry->data.enumeration.most_aligned_union_member; - - if (pad_bytes == 0) { - union_value = correctly_typed_value; - } else { - LLVMValueRef fields[] = { - correctly_typed_value, - LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)), - }; - union_value = LLVMConstStruct(fields, 2, false); - } - } else { - make_unnamed_struct = false; - union_value = LLVMGetUndef(union_type_ref); - } - LLVMValueRef fields[2]; - fields[type_entry->data.enumeration.gen_tag_index] = tag_value; - fields[type_entry->data.enumeration.gen_union_index] = union_value; - - if (make_unnamed_struct) { - return LLVMConstStruct(fields, 2, false); - } else { - return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); - } - } - } + return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_enum_tag); case TypeTableEntryIdFn: return fn_llvm_value(g, const_val->data.x_fn.fn_entry); case TypeTableEntryIdPointer: @@ -4303,9 +4220,8 @@ static void generate_enum_name_tables(CodeGen *g) { for (size_t enum_i = 0; enum_i < g->name_table_enums.length; enum_i += 1) { - TypeTableEntry *enum_tag_type = g->name_table_enums.at(enum_i); - assert(enum_tag_type->id == TypeTableEntryIdEnumTag); - TypeTableEntry *enum_type = enum_tag_type->data.enum_tag.enum_type; + TypeTableEntry *enum_type = g->name_table_enums.at(enum_i); + assert(enum_type->id == TypeTableEntryIdEnum); size_t field_count = enum_type->data.enumeration.src_field_count; LLVMValueRef *values = allocate(field_count); @@ -4336,7 +4252,7 @@ static void generate_enum_name_tables(CodeGen *g) { LLVMSetGlobalConstant(name_table, true); LLVMSetUnnamedAddr(name_table, true); LLVMSetAlignment(name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(name_table_init))); - enum_tag_type->data.enum_tag.name_table = name_table; + enum_type->data.enumeration.name_table = name_table; } } @@ -4540,9 +4456,6 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdErrWrapCode) { IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; slot = &err_wrap_code_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdInitEnum) { - IrInstructionInitEnum *init_enum_instruction = (IrInstructionInitEnum *)instruction; - slot = &init_enum_instruction->tmp_ptr; } else { zig_unreachable(); } @@ -5039,7 +4952,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); - create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); + create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2); create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2); create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2); @@ -5049,7 +4962,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdBitCast, "bitCast", 2); create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2); create_builtin_fn(g, BuiltinFnIdPtrToInt, "ptrToInt", 1); - create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1); + create_builtin_fn(g, BuiltinFnIdTagName, "tagName", 1); + create_builtin_fn(g, BuiltinFnIdTagType, "TagType", 1); create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3); create_builtin_fn(g, BuiltinFnIdOffsetOf, "offsetOf", 2); create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2); @@ -5215,7 +5129,19 @@ static void define_builtin_compile_vars(CodeGen *g) { assert(FloatModeOptimized == 0); assert(FloatModeStrict == 1); } - buf_appendf(contents, "pub const is_big_endian = %s;\n", bool_to_str(g->is_big_endian)); + { + buf_appendf(contents, + "pub const Endian = enum {\n" + " Big,\n" + " Little,\n" + "};\n\n"); + assert(FloatModeOptimized == 0); + assert(FloatModeStrict == 1); + } + { + const char *endian_str = g->is_big_endian ? "Endian.Big" : "Endian.Little"; + buf_appendf(contents, "pub const endian = %s;\n", endian_str); + } buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); @@ -5665,7 +5591,6 @@ static void get_c_type(CodeGen *g, TypeTableEntry *type_entry, Buf *out_buf) { case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: zig_panic("TODO implement get_c_type for more types"); case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: diff --git a/src/errmsg.cpp b/src/errmsg.cpp index 01c3ee8429..edbfd858a8 100644 --- a/src/errmsg.cpp +++ b/src/errmsg.cpp @@ -24,20 +24,20 @@ static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type) bool is_tty = os_stderr_tty(); if (color == ErrColorOn || (color == ErrColorAuto && is_tty)) { if (err_type == ErrTypeError) { - os_stderr_set_color(TermColorWhite); + os_stderr_set_color(TermColorBold); fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col); os_stderr_set_color(TermColorRed); fprintf(stderr, "error:"); - os_stderr_set_color(TermColorWhite); + os_stderr_set_color(TermColorBold); fprintf(stderr, " %s", text); os_stderr_set_color(TermColorReset); fprintf(stderr, "\n"); } else if (err_type == ErrTypeNote) { - os_stderr_set_color(TermColorWhite); + os_stderr_set_color(TermColorBold); fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col); os_stderr_set_color(TermColorCyan); fprintf(stderr, "note:"); - os_stderr_set_color(TermColorWhite); + os_stderr_set_color(TermColorBold); fprintf(stderr, " %s", text); os_stderr_set_color(TermColorReset); fprintf(stderr, "\n"); diff --git a/src/error.cpp b/src/error.cpp index a6953d4ab3..8303a80a1d 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -26,6 +26,7 @@ const char *err_str(int err) { case ErrorExactDivRemainder: return "exact division had a remainder"; case ErrorNegativeDenominator: return "negative denominator"; case ErrorShiftedOutOneBits: return "exact shift shifted out one bits"; + case ErrorCCompileErrors: return "C compile errors"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index d7d9c45baf..e3b87fc6b8 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -26,6 +26,7 @@ enum Error { ErrorExactDivRemainder, ErrorNegativeDenominator, ErrorShiftedOutOneBits, + ErrorCCompileErrors, }; const char *err_str(int err); diff --git a/src/ir.cpp b/src/ir.cpp index 7c15b48bee..facd7087f0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -223,10 +223,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStructFieldPtr * return IrInstructionIdStructFieldPtr; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumFieldPtr *) { - return IrInstructionIdEnumFieldPtr; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionFieldPtr *) { return IrInstructionIdUnionFieldPtr; } @@ -319,8 +315,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) { return IrInstructionIdCtz; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTag *) { - return IrInstructionIdEnumTag; +static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionTag *) { + return IrInstructionIdUnionTag; } static constexpr IrInstructionId ir_instruction_id(IrInstructionImport *) { @@ -479,10 +475,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) return IrInstructionIdTestComptime; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionInitEnum *) { - return IrInstructionIdInitEnum; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCast *) { return IrInstructionIdPtrCast; } @@ -547,8 +539,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPanic *) { return IrInstructionIdPanic; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagName *) { - return IrInstructionIdEnumTagName; +static constexpr IrInstructionId ir_instruction_id(IrInstructionTagName *) { + return IrInstructionIdTagName; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionTagType *) { + return IrInstructionIdTagType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldParentPtr *) { @@ -909,27 +905,6 @@ static IrInstruction *ir_build_struct_field_ptr_from(IrBuilder *irb, IrInstructi return new_instruction; } -static IrInstruction *ir_build_enum_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *enum_ptr, TypeEnumField *field) -{ - IrInstructionEnumFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->enum_ptr = enum_ptr; - instruction->field = field; - - ir_ref_instruction(enum_ptr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, - IrInstruction *enum_ptr, TypeEnumField *type_enum_field) -{ - IrInstruction *new_instruction = ir_build_enum_field_ptr(irb, old_instruction->scope, - old_instruction->source_node, enum_ptr, type_enum_field); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *union_ptr, TypeUnionField *field) { @@ -1524,8 +1499,8 @@ static IrInstruction *ir_build_switch_var(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_enum_tag(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { - IrInstructionEnumTag *instruction = ir_build_instruction(irb, scope, source_node); +static IrInstruction *ir_build_union_tag(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { + IrInstructionUnionTag *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); @@ -1533,13 +1508,6 @@ static IrInstruction *ir_build_enum_tag(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } -static IrInstruction *ir_build_enum_tag_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { - IrInstruction *new_instruction = ir_build_enum_tag(irb, old_instruction->scope, - old_instruction->source_node, value); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_import(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) { IrInstructionImport *instruction = ir_build_instruction(irb, scope, source_node); instruction->name = name; @@ -2029,28 +1997,6 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } -static IrInstruction *ir_build_init_enum(IrBuilder *irb, Scope *scope, AstNode *source_node, - TypeTableEntry *enum_type, TypeEnumField *field, IrInstruction *init_value) -{ - IrInstructionInitEnum *instruction = ir_build_instruction(irb, scope, source_node); - instruction->enum_type = enum_type; - instruction->field = field; - instruction->init_value = init_value; - - ir_ref_instruction(init_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstruction *ir_build_init_enum_from(IrBuilder *irb, IrInstruction *old_instruction, - TypeTableEntry *enum_type, TypeEnumField *field, IrInstruction *init_value) -{ - IrInstruction *new_instruction = ir_build_init_enum(irb, old_instruction->scope, old_instruction->source_node, - enum_type, field, init_value); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *ptr) { @@ -2259,10 +2205,21 @@ static IrInstruction *ir_build_panic(IrBuilder *irb, Scope *scope, AstNode *sour return &instruction->base; } -static IrInstruction *ir_build_enum_tag_name(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_tag_name(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { - IrInstructionEnumTagName *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionTagName *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_tag_type(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target) +{ + IrInstructionTagType *instruction = ir_build_instruction(irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); @@ -2466,13 +2423,6 @@ static IrInstruction *ir_instruction_structfieldptr_get_dep(IrInstructionStructF } } -static IrInstruction *ir_instruction_enumfieldptr_get_dep(IrInstructionEnumFieldPtr *instruction, size_t index) { - switch (index) { - case 0: return instruction->enum_ptr; - default: return nullptr; - } -} - static IrInstruction *ir_instruction_unionfieldptr_get_dep(IrInstructionUnionFieldPtr *instruction, size_t index) { switch (index) { case 0: return instruction->union_ptr; @@ -2642,7 +2592,7 @@ static IrInstruction *ir_instruction_maybewrap_get_dep(IrInstructionMaybeWrap *i } } -static IrInstruction *ir_instruction_enumtag_get_dep(IrInstructionEnumTag *instruction, size_t index) { +static IrInstruction *ir_instruction_uniontag_get_dep(IrInstructionUnionTag *instruction, size_t index) { switch (index) { case 0: return instruction->value; default: return nullptr; @@ -2928,13 +2878,6 @@ static IrInstruction *ir_instruction_testcomptime_get_dep(IrInstructionTestCompt } } -static IrInstruction *ir_instruction_initenum_get_dep(IrInstructionInitEnum *instruction, size_t index) { - switch (index) { - case 0: return instruction->init_value; - default: return nullptr; - } -} - static IrInstruction *ir_instruction_ptrcast_get_dep(IrInstructionPtrCast *instruction, size_t index) { @@ -3059,7 +3002,14 @@ static IrInstruction *ir_instruction_panic_get_dep(IrInstructionPanic *instructi } } -static IrInstruction *ir_instruction_enumtagname_get_dep(IrInstructionEnumTagName *instruction, size_t index) { +static IrInstruction *ir_instruction_enumtagname_get_dep(IrInstructionTagName *instruction, size_t index) { + switch (index) { + case 0: return instruction->target; + default: return nullptr; + } +} + +static IrInstruction *ir_instruction_enumtagtype_get_dep(IrInstructionTagType *instruction, size_t index) { switch (index) { case 0: return instruction->target; default: return nullptr; @@ -3162,8 +3112,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_fieldptr_get_dep((IrInstructionFieldPtr *) instruction, index); case IrInstructionIdStructFieldPtr: return ir_instruction_structfieldptr_get_dep((IrInstructionStructFieldPtr *) instruction, index); - case IrInstructionIdEnumFieldPtr: - return ir_instruction_enumfieldptr_get_dep((IrInstructionEnumFieldPtr *) instruction, index); case IrInstructionIdUnionFieldPtr: return ir_instruction_unionfieldptr_get_dep((IrInstructionUnionFieldPtr *) instruction, index); case IrInstructionIdElemPtr: @@ -3212,8 +3160,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_unwrapmaybe_get_dep((IrInstructionUnwrapMaybe *) instruction, index); case IrInstructionIdMaybeWrap: return ir_instruction_maybewrap_get_dep((IrInstructionMaybeWrap *) instruction, index); - case IrInstructionIdEnumTag: - return ir_instruction_enumtag_get_dep((IrInstructionEnumTag *) instruction, index); + case IrInstructionIdUnionTag: + return ir_instruction_uniontag_get_dep((IrInstructionUnionTag *) instruction, index); case IrInstructionIdClz: return ir_instruction_clz_get_dep((IrInstructionClz *) instruction, index); case IrInstructionIdCtz: @@ -3290,8 +3238,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_fnproto_get_dep((IrInstructionFnProto *) instruction, index); case IrInstructionIdTestComptime: return ir_instruction_testcomptime_get_dep((IrInstructionTestComptime *) instruction, index); - case IrInstructionIdInitEnum: - return ir_instruction_initenum_get_dep((IrInstructionInitEnum *) instruction, index); case IrInstructionIdPtrCast: return ir_instruction_ptrcast_get_dep((IrInstructionPtrCast *) instruction, index); case IrInstructionIdBitCast: @@ -3324,8 +3270,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_declref_get_dep((IrInstructionDeclRef *) instruction, index); case IrInstructionIdPanic: return ir_instruction_panic_get_dep((IrInstructionPanic *) instruction, index); - case IrInstructionIdEnumTagName: - return ir_instruction_enumtagname_get_dep((IrInstructionEnumTagName *) instruction, index); + case IrInstructionIdTagName: + return ir_instruction_enumtagname_get_dep((IrInstructionTagName *) instruction, index); + case IrInstructionIdTagType: + return ir_instruction_enumtagtype_get_dep((IrInstructionTagType *) instruction, index); case IrInstructionIdFieldParentPtr: return ir_instruction_fieldparentptr_get_dep((IrInstructionFieldParentPtr *) instruction, index); case IrInstructionIdOffsetOf: @@ -4671,15 +4619,24 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_ptr_to_int(irb, scope, node, arg0_value); } - case BuiltinFnIdEnumTagName: + case BuiltinFnIdTagName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - IrInstruction *actual_tag = ir_build_enum_tag(irb, scope, node, arg0_value); - return ir_build_enum_tag_name(irb, scope, node, actual_tag); + IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value); + return ir_build_tag_name(irb, scope, node, actual_tag); + } + case BuiltinFnIdTagType: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + return ir_build_tag_type(irb, scope, node, arg0_value); } case BuiltinFnIdFieldParentPtr: { @@ -8348,13 +8305,28 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour { assert(wanted_type->id == TypeTableEntryIdInt); + TypeTableEntry *actual_type = target->value.type; + ensure_complete_type(ira->codegen, actual_type); + if (type_is_invalid(actual_type)) + return ira->codegen->invalid_instruction; + + if (wanted_type != actual_type->data.enumeration.tag_int_type) { + ir_add_error(ira, source_instr, + buf_sprintf("enum to integer cast to '%s' instead of its tag type, '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&actual_type->data.enumeration.tag_int_type->name))); + return ira->codegen->invalid_instruction; + } + + assert(actual_type->id == TypeTableEntryIdEnum); + if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - init_const_unsigned_negative(&result->value, wanted_type, val->data.x_enum.tag, false); + init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); return result; } @@ -8364,6 +8336,31 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour return result; } +static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *target, TypeTableEntry *wanted_type) +{ + assert(target->value.type->id == TypeTableEntryIdUnion); + assert(wanted_type->id == TypeTableEntryIdEnum); + assert(wanted_type == target->value.type->data.unionation.tag_type); + + if (instr_is_comptime(target)) { + ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + result->value.special = ConstValSpecialStatic; + result->value.type = wanted_type; + bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_union.tag); + return result; + } + + IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope, + source_instr->source_node, target); + result->value.type = wanted_type; + return result; +} + static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, TypeTableEntry *wanted_type) { @@ -8419,6 +8416,22 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour { assert(wanted_type->id == TypeTableEntryIdEnum); + TypeTableEntry *actual_type = target->value.type; + + ensure_complete_type(ira->codegen, wanted_type); + if (type_is_invalid(wanted_type)) + return ira->codegen->invalid_instruction; + + if (actual_type != wanted_type->data.enumeration.tag_int_type) { + ir_add_error(ira, source_instr, + buf_sprintf("integer to enum cast from '%s' instead of its tag type, '%s'", + buf_ptr(&actual_type->name), + buf_ptr(&wanted_type->data.enumeration.tag_int_type->name))); + return ira->codegen->invalid_instruction; + } + + assert(actual_type->id == TypeTableEntryIdInt); + if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) @@ -8436,7 +8449,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - result->value.data.x_enum.tag = bigint_as_unsigned(&val->data.x_bigint); + bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint); return result; } @@ -8874,21 +8887,26 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit cast from integer to enum type with no payload - if (actual_type->id == TypeTableEntryIdInt && - wanted_type->id == TypeTableEntryIdEnum && - wanted_type->data.enumeration.gen_field_count == 0) - { + if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum) { return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type); } // explicit cast from enum type with no payload to integer - if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdEnum && - actual_type->data.enumeration.gen_field_count == 0) - { + if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdEnum) { return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type); } + // explicit cast from union to the enum type of the union + if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) { + type_ensure_zero_bits_known(ira->codegen, actual_type); + if (type_is_invalid(actual_type)) + return ira->codegen->invalid_instruction; + + if (actual_type->data.unionation.tag_type == wanted_type) { + return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type); + } + } + // explicit cast from undefined to anything if (actual_type->id == TypeTableEntryIdUndefLit) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); @@ -9095,7 +9113,7 @@ static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, Atomic if (!const_val) return false; - *out = (AtomicOrder)const_val->data.x_enum.tag; + *out = (AtomicOrder)bigint_as_unsigned(&const_val->data.x_enum_tag); return true; } @@ -9115,7 +9133,27 @@ static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, Glob if (!const_val) return false; - *out = (GlobalLinkageId)const_val->data.x_enum.tag; + *out = (GlobalLinkageId)bigint_as_unsigned(&const_val->data.x_enum_tag); + return true; +} + +static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstruction *value, FloatMode *out) { + if (type_is_invalid(value->value.type)) + return false; + + ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode"); + assert(float_mode_val->type->id == TypeTableEntryIdMetaType); + TypeTableEntry *float_mode_type = float_mode_val->data.x_type; + + IrInstruction *casted_value = ir_implicit_cast(ira, value, float_mode_type); + if (type_is_invalid(casted_value->value.type)) + return false; + + ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = (FloatMode)bigint_as_unsigned(&const_val->data.x_enum_tag); return true; } @@ -9327,7 +9365,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp break; case TypeTableEntryIdEnum: - if (!is_equality_cmp || resolved_type->data.enumeration.gen_field_count != 0) { + if (!is_equality_cmp) { ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -9346,9 +9384,6 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return ira->codegen->builtin_types.entry_invalid; - case TypeTableEntryIdEnumTag: - zig_panic("TODO implement comparison for enum tag type"); - case TypeTableEntryIdVar: zig_unreachable(); } @@ -10097,7 +10132,6 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { case TypeTableEntryIdVoid: case TypeTableEntryIdPureError: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: return VarClassRequiredAny; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: @@ -10840,7 +10874,6 @@ static TypeTableEntry *ir_analyze_unary_prefix_op_err(IrAnalyze *ira, IrInstruct case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); TypeTableEntry *result_type = get_error_type(ira->codegen, meta_type); @@ -10928,7 +10961,6 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); @@ -11154,8 +11186,8 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP } if (new_incoming_blocks.length == 0) { - ir_build_const_from(ira, &phi_instruction->base); - return ira->codegen->builtin_types.entry_void; + ir_build_unreachable_from(&ira->new_irb, &phi_instruction->base); + return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } if (new_incoming_blocks.length == 1) { @@ -11589,15 +11621,8 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field field_ptr_instruction, container_ptr, container_type); } } else if (bare_type->id == TypeTableEntryIdEnum) { - TypeEnumField *field = find_enum_type_field(bare_type, field_name); - if (field) { - ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); - return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, - get_abi_alignment(ira->codegen, field->type_entry), 0, 0); - } else { - return ir_analyze_container_member_access_inner(ira, bare_type, field_name, - field_ptr_instruction, container_ptr, container_type); - } + return ir_analyze_container_member_access_inner(ira, bare_type, field_name, + field_ptr_instruction, container_ptr, container_type); } else if (bare_type->id == TypeTableEntryIdUnion) { TypeUnionField *field = find_union_type_field(bare_type, field_name); if (field) { @@ -11768,20 +11793,27 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru TypeEnumField *field = find_enum_type_field(child_type, field_name); if (field) { - if (field->type_entry->id == TypeTableEntryIdVoid) { - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, - create_const_enum_tag(child_type, field->value), child_type, - ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); - } else { - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, - create_const_unsigned_negative(child_type->data.enumeration.tag_type, field->value, false), - child_type->data.enumeration.tag_type, + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_enum(child_type, &field->value), child_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } + } else if (child_type->id == TypeTableEntryIdUnion && + (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || + child_type->data.unionation.decl_node->data.container_decl.auto_enum)) + { + ensure_complete_type(ira->codegen, child_type); + if (type_is_invalid(child_type)) + return ira->codegen->builtin_types.entry_invalid; + TypeUnionField *field = find_union_type_field(child_type, field_name); + if (field) { + TypeTableEntry *enum_type = child_type->data.unionation.tag_type; + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_enum(enum_type, &field->enum_field->value), enum_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); - } } } ScopeDecls *container_scope = get_container_scope(child_type); @@ -12090,7 +12122,6 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: { @@ -12367,21 +12398,11 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; } - ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode"); - assert(float_mode_val->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *float_mode_enum_type = float_mode_val->data.x_type; - IrInstruction *float_mode_value = instruction->mode_value->other; - if (type_is_invalid(float_mode_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - IrInstruction *casted_value = ir_implicit_cast(ira, float_mode_value, float_mode_enum_type); - if (type_is_invalid(casted_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *mode_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!mode_val) - return ira->codegen->builtin_types.entry_invalid; - bool want_fast_math = (mode_val->data.x_enum.tag == FloatModeOptimized); + FloatMode float_mode_scalar; + if (!ir_resolve_float_mode(ira, float_mode_value, &float_mode_scalar)) + return ira->codegen->builtin_types.entry_invalid; AstNode *source_node = instruction->base.source_node; if (*fast_math_set_node_ptr) { @@ -12391,7 +12412,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; } *fast_math_set_node_ptr = source_node; - *fast_math_off_ptr = !want_fast_math; + *fast_math_off_ptr = (float_mode_scalar == FloatModeStrict); ir_build_const_from(ira, &instruction->base); return ira->codegen->builtin_types.entry_void; @@ -12448,7 +12469,6 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: { type_ensure_zero_bits_known(ira->codegen, child_type); TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, @@ -12557,7 +12577,6 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdEnumTag: { TypeTableEntry *result_type = get_array_type(ira->codegen, child_type, size); ConstExprValue *out_val = ir_build_const_from(ira, &array_type_instruction->base); @@ -12608,7 +12627,6 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdFn: { uint64_t size_in_bytes = type_size(ira->codegen, type_entry); @@ -12761,17 +12779,22 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC } } -static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) { +static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) { if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - if (value->value.type->id != TypeTableEntryIdEnum) { + if (value->value.type->id == TypeTableEntryIdEnum) { + return value; + } + + if (value->value.type->id != TypeTableEntryIdUnion) { ir_add_error(ira, source_instr, - buf_sprintf("expected enum type, found '%s'", buf_ptr(&value->value.type->name))); + buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name))); return ira->codegen->invalid_instruction; } - TypeTableEntry *tag_type = value->value.type->data.enumeration.tag_type; + TypeTableEntry *tag_type = value->value.type->data.unionation.tag_type; + assert(tag_type->id == TypeTableEntryIdEnum); if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); @@ -12782,11 +12805,11 @@ static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_ source_instr->scope, source_instr->source_node); const_instruction->base.value.type = tag_type; const_instruction->base.value.special = ConstValSpecialStatic; - bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, val->data.x_enum.tag); + bigint_init_bigint(&const_instruction->base.value.data.x_enum_tag, &val->data.x_union.tag); return &const_instruction->base; } - IrInstruction *result = ir_build_enum_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, value); + IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = tag_type; return result; } @@ -12817,7 +12840,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, return ir_unreach_error(ira); if (case_value->value.type->id == TypeTableEntryIdEnum) { - case_value = ir_analyze_enum_tag(ira, &switch_br_instruction->base, case_value); + case_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, case_value); if (type_is_invalid(case_value->value.type)) return ir_unreach_error(ira); } @@ -12864,7 +12887,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, continue; if (new_value->value.type->id == TypeTableEntryIdEnum) { - new_value = ir_analyze_enum_tag(ira, &switch_br_instruction->base, new_value); + new_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, new_value); if (type_is_invalid(new_value->value.type)) continue; } @@ -12946,34 +12969,57 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, ir_build_load_ptr_from(&ira->new_irb, &switch_target_instruction->base, target_value_ptr); return target_type; - case TypeTableEntryIdEnum: + case TypeTableEntryIdUnion: { + AstNode *decl_node = target_type->data.unionation.decl_node; + if (!decl_node->data.container_decl.auto_enum && + decl_node->data.container_decl.init_arg_expr == nullptr) { - TypeTableEntry *tag_type = target_type->data.enumeration.tag_type; - assert(tag_type != nullptr); - if (pointee_val) { - ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); - bigint_init_unsigned(&out_val->data.x_bigint, pointee_val->data.x_enum.tag); - return tag_type; - } - - IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); - enum_value->value.type = target_type; - ir_build_enum_tag_from(&ira->new_irb, &switch_target_instruction->base, enum_value); + ErrorMsg *msg = ir_add_error(ira, target_value_ptr, + buf_sprintf("switch on union which has no attached enum")); + add_error_note(ira->codegen, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); + return ira->codegen->builtin_types.entry_invalid; + } + TypeTableEntry *tag_type = target_type->data.unionation.tag_type; + assert(tag_type != nullptr); + if (pointee_val) { + ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); + bigint_init_bigint(&out_val->data.x_enum_tag, &pointee_val->data.x_union.tag); return tag_type; } + + IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, + switch_target_instruction->base.source_node, target_value_ptr); + union_value->value.type = target_type; + + IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, + switch_target_instruction->base.source_node, union_value); + union_tag_inst->value.type = tag_type; + ir_link_new_instruction(union_tag_inst, &switch_target_instruction->base); + return tag_type; + } + case TypeTableEntryIdEnum: { + if (pointee_val) { + ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); + bigint_init_bigint(&out_val->data.x_enum_tag, &pointee_val->data.x_enum_tag); + return target_type; + } + + IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, + switch_target_instruction->base.source_node, target_value_ptr); + enum_value->value.type = target_type; + ir_link_new_instruction(enum_value, &switch_target_instruction->base); + return target_type; + } case TypeTableEntryIdErrorUnion: - // see https://github.com/andrewrk/zig/issues/83 + // see https://github.com/andrewrk/zig/issues/632 zig_panic("TODO switch on error union"); - case TypeTableEntryIdEnumTag: - zig_panic("TODO switch on enum tag type"); case TypeTableEntryIdUnreachable: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: - case TypeTableEntryIdUnion: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: @@ -12996,19 +13042,13 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer); TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type; - if (target_type->id == TypeTableEntryIdEnum) { + if (target_type->id == TypeTableEntryIdUnion) { ConstExprValue *prong_val = ir_resolve_const(ira, prong_value, UndefBad); if (!prong_val) return ira->codegen->builtin_types.entry_invalid; - TypeEnumField *field; - if (prong_value->value.type->id == TypeTableEntryIdEnumTag) { - field = &target_type->data.enumeration.fields[bigint_as_unsigned(&prong_val->data.x_bigint)]; - } else if (prong_value->value.type->id == TypeTableEntryIdEnum) { - field = &target_type->data.enumeration.fields[prong_val->data.x_enum.tag]; - } else { - zig_unreachable(); - } + assert(prong_value->value.type->id == TypeTableEntryIdEnum); + TypeUnionField *field = find_union_field_by_tag(target_type, &prong_val->data.x_enum_tag); if (instr_is_comptime(target_value_ptr)) { ConstExprValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); @@ -13019,11 +13059,11 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = target_val_ptr->data.x_ptr.mut; - out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_enum.payload; + out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_union.payload; return get_pointer_to_type(ira->codegen, field->type_entry, target_val_ptr->type->data.pointer.is_const); } - ir_build_enum_field_ptr_from(&ira->new_irb, &instruction->base, target_value_ptr, field); + ir_build_union_field_ptr_from(&ira->new_irb, &instruction->base, target_value_ptr, field); return get_pointer_to_type(ira->codegen, field->type_entry, target_value_ptr->value.type->data.pointer.is_const); } else { @@ -13033,10 +13073,10 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr } } -static TypeTableEntry *ir_analyze_instruction_enum_tag(IrAnalyze *ira, IrInstructionEnumTag *enum_tag_instruction) { - IrInstruction *value = enum_tag_instruction->value->other; - IrInstruction *new_instruction = ir_analyze_enum_tag(ira, &enum_tag_instruction->base, value); - ir_link_new_instruction(new_instruction, &enum_tag_instruction->base); +static TypeTableEntry *ir_analyze_instruction_union_tag(IrAnalyze *ira, IrInstructionUnionTag *instruction) { + IrInstruction *value = instruction->value->other; + IrInstruction *new_instruction = ir_analyze_union_tag(ira, &instruction->base, value); + ir_link_new_instruction(new_instruction, &instruction->base); return new_instruction->value.type; } @@ -13192,7 +13232,7 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir ConstExprValue *out_val = ir_build_const_from(ira, instruction); out_val->data.x_union.payload = field_val; - out_val->data.x_union.tag = type_field->value; + out_val->data.x_union.tag = type_field->enum_field->value; ConstParent *parent = get_const_val_parent(ira->codegen, field_val); if (parent != nullptr) { @@ -13439,46 +13479,9 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; } - } else if (container_type_value->value.type->id == TypeTableEntryIdEnumTag) { - if (elem_count != 1) { - ir_add_error(ira, &instruction->base, buf_sprintf("enum initialization requires exactly one element")); - return ira->codegen->builtin_types.entry_invalid; - } - ConstExprValue *tag_value = ir_resolve_const(ira, container_type_value, UndefBad); - if (!tag_value) - return ira->codegen->builtin_types.entry_invalid; - - TypeTableEntry *enum_type = container_type_value->value.type->data.enum_tag.enum_type; - - uint64_t tag_uint = bigint_as_unsigned(&tag_value->data.x_bigint); - TypeEnumField *field = &enum_type->data.enumeration.fields[tag_uint]; - TypeTableEntry *this_field_type = field->type_entry; - - IrInstruction *init_value = instruction->items[0]->other; - if (type_is_invalid(init_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - - IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, this_field_type); - if (casted_init_value == ira->codegen->invalid_instruction) - return ira->codegen->builtin_types.entry_invalid; - - if (instr_is_comptime(casted_init_value)) { - ConstExprValue *init_val = ir_resolve_const(ira, casted_init_value, UndefOk); - if (!init_val) - return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_enum.tag = tag_uint; - out_val->data.x_enum.payload = init_val; - return enum_type; - } - - IrInstruction *new_instruction = ir_build_init_enum_from(&ira->new_irb, &instruction->base, - enum_type, field, casted_init_value); - ir_add_alloca(ira, new_instruction, enum_type); - return enum_type; } else { ir_add_error(ira, container_type_value, - buf_sprintf("expected type, found '%s'", buf_ptr(&container_type_value->value.type->name))); + buf_sprintf("expected type, found '%s' value", buf_ptr(&container_type_value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } } @@ -13521,8 +13524,8 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ eval_min_max_value(ira->codegen, target_type, out_val, is_max); return target_type; } - case TypeTableEntryIdEnumTag: - zig_panic("TODO min/max value for enum tag type"); + case TypeTableEntryIdEnum: + zig_panic("TODO min/max value for enum type"); case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: @@ -13536,7 +13539,6 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: - case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: @@ -13639,29 +13641,27 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc return str_type; } -static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionEnumTagName *instruction) { +static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionTagName *instruction) { IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; - assert(target->value.type->id == TypeTableEntryIdEnumTag); + assert(target->value.type->id == TypeTableEntryIdEnum); if (instr_is_comptime(target)) { - TypeTableEntry *enum_type = target->value.type->data.enum_tag.enum_type; - uint64_t tag_value = bigint_as_unsigned(&target->value.data.x_bigint); - TypeEnumField *field = &enum_type->data.enumeration.fields[tag_value]; + TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint); ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_slice(ira->codegen, out_val, array_val, 0, buf_len(field->name), true); return out_val->type; } - if (!target->value.type->data.enum_tag.generate_name_table) { - target->value.type->data.enum_tag.generate_name_table = true; + if (!target->value.type->data.enumeration.generate_name_table) { + target->value.type->data.enumeration.generate_name_table = true; ira->codegen->name_table_enums.append(target->value.type); } - IrInstruction *result = ir_build_enum_tag_name(&ira->new_irb, instruction->base.scope, + IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target); ir_link_new_instruction(result, &instruction->base); TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); @@ -13806,7 +13806,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, TypeTableEntry *result_type = var_value->data.x_type; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_enum.tag = type_id_index(type_entry->id); + bigint_init_unsigned(&out_val->data.x_enum_tag, type_id_index(type_entry->id)); return result_type; } @@ -13870,7 +13870,10 @@ static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruc int err; if ((err = parse_h_buf(child_import, &errors, &cimport_scope->buf, ira->codegen, node))) { - zig_panic("unable to parse C file: %s\n", err_str(err)); + if (err != ErrorCCompileErrors) { + ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err))); + return ira->codegen->builtin_types.entry_invalid; + } } if (errors.length > 0) { @@ -14632,14 +14635,14 @@ static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInst ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = field->type_entry; return ira->codegen->builtin_types.entry_type; - } else if (container_type->id == TypeTableEntryIdEnum) { - if (member_index >= container_type->data.enumeration.src_field_count) { + } else if (container_type->id == TypeTableEntryIdUnion) { + if (member_index >= container_type->data.unionation.src_field_count) { ir_add_error(ira, index_value, buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", - member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count)); + member_index, buf_ptr(&container_type->name), container_type->data.unionation.src_field_count)); return ira->codegen->builtin_types.entry_invalid; } - TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; + TypeUnionField *field = &container_type->data.unionation.fields[member_index]; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = field->type_entry; @@ -14683,6 +14686,18 @@ static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInst } TypeEnumField *field = &container_type->data.enumeration.fields[member_index]; + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + init_const_str_lit(ira->codegen, out_val, field->name); + return out_val->type; + } else if (container_type->id == TypeTableEntryIdUnion) { + if (member_index >= container_type->data.unionation.src_field_count) { + ir_add_error(ira, index_value, + buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members", + member_index, buf_ptr(&container_type->name), container_type->data.unionation.src_field_count)); + return ira->codegen->builtin_types.entry_invalid; + } + TypeUnionField *field = &container_type->data.unionation.fields[member_index]; + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); init_const_str_lit(ira->codegen, out_val, field->name); return out_val->type; @@ -14753,7 +14768,6 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc case TypeTableEntryIdErrorUnion: case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: - case TypeTableEntryIdEnumTag: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: { @@ -15059,9 +15073,10 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (type_is_invalid(switch_type)) return ira->codegen->builtin_types.entry_invalid; - if (switch_type->id == TypeTableEntryIdEnumTag) { - TypeTableEntry *enum_type = switch_type->data.enum_tag.enum_type; - AstNode **field_prev_uses = allocate(enum_type->data.enumeration.src_field_count); + if (switch_type->id == TypeTableEntryIdEnum) { + HashMap field_prev_uses = {}; + field_prev_uses.init(switch_type->data.enumeration.src_field_count); + for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; @@ -15073,41 +15088,43 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - size_t start_index; - size_t end_index; - if (start_value->value.type->id == TypeTableEntryIdEnumTag) { - start_index = bigint_as_unsigned(&start_value->value.data.x_bigint); - } else if (start_value->value.type->id == TypeTableEntryIdEnum) { - start_index = start_value->value.data.x_enum.tag; - } else { - zig_unreachable(); - } - if (end_value->value.type->id == TypeTableEntryIdEnumTag) { - end_index = bigint_as_unsigned(&end_value->value.data.x_bigint); - } else if (end_value->value.type->id == TypeTableEntryIdEnum) { - end_index = end_value->value.data.x_enum.tag; - } else { - zig_unreachable(); - } + assert(start_value->value.type->id == TypeTableEntryIdEnum); + BigInt start_index; + bigint_init_bigint(&start_index, &start_value->value.data.x_enum_tag); - for (size_t field_index = start_index; field_index <= end_index; field_index += 1) { - AstNode *prev_node = field_prev_uses[field_index]; - if (prev_node != nullptr) { - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_index]; + assert(end_value->value.type->id == TypeTableEntryIdEnum); + BigInt end_index; + bigint_init_bigint(&end_index, &end_value->value.data.x_enum_tag); + + BigInt field_index; + bigint_init_bigint(&field_index, &start_index); + for (;;) { + Cmp cmp = bigint_cmp(&field_index, &end_index); + if (cmp == CmpGT) { + break; + } + auto entry = field_prev_uses.put_unique(field_index, start_value->source_node); + if (entry) { + AstNode *prev_node = entry->value; + TypeEnumField *enum_field = find_enum_field_by_tag(switch_type, &field_index); + assert(enum_field != nullptr); ErrorMsg *msg = ir_add_error(ira, start_value, - buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&enum_type->name), - buf_ptr(type_enum_field->name))); + buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), + buf_ptr(enum_field->name))); add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here")); } - field_prev_uses[field_index] = start_value->source_node; + bigint_incr(&field_index); } } if (!instruction->have_else_prong) { - for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { - if (field_prev_uses[i] == nullptr) { + for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; + + auto entry = field_prev_uses.maybe_get(enum_field->value); + if (!entry) { ir_add_error(ira, &instruction->base, - buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&enum_type->name), - buf_ptr(enum_type->data.enumeration.fields[i].name))); + buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), + buf_ptr(enum_field->name))); } } } @@ -15402,8 +15419,6 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_write_value_bytes pure error type"); case TypeTableEntryIdEnum: zig_panic("TODO buf_write_value_bytes enum type"); - case TypeTableEntryIdEnumTag: - zig_panic("TODO buf_write_value_bytes enum tag type"); case TypeTableEntryIdFn: zig_panic("TODO buf_write_value_bytes fn type"); case TypeTableEntryIdUnion: @@ -15462,8 +15477,6 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_read_value_bytes pure error type"); case TypeTableEntryIdEnum: zig_panic("TODO buf_read_value_bytes enum type"); - case TypeTableEntryIdEnumTag: - zig_panic("TODO buf_read_value_bytes enum tag type"); case TypeTableEntryIdFn: zig_panic("TODO buf_read_value_bytes fn type"); case TypeTableEntryIdUnion: @@ -15828,6 +15841,45 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_type; } +static TypeTableEntry *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) { + IrInstruction *target_inst = instruction->target->other; + TypeTableEntry *enum_type = ir_resolve_type(ira, target_inst); + if (type_is_invalid(enum_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (enum_type->id == TypeTableEntryIdEnum) { + ensure_complete_type(ira->codegen, enum_type); + if (type_is_invalid(enum_type)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = enum_type->data.enumeration.tag_int_type; + return ira->codegen->builtin_types.entry_type; + } else if (enum_type->id == TypeTableEntryIdUnion) { + ensure_complete_type(ira->codegen, enum_type); + if (type_is_invalid(enum_type)) + return ira->codegen->builtin_types.entry_invalid; + + AstNode *decl_node = enum_type->data.unionation.decl_node; + if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { + assert(enum_type->data.unionation.tag_type != nullptr); + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = enum_type->data.unionation.tag_type; + return ira->codegen->builtin_types.entry_type; + } else { + ErrorMsg *msg = ir_add_error(ira, target_inst, buf_sprintf("union '%s' has no tag", + buf_ptr(&enum_type->name))); + add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); + return ira->codegen->builtin_types.entry_invalid; + } + } else { + ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'", + buf_ptr(&enum_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -15838,9 +15890,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi case IrInstructionIdStructInit: case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: - case IrInstructionIdEnumFieldPtr: case IrInstructionIdUnionFieldPtr: - case IrInstructionIdInitEnum: case IrInstructionIdMaybeWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: @@ -15912,8 +15962,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_switch_target(ira, (IrInstructionSwitchTarget *)instruction); case IrInstructionIdSwitchVar: return ir_analyze_instruction_switch_var(ira, (IrInstructionSwitchVar *)instruction); - case IrInstructionIdEnumTag: - return ir_analyze_instruction_enum_tag(ira, (IrInstructionEnumTag *)instruction); + case IrInstructionIdUnionTag: + return ir_analyze_instruction_union_tag(ira, (IrInstructionUnionTag *)instruction); case IrInstructionIdImport: return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction); case IrInstructionIdArrayLen: @@ -16006,8 +16056,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction); case IrInstructionIdPtrToInt: return ir_analyze_instruction_ptr_to_int(ira, (IrInstructionPtrToInt *)instruction); - case IrInstructionIdEnumTagName: - return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionEnumTagName *)instruction); + case IrInstructionIdTagName: + return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionTagName *)instruction); case IrInstructionIdFieldParentPtr: return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction); case IrInstructionIdOffsetOf: @@ -16026,6 +16076,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction); case IrInstructionIdArgType: return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction); + case IrInstructionIdTagType: + return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction); } zig_unreachable(); } @@ -16158,7 +16210,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdPtrTypeChild: case IrInstructionIdArrayLen: case IrInstructionIdStructFieldPtr: - case IrInstructionIdEnumFieldPtr: case IrInstructionIdUnionFieldPtr: case IrInstructionIdArrayType: case IrInstructionIdSliceType: @@ -16169,7 +16220,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCtz: case IrInstructionIdSwitchVar: case IrInstructionIdSwitchTarget: - case IrInstructionIdEnumTag: + case IrInstructionIdUnionTag: case IrInstructionIdRef: case IrInstructionIdMinValue: case IrInstructionIdMaxValue: @@ -16191,7 +16242,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: - case IrInstructionIdInitEnum: case IrInstructionIdPtrCast: case IrInstructionIdBitCast: case IrInstructionIdWidenOrShorten: @@ -16204,13 +16254,14 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdDeclRef: case IrInstructionIdErrName: case IrInstructionIdTypeName: - case IrInstructionIdEnumTagName: + case IrInstructionIdTagName: case IrInstructionIdFieldParentPtr: case IrInstructionIdOffsetOf: case IrInstructionIdTypeId: case IrInstructionIdAlignCast: case IrInstructionIdOpaqueType: case IrInstructionIdArgType: + case IrInstructionIdTagType: return false; case IrInstructionIdAsm: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 55ad3ceb6c..223a012456 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -291,7 +291,7 @@ static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruct } static void ir_print_union_init(IrPrint *irp, IrInstructionUnionInit *instruction) { - Buf *field_name = instruction->field->name; + Buf *field_name = instruction->field->enum_field->name; fprintf(irp->f, "%s {", buf_ptr(&instruction->union_type->name)); fprintf(irp->f, ".%s = ", buf_ptr(field_name)); @@ -361,17 +361,10 @@ static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr fprintf(irp->f, ")"); } -static void ir_print_enum_field_ptr(IrPrint *irp, IrInstructionEnumFieldPtr *instruction) { - fprintf(irp->f, "@EnumFieldPtr(&"); - ir_print_other_instruction(irp, instruction->enum_ptr); - fprintf(irp->f, ".%s", buf_ptr(instruction->field->name)); - fprintf(irp->f, ")"); -} - static void ir_print_union_field_ptr(IrPrint *irp, IrInstructionUnionFieldPtr *instruction) { fprintf(irp->f, "@UnionFieldPtr(&"); ir_print_other_instruction(irp, instruction->union_ptr); - fprintf(irp->f, ".%s", buf_ptr(instruction->field->name)); + fprintf(irp->f, ".%s", buf_ptr(instruction->field->enum_field->name)); fprintf(irp->f, ")"); } @@ -509,8 +502,8 @@ static void ir_print_switch_target(IrPrint *irp, IrInstructionSwitchTarget *inst ir_print_other_instruction(irp, instruction->target_value_ptr); } -static void ir_print_enum_tag(IrPrint *irp, IrInstructionEnumTag *instruction) { - fprintf(irp->f, "enumtag "); +static void ir_print_union_tag(IrPrint *irp, IrInstructionUnionTag *instruction) { + fprintf(irp->f, "uniontag "); ir_print_other_instruction(irp, instruction->value); } @@ -799,12 +792,6 @@ static void ir_print_test_comptime(IrPrint *irp, IrInstructionTestComptime *inst fprintf(irp->f, ")"); } -static void ir_print_init_enum(IrPrint *irp, IrInstructionInitEnum *instruction) { - fprintf(irp->f, "%s.%s {", buf_ptr(&instruction->enum_type->name), buf_ptr(instruction->field->name)); - ir_print_other_instruction(irp, instruction->init_value); - fprintf(irp->f, "}"); -} - static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { fprintf(irp->f, "@ptrCast("); if (instruction->dest_type) { @@ -885,8 +872,8 @@ static void ir_print_type_name(IrPrint *irp, IrInstructionTypeName *instruction) ir_print_other_instruction(irp, instruction->type_value); } -static void ir_print_enum_tag_name(IrPrint *irp, IrInstructionEnumTagName *instruction) { - fprintf(irp->f, "enumtagname "); +static void ir_print_tag_name(IrPrint *irp, IrInstructionTagName *instruction) { + fprintf(irp->f, "tagname "); ir_print_other_instruction(irp, instruction->target); } @@ -994,6 +981,12 @@ static void ir_print_arg_type(IrPrint *irp, IrInstructionArgType *instruction) { fprintf(irp->f, ")"); } +static void ir_print_enum_tag_type(IrPrint *irp, IrInstructionTagType *instruction) { + fprintf(irp->f, "@TagType("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); @@ -1072,9 +1065,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdStructFieldPtr: ir_print_struct_field_ptr(irp, (IrInstructionStructFieldPtr *)instruction); break; - case IrInstructionIdEnumFieldPtr: - ir_print_enum_field_ptr(irp, (IrInstructionEnumFieldPtr *)instruction); - break; case IrInstructionIdUnionFieldPtr: ir_print_union_field_ptr(irp, (IrInstructionUnionFieldPtr *)instruction); break; @@ -1117,8 +1107,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSwitchTarget: ir_print_switch_target(irp, (IrInstructionSwitchTarget *)instruction); break; - case IrInstructionIdEnumTag: - ir_print_enum_tag(irp, (IrInstructionEnumTag *)instruction); + case IrInstructionIdUnionTag: + ir_print_union_tag(irp, (IrInstructionUnionTag *)instruction); break; case IrInstructionIdImport: ir_print_import(irp, (IrInstructionImport *)instruction); @@ -1231,9 +1221,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTestComptime: ir_print_test_comptime(irp, (IrInstructionTestComptime *)instruction); break; - case IrInstructionIdInitEnum: - ir_print_init_enum(irp, (IrInstructionInitEnum *)instruction); - break; case IrInstructionIdPtrCast: ir_print_ptr_cast(irp, (IrInstructionPtrCast *)instruction); break; @@ -1267,8 +1254,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTypeName: ir_print_type_name(irp, (IrInstructionTypeName *)instruction); break; - case IrInstructionIdEnumTagName: - ir_print_enum_tag_name(irp, (IrInstructionEnumTagName *)instruction); + case IrInstructionIdTagName: + ir_print_tag_name(irp, (IrInstructionTagName *)instruction); break; case IrInstructionIdCanImplicitCast: ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction); @@ -1312,6 +1299,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdArgType: ir_print_arg_type(irp, (IrInstructionArgType *)instruction); break; + case IrInstructionIdTagType: + ir_print_enum_tag_type(irp, (IrInstructionTagType *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/src/os.cpp b/src/os.cpp index 6c242bc350..a59fa12657 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -931,6 +931,7 @@ int os_self_exe_path(Buf *out_path) { #define VT_GREEN "\x1b[32;1m" #define VT_CYAN "\x1b[36;1m" #define VT_WHITE "\x1b[37;1m" +#define VT_BOLD "\x1b[0;1m" #define VT_RESET "\x1b[0m" static void set_color_posix(TermColor color) { @@ -947,6 +948,9 @@ static void set_color_posix(TermColor color) { case TermColorWhite: fprintf(stderr, VT_WHITE); break; + case TermColorBold: + fprintf(stderr, VT_BOLD); + break; case TermColorReset: fprintf(stderr, VT_RESET); break; @@ -989,6 +993,7 @@ void os_stderr_set_color(TermColor color) { SetConsoleTextAttribute(stderr_handle, FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); break; case TermColorWhite: + case TermColorBold: SetConsoleTextAttribute(stderr_handle, FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); break; diff --git a/src/os.hpp b/src/os.hpp index 611779f7c8..d4d1676df6 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -21,6 +21,7 @@ enum TermColor { TermColorGreen, TermColorCyan, TermColorWhite, + TermColorBold, TermColorReset, }; diff --git a/src/parser.cpp b/src/parser.cpp index ba1fd99e57..26ca7da31a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2377,9 +2377,11 @@ static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod vi } /* -ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}" +ContainerDecl = option("extern" | "packed") + ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression))) + "{" many(ContainerMember) "}" ContainerMember = (ContainerField | FnDef | GlobalVarDecl) -ContainerField = Symbol option(":" Expression) "," +ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression "," */ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory) { Token *first_token = &pc->tokens->at(*token_index); @@ -2415,6 +2417,28 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, node->data.container_decl.layout = layout; node->data.container_decl.kind = kind; + if (kind == ContainerKindUnion) { + Token *lparen_token = &pc->tokens->at(*token_index); + if (lparen_token->id == TokenIdLParen) { + Token *enum_token = &pc->tokens->at(*token_index + 1); + if (enum_token->id == TokenIdKeywordEnum) { + Token *paren_token = &pc->tokens->at(*token_index + 2); + if (paren_token->id == TokenIdLParen) { + node->data.container_decl.auto_enum = true; + *token_index += 2; + node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, true); + ast_eat_token(pc, token_index, TokenIdRParen); + } else if (paren_token->id == TokenIdRParen) { + node->data.container_decl.auto_enum = true; + *token_index += 3; + } + } + } + } + if (!node->data.container_decl.auto_enum) { + node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false); + } + ast_eat_token(pc, token_index, TokenIdLBrace); for (;;) { @@ -2452,31 +2476,33 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token); *token_index += 1; + node->data.container_decl.fields.append(field_node); field_node->data.struct_field.visib_mod = visib_mod; field_node->data.struct_field.name = token_buf(token); - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdComma || token->id == TokenIdRBrace) { - field_node->data.struct_field.type = ast_create_void_type_node(pc, token); + Token *colon_token = &pc->tokens->at(*token_index); + if (colon_token->id == TokenIdColon) { *token_index += 1; - node->data.container_decl.fields.append(field_node); - - if (token->id == TokenIdRBrace) { - break; - } - } else { - ast_eat_token(pc, token_index, TokenIdColon); - field_node->data.struct_field.type = ast_parse_expression(pc, token_index, true); - node->data.container_decl.fields.append(field_node); - - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRBrace) { - *token_index += 1; - break; - } else { - ast_eat_token(pc, token_index, TokenIdComma); - } + field_node->data.struct_field.type = ast_parse_prefix_op_expr(pc, token_index, true); } + Token *eq_token = &pc->tokens->at(*token_index); + if (eq_token->id == TokenIdEq) { + *token_index += 1; + field_node->data.struct_field.value = ast_parse_prefix_op_expr(pc, token_index, true); + } + + Token *next_token = &pc->tokens->at(*token_index); + if (next_token->id == TokenIdComma) { + *token_index += 1; + continue; + } + + if (next_token->id == TokenIdRBrace) { + *token_index += 1; + break; + } + + ast_invalid_token_error(pc, next_token); } else { ast_invalid_token_error(pc, token); } @@ -2804,9 +2830,11 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeContainerDecl: visit_node_list(&node->data.container_decl.fields, visit, context); visit_node_list(&node->data.container_decl.decls, visit, context); + visit_field(&node->data.container_decl.init_arg_expr, visit, context); break; case NodeTypeStructField: visit_field(&node->data.struct_field.type, visit, context); + visit_field(&node->data.struct_field.value, visit, context); break; case NodeTypeContainerInitExpr: visit_field(&node->data.container_init_expr.type, visit, context); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index dbf9613308..0139774f02 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -651,6 +651,14 @@ static bool c_is_unsigned_integer(Context *c, QualType qt) { } } +static bool c_is_builtin_type(Context *c, QualType qt, BuiltinType::Kind kind) { + const Type *c_type = qual_type_canon(qt); + if (c_type->getTypeClass() != Type::Builtin) + return false; + const BuiltinType *builtin_ty = static_cast(c_type); + return builtin_ty->getKind() == kind; +} + static bool c_is_float(Context *c, QualType qt) { const Type *c_type = qt.getTypePtr(); if (c_type->getTypeClass() != Type::Builtin) @@ -978,11 +986,23 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou const AttributedType *attributed_ty = static_cast(ty); return trans_qual_type(c, attributed_ty->getEquivalentType(), source_loc); } + case Type::IncompleteArray: + { + const IncompleteArrayType *incomplete_array_ty = static_cast(ty); + QualType child_qt = incomplete_array_ty->getElementType(); + AstNode *child_type_node = trans_qual_type(c, child_qt, source_loc); + if (child_type_node == nullptr) { + emit_warning(c, source_loc, "unresolved array element type"); + return nullptr; + } + AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(), + child_qt.isVolatileQualified(), child_type_node); + return pointer_node; + } case Type::BlockPointer: case Type::LValueReference: case Type::RValueReference: case Type::MemberPointer: - case Type::IncompleteArray: case Type::VariableArray: case Type::DependentSizedArray: case Type::DependentSizedExtVector: @@ -3417,7 +3437,14 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); enum_node->data.container_decl.kind = ContainerKindEnum; enum_node->data.container_decl.layout = ContainerLayoutExtern; - enum_node->data.container_decl.init_arg_expr = tag_int_type; + // TODO only emit this tag type if the enum tag type is not the default. + // I don't know what the default is, need to figure out how clang is deciding. + // it appears to at least be different across gcc/msvc + if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) && + !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) + { + enum_node->data.container_decl.init_arg_expr = tag_int_type; + } enum_node->data.container_decl.fields.resize(field_count); uint32_t i = 0; @@ -4304,7 +4331,7 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch } } - return 0; + return ErrorCCompileErrors; } c->ctx = &ast_unit->getASTContext(); diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 03a1475062..5623002197 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -793,7 +793,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ zig_unreachable(); case ZigLLVM_COFF: - return lld::coff::link(array_ref_args, false); + return lld::coff::link(array_ref_args, false, diag); case ZigLLVM_ELF: return lld::elf::link(array_ref_args, false, diag); diff --git a/std/buffer.zig b/std/buffer.zig index a1aa8faf9d..96abaeb762 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -4,6 +4,8 @@ const Allocator = mem.Allocator; const assert = debug.assert; const ArrayList = @import("array_list.zig").ArrayList; +const fmt = @import("fmt/index.zig"); + /// A buffer that allocates memory and maintains a null byte at the end. pub const Buffer = struct { list: ArrayList(u8), @@ -96,6 +98,10 @@ pub const Buffer = struct { mem.copy(u8, self.list.toSlice()[old_len..], m); } + pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) -> %void { + return fmt.format(self, append, format, args); + } + pub fn appendByte(self: &Buffer, byte: u8) -> %void { return self.appendByteNTimes(byte, 1); } diff --git a/std/build.zig b/std/build.zig index 009295c6ad..9bdc4b3076 100644 --- a/std/build.zig +++ b/std/build.zig @@ -69,8 +69,8 @@ pub const Builder = struct { used: bool, }; - const UserValue = enum { - Flag, + const UserValue = union(enum) { + Flag: void, Scalar: []const u8, List: ArrayList([]const u8), }; @@ -450,7 +450,7 @@ pub const Builder = struct { pub fn addUserInputOption(self: &Builder, name: []const u8, value: []const u8) -> bool { if (%%self.user_input_options.put(name, UserInputOption { .name = name, - .value = UserValue.Scalar { value }, + .value = UserValue { .Scalar = value }, .used = false, })) |*prev_value| { // option already exists @@ -462,7 +462,7 @@ pub const Builder = struct { %%list.append(value); _ = %%self.user_input_options.put(name, UserInputOption { .name = name, - .value = UserValue.List { list }, + .value = UserValue { .List = list }, .used = false, }); }, @@ -471,7 +471,7 @@ pub const Builder = struct { %%list.append(value); _ = %%self.user_input_options.put(name, UserInputOption { .name = name, - .value = UserValue.List { *list }, + .value = UserValue { .List = *list }, .used = false, }); }, @@ -487,7 +487,7 @@ pub const Builder = struct { pub fn addUserInputFlag(self: &Builder, name: []const u8) -> bool { if (%%self.user_input_options.put(name, UserInputOption { .name = name, - .value = UserValue.Flag, + .value = UserValue {.Flag = {} }, .used = false, })) |*prev_value| { switch (prev_value.value) { @@ -685,8 +685,8 @@ const CrossTarget = struct { environ: builtin.Environ, }; -const Target = enum { - Native, +const Target = union(enum) { + Native: void, Cross: CrossTarget, pub fn oFileExt(self: &const Target) -> []const u8 { @@ -844,7 +844,7 @@ pub const LibExeObjStep = struct { .kind = kind, .root_src = root_src, .name = name, - .target = Target.Native, + .target = Target { .Native = {} }, .linker_script = null, .link_libs = BufSet.init(builder.allocator), .frameworks = BufSet.init(builder.allocator), @@ -879,7 +879,7 @@ pub const LibExeObjStep = struct { .kind = kind, .version = *version, .static = static, - .target = Target.Native, + .target = Target { .Native = {} }, .cflags = ArrayList([]const u8).init(builder.allocator), .source_files = ArrayList([]const u8).init(builder.allocator), .object_files = ArrayList([]const u8).init(builder.allocator), @@ -948,8 +948,8 @@ pub const LibExeObjStep = struct { pub fn setTarget(self: &LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) { - self.target = Target.Cross { - CrossTarget { + self.target = Target { + .Cross = CrossTarget { .arch = target_arch, .os = target_os, .environ = target_environ, @@ -1186,13 +1186,13 @@ pub const LibExeObjStep = struct { Target.Native => {}, Target.Cross => |cross_target| { %%zig_args.append("--target-arch"); - %%zig_args.append(@enumTagName(cross_target.arch)); + %%zig_args.append(@tagName(cross_target.arch)); %%zig_args.append("--target-os"); - %%zig_args.append(@enumTagName(cross_target.os)); + %%zig_args.append(@tagName(cross_target.os)); %%zig_args.append("--target-environ"); - %%zig_args.append(@enumTagName(cross_target.environ)); + %%zig_args.append(@tagName(cross_target.environ)); }, } @@ -1553,7 +1553,7 @@ pub const TestStep = struct { .name_prefix = "", .filter = null, .link_libs = BufSet.init(builder.allocator), - .target = Target.Native, + .target = Target { .Native = {} }, .exec_cmd_args = null, } } @@ -1581,8 +1581,8 @@ pub const TestStep = struct { pub fn setTarget(self: &TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) { - self.target = Target.Cross { - CrossTarget { + self.target = Target { + .Cross = CrossTarget { .arch = target_arch, .os = target_os, .environ = target_environ, @@ -1620,13 +1620,13 @@ pub const TestStep = struct { Target.Native => {}, Target.Cross => |cross_target| { %%zig_args.append("--target-arch"); - %%zig_args.append(@enumTagName(cross_target.arch)); + %%zig_args.append(@tagName(cross_target.arch)); %%zig_args.append("--target-os"); - %%zig_args.append(@enumTagName(cross_target.os)); + %%zig_args.append(@tagName(cross_target.os)); %%zig_args.append("--target-environ"); - %%zig_args.append(@enumTagName(cross_target.environ)); + %%zig_args.append(@tagName(cross_target.environ)); }, } diff --git a/std/debug.zig b/std/debug.zig index 50322024c3..a2bea9eddd 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -280,7 +280,7 @@ const AbbrevAttr = struct { form_id: u64, }; -const FormValue = enum { +const FormValue = union(enum) { Address: u64, Block: []u8, Const: Constant, @@ -303,7 +303,7 @@ const Constant = struct { return error.InvalidDebugInfo; if (self.signed) return error.InvalidDebugInfo; - return mem.readInt(self.payload, u64, false); + return mem.readInt(self.payload, u64, builtin.Endian.Little); } }; @@ -475,16 +475,16 @@ fn readAllocBytes(allocator: &mem.Allocator, in_stream: &io.InStream, size: usiz fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { const buf = %return readAllocBytes(allocator, in_stream, size); - return FormValue.Block { buf }; + return FormValue { .Block = buf }; } fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { - const block_len = %return in_stream.readVarInt(false, usize, size); + const block_len = %return in_stream.readVarInt(builtin.Endian.Little, usize, size); return parseFormValueBlockLen(allocator, in_stream, block_len); } fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { - FormValue.Const { Constant { + FormValue { .Const = Constant { .signed = signed, .payload = %return readAllocBytes(allocator, in_stream, size), }} @@ -510,7 +510,7 @@ fn parseFormValueTargetAddrSize(in_stream: &io.InStream) -> %u64 { fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue { const buf = %return readAllocBytes(allocator, in_stream, size); - return FormValue.Ref { buf }; + return FormValue { .Ref = buf }; } fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptime T: type) -> %FormValue { @@ -520,7 +520,7 @@ fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptim fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { return switch (form_id) { - DW.FORM_addr => FormValue.Address { %return parseFormValueTargetAddrSize(in_stream) }, + DW.FORM_addr => FormValue { .Address = %return parseFormValueTargetAddrSize(in_stream) }, DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), @@ -540,13 +540,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u DW.FORM_exprloc => { const size = %return readULeb128(in_stream); const buf = %return readAllocBytes(allocator, in_stream, size); - return FormValue.ExprLoc { buf }; - }, - DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, - DW.FORM_flag_present => FormValue.Flag { true }, - DW.FORM_sec_offset => FormValue.SecOffset { - %return parseFormValueDwarfOffsetSize(in_stream, is_64) + return FormValue { .ExprLoc = buf }; }, + DW.FORM_flag => FormValue { .Flag = (%return in_stream.readByte()) != 0 }, + DW.FORM_flag_present => FormValue { .Flag = true }, + DW.FORM_sec_offset => FormValue { .SecOffset = %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8), DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16), @@ -557,11 +555,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u parseFormValueRefLen(allocator, in_stream, ref_len) }, - DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, - DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, + DW.FORM_ref_addr => FormValue { .RefAddr = %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_ref_sig8 => FormValue { .RefSig8 = %return in_stream.readIntLe(u64) }, - DW.FORM_string => FormValue.String { %return readStringRaw(allocator, in_stream) }, - DW.FORM_strp => FormValue.StrPtr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_string => FormValue { .String = %return readStringRaw(allocator, in_stream) }, + DW.FORM_strp => FormValue { .StrPtr = %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_indirect => { const child_form_id = %return readULeb128(in_stream); parseFormValue(allocator, in_stream, child_form_id, is_64) @@ -671,10 +669,10 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe continue; } - const version = %return in_stream.readInt(st.elf.is_big_endian, u16); + const version = %return in_stream.readInt(st.elf.endian, u16); if (version != 2) return error.InvalidDebugInfo; - const prologue_length = %return in_stream.readInt(st.elf.is_big_endian, u32); + const prologue_length = %return in_stream.readInt(st.elf.endian, u32); const prog_start_offset = (%return in_file.getPos()) + prologue_length; const minimum_instruction_length = %return in_stream.readByte(); @@ -741,7 +739,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe return error.MissingDebugInfo; }, DW.LNE_set_address => { - const addr = %return in_stream.readInt(st.elf.is_big_endian, usize); + const addr = %return in_stream.readInt(st.elf.endian, usize); prog.address = addr; }, DW.LNE_define_file => { @@ -803,7 +801,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe prog.address += inc_addr; }, DW.LNS_fixed_advance_pc => { - const arg = %return in_stream.readInt(st.elf.is_big_endian, u16); + const arg = %return in_stream.readInt(st.elf.endian, u16); prog.address += arg; }, DW.LNS_set_prologue_end => { @@ -841,13 +839,13 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { return; const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); - const version = %return in_stream.readInt(st.elf.is_big_endian, u16); + const version = %return in_stream.readInt(st.elf.endian, u16); if (version < 2 or version > 5) return error.InvalidDebugInfo; const debug_abbrev_offset = if (is_64) { - %return in_stream.readInt(st.elf.is_big_endian, u64) + %return in_stream.readInt(st.elf.endian, u64) } else { - %return in_stream.readInt(st.elf.is_big_endian, u32) + %return in_stream.readInt(st.elf.endian, u32) }; const address_size = %return in_stream.readByte(); diff --git a/std/elf.zig b/std/elf.zig index f3f7de512f..f7be236d15 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("index.zig"); const io = std.io; const math = std.math; @@ -67,7 +68,7 @@ pub const Elf = struct { in_file: &io.File, auto_close_stream: bool, is_64: bool, - is_big_endian: bool, + endian: builtin.Endian, file_type: FileType, arch: Arch, entry_addr: u64, @@ -105,9 +106,9 @@ pub const Elf = struct { else => return error.InvalidFormat, }; - elf.is_big_endian = switch (%return in.readByte()) { - 1 => false, - 2 => true, + elf.endian = switch (%return in.readByte()) { + 1 => builtin.Endian.Little, + 2 => builtin.Endian.Big, else => return error.InvalidFormat, }; @@ -117,7 +118,7 @@ pub const Elf = struct { // skip over padding %return elf.in_file.seekForward(9); - elf.file_type = switch (%return in.readInt(elf.is_big_endian, u16)) { + elf.file_type = switch (%return in.readInt(elf.endian, u16)) { 1 => FileType.Relocatable, 2 => FileType.Executable, 3 => FileType.Shared, @@ -125,7 +126,7 @@ pub const Elf = struct { else => return error.InvalidFormat, }; - elf.arch = switch (%return in.readInt(elf.is_big_endian, u16)) { + elf.arch = switch (%return in.readInt(elf.endian, u16)) { 0x02 => Arch.Sparc, 0x03 => Arch.x86, 0x08 => Arch.Mips, @@ -138,34 +139,34 @@ pub const Elf = struct { else => return error.InvalidFormat, }; - const elf_version = %return in.readInt(elf.is_big_endian, u32); + const elf_version = %return in.readInt(elf.endian, u32); if (elf_version != 1) return error.InvalidFormat; if (elf.is_64) { - elf.entry_addr = %return in.readInt(elf.is_big_endian, u64); - elf.program_header_offset = %return in.readInt(elf.is_big_endian, u64); - elf.section_header_offset = %return in.readInt(elf.is_big_endian, u64); + elf.entry_addr = %return in.readInt(elf.endian, u64); + elf.program_header_offset = %return in.readInt(elf.endian, u64); + elf.section_header_offset = %return in.readInt(elf.endian, u64); } else { - elf.entry_addr = u64(%return in.readInt(elf.is_big_endian, u32)); - elf.program_header_offset = u64(%return in.readInt(elf.is_big_endian, u32)); - elf.section_header_offset = u64(%return in.readInt(elf.is_big_endian, u32)); + elf.entry_addr = u64(%return in.readInt(elf.endian, u32)); + elf.program_header_offset = u64(%return in.readInt(elf.endian, u32)); + elf.section_header_offset = u64(%return in.readInt(elf.endian, u32)); } // skip over flags %return elf.in_file.seekForward(4); - const header_size = %return in.readInt(elf.is_big_endian, u16); + const header_size = %return in.readInt(elf.endian, u16); if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) { return error.InvalidFormat; } - const ph_entry_size = %return in.readInt(elf.is_big_endian, u16); - const ph_entry_count = %return in.readInt(elf.is_big_endian, u16); - const sh_entry_size = %return in.readInt(elf.is_big_endian, u16); - const sh_entry_count = %return in.readInt(elf.is_big_endian, u16); - elf.string_section_index = u64(%return in.readInt(elf.is_big_endian, u16)); + const ph_entry_size = %return in.readInt(elf.endian, u16); + const ph_entry_count = %return in.readInt(elf.endian, u16); + const sh_entry_size = %return in.readInt(elf.endian, u16); + const sh_entry_count = %return in.readInt(elf.endian, u16); + elf.string_section_index = u64(%return in.readInt(elf.endian, u16)); if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat; @@ -188,32 +189,32 @@ pub const Elf = struct { if (sh_entry_size != 64) return error.InvalidFormat; for (elf.section_headers) |*section| { - section.name = %return in.readInt(elf.is_big_endian, u32); - section.sh_type = %return in.readInt(elf.is_big_endian, u32); - section.flags = %return in.readInt(elf.is_big_endian, u64); - section.addr = %return in.readInt(elf.is_big_endian, u64); - section.offset = %return in.readInt(elf.is_big_endian, u64); - section.size = %return in.readInt(elf.is_big_endian, u64); - section.link = %return in.readInt(elf.is_big_endian, u32); - section.info = %return in.readInt(elf.is_big_endian, u32); - section.addr_align = %return in.readInt(elf.is_big_endian, u64); - section.ent_size = %return in.readInt(elf.is_big_endian, u64); + section.name = %return in.readInt(elf.endian, u32); + section.sh_type = %return in.readInt(elf.endian, u32); + section.flags = %return in.readInt(elf.endian, u64); + section.addr = %return in.readInt(elf.endian, u64); + section.offset = %return in.readInt(elf.endian, u64); + section.size = %return in.readInt(elf.endian, u64); + section.link = %return in.readInt(elf.endian, u32); + section.info = %return in.readInt(elf.endian, u32); + section.addr_align = %return in.readInt(elf.endian, u64); + section.ent_size = %return in.readInt(elf.endian, u64); } } else { if (sh_entry_size != 40) return error.InvalidFormat; for (elf.section_headers) |*section| { // TODO (multiple occurences) allow implicit cast from %u32 -> %u64 ? - section.name = %return in.readInt(elf.is_big_endian, u32); - section.sh_type = %return in.readInt(elf.is_big_endian, u32); - section.flags = u64(%return in.readInt(elf.is_big_endian, u32)); - section.addr = u64(%return in.readInt(elf.is_big_endian, u32)); - section.offset = u64(%return in.readInt(elf.is_big_endian, u32)); - section.size = u64(%return in.readInt(elf.is_big_endian, u32)); - section.link = %return in.readInt(elf.is_big_endian, u32); - section.info = %return in.readInt(elf.is_big_endian, u32); - section.addr_align = u64(%return in.readInt(elf.is_big_endian, u32)); - section.ent_size = u64(%return in.readInt(elf.is_big_endian, u32)); + section.name = %return in.readInt(elf.endian, u32); + section.sh_type = %return in.readInt(elf.endian, u32); + section.flags = u64(%return in.readInt(elf.endian, u32)); + section.addr = u64(%return in.readInt(elf.endian, u32)); + section.offset = u64(%return in.readInt(elf.endian, u32)); + section.size = u64(%return in.readInt(elf.endian, u32)); + section.link = %return in.readInt(elf.endian, u32); + section.info = %return in.readInt(elf.endian, u32); + section.addr_align = u64(%return in.readInt(elf.endian, u32)); + section.ent_size = u64(%return in.readInt(elf.endian, u32)); } } diff --git a/std/endian.zig b/std/endian.zig index 8ae8ae22f8..2dc6b8d34e 100644 --- a/std/endian.zig +++ b/std/endian.zig @@ -9,8 +9,8 @@ pub fn swapIfBe(comptime T: type, x: T) -> T { swapIf(true, T, x) } -pub fn swapIf(is_be: bool, comptime T: type, x: T) -> T { - if (builtin.is_big_endian == is_be) swap(T, x) else x +pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) -> T { + if (builtin.endian == endian) swap(T, x) else x } pub fn swap(comptime T: type, x: T) -> T { diff --git a/std/io.zig b/std/io.zig index d570927488..c86ebed326 100644 --- a/std/io.zig +++ b/std/io.zig @@ -259,7 +259,7 @@ pub const File = struct { if (err > 0) { return switch (err) { system.EBADF => error.BadFd, - system.ENOMEM => error.OutOfMemory, + system.ENOMEM => error.SystemResources, else => os.unexpectedErrorPosix(err), } } @@ -441,26 +441,26 @@ pub const InStream = struct { } pub fn readIntLe(self: &InStream, comptime T: type) -> %T { - return self.readInt(false, T); + return self.readInt(builtin.Endian.Little, T); } pub fn readIntBe(self: &InStream, comptime T: type) -> %T { - return self.readInt(true, T); + return self.readInt(builtin.Endian.Big, T); } - pub fn readInt(self: &InStream, is_be: bool, comptime T: type) -> %T { + pub fn readInt(self: &InStream, endian: builtin.Endian, comptime T: type) -> %T { var bytes: [@sizeOf(T)]u8 = undefined; %return self.readNoEof(bytes[0..]); - return mem.readInt(bytes, T, is_be); + return mem.readInt(bytes, T, endian); } - pub fn readVarInt(self: &InStream, is_be: bool, comptime T: type, size: usize) -> %T { + pub fn readVarInt(self: &InStream, endian: builtin.Endian, comptime T: type, size: usize) -> %T { assert(size <= @sizeOf(T)); assert(size <= 8); var input_buf: [8]u8 = undefined; const input_slice = input_buf[0..size]; %return self.readNoEof(input_slice); - return mem.readInt(input_slice, T, is_be); + return mem.readInt(input_slice, T, endian); } diff --git a/std/mem.zig b/std/mem.zig index 3cfdb25b35..815e122812 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -1,6 +1,7 @@ const debug = @import("debug.zig"); const assert = debug.assert; const math = @import("math/index.zig"); +const builtin = @import("builtin"); pub const Cmp = math.Cmp; @@ -180,43 +181,78 @@ test "mem.indexOf" { /// Reads an integer from memory with size equal to bytes.len. /// T specifies the return type, which must be large enough to store /// the result. -pub fn readInt(bytes: []const u8, comptime T: type, big_endian: bool) -> T { +/// See also ::readIntBE or ::readIntLE. +pub fn readInt(bytes: []const u8, comptime T: type, endian: builtin.Endian) -> T { if (T.bit_count == 8) { return bytes[0]; } var result: T = 0; - if (big_endian) { - for (bytes) |b| { - result = (result << 8) | b; - } - } else { - const ShiftType = math.Log2Int(T); - for (bytes) |b, index| { - result = result | (T(b) << ShiftType(index * 8)); - } + switch (endian) { + builtin.Endian.Big => { + for (bytes) |b| { + result = (result << 8) | b; + } + }, + builtin.Endian.Little => { + const ShiftType = math.Log2Int(T); + for (bytes) |b, index| { + result = result | (T(b) << ShiftType(index * 8)); + } + }, } return result; } +/// Reads a big-endian int of type T from bytes. +/// bytes.len must be exactly @sizeOf(T). +pub fn readIntBE(comptime T: type, bytes: []const u8) -> T { + if (T.is_signed) { + return @bitCast(T, readIntBE(@IntType(false, T.bit_count), bytes)); + } + assert(bytes.len == @sizeOf(T)); + var result: T = 0; + {comptime var i = 0; inline while (i < @sizeOf(T)) : (i += 1) { + result = (result << 8) | T(bytes[i]); + }} + return result; +} + +/// Reads a little-endian int of type T from bytes. +/// bytes.len must be exactly @sizeOf(T). +pub fn readIntLE(comptime T: type, bytes: []const u8) -> T { + if (T.is_signed) { + return @bitCast(T, readIntLE(@IntType(false, T.bit_count), bytes)); + } + assert(bytes.len == @sizeOf(T)); + var result: T = 0; + {comptime var i = 0; inline while (i < @sizeOf(T)) : (i += 1) { + result |= T(bytes[i]) << i * 8; + }} + return result; +} + /// Writes an integer to memory with size equal to bytes.len. Pads with zeroes /// to fill the entire buffer provided. /// value must be an integer. -pub fn writeInt(buf: []u8, value: var, big_endian: bool) { +pub fn writeInt(buf: []u8, value: var, endian: builtin.Endian) { const uint = @IntType(false, @typeOf(value).bit_count); var bits = @truncate(uint, value); - if (big_endian) { - var index: usize = buf.len; - while (index != 0) { - index -= 1; + switch (endian) { + builtin.Endian.Big => { + var index: usize = buf.len; + while (index != 0) { + index -= 1; - buf[index] = @truncate(u8, bits); - bits >>= 8; - } - } else { - for (buf) |*b| { - *b = @truncate(u8, bits); - bits >>= 8; - } + buf[index] = @truncate(u8, bits); + bits >>= 8; + } + }, + builtin.Endian.Little => { + for (buf) |*b| { + *b = @truncate(u8, bits); + bits >>= 8; + } + }, } assert(bits == 0); } @@ -348,19 +384,30 @@ test "testReadInt" { fn testReadIntImpl() { { const bytes = []u8{ 0x12, 0x34, 0x56, 0x78 }; - assert(readInt(bytes, u32, true) == 0x12345678); - assert(readInt(bytes, u32, false) == 0x78563412); + assert(readInt(bytes, u32, builtin.Endian.Big) == 0x12345678); + assert(readIntBE(u32, bytes) == 0x12345678); + assert(readIntBE(i32, bytes) == 0x12345678); + assert(readInt(bytes, u32, builtin.Endian.Little) == 0x78563412); + assert(readIntLE(u32, bytes) == 0x78563412); + assert(readIntLE(i32, bytes) == 0x78563412); } { const buf = []u8{0x00, 0x00, 0x12, 0x34}; - const answer = readInt(buf, u64, true); + const answer = readInt(buf, u64, builtin.Endian.Big); assert(answer == 0x00001234); } { const buf = []u8{0x12, 0x34, 0x00, 0x00}; - const answer = readInt(buf, u64, false); + const answer = readInt(buf, u64, builtin.Endian.Little); assert(answer == 0x00003412); } + { + const bytes = []u8{0xff, 0xfe}; + assert(readIntBE(u16, bytes) == 0xfffe); + assert(readIntBE(i16, bytes) == -0x0002); + assert(readIntLE(u16, bytes) == 0xfeff); + assert(readIntLE(i16, bytes) == -0x0101); + } } test "testWriteInt" { @@ -370,16 +417,16 @@ test "testWriteInt" { fn testWriteIntImpl() { var bytes: [4]u8 = undefined; - writeInt(bytes[0..], u32(0x12345678), true); + writeInt(bytes[0..], u32(0x12345678), builtin.Endian.Big); assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 })); - writeInt(bytes[0..], u32(0x78563412), false); + writeInt(bytes[0..], u32(0x78563412), builtin.Endian.Little); assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 })); - writeInt(bytes[0..], u16(0x1234), true); + writeInt(bytes[0..], u16(0x1234), builtin.Endian.Big); assert(eql(u8, bytes, []u8{ 0x00, 0x00, 0x12, 0x34 })); - writeInt(bytes[0..], u16(0x1234), false); + writeInt(bytes[0..], u16(0x1234), builtin.Endian.Little); assert(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, 0x00 })); } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 005d9772e4..75a2dcf24d 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -58,7 +58,7 @@ pub const ChildProcess = struct { err_pipe: if (is_windows) void else [2]i32, llnode: if (is_windows) void else LinkedList(&ChildProcess).Node, - pub const Term = enum { + pub const Term = union(enum) { Exited: i32, Signal: i32, Stopped: i32, @@ -213,9 +213,9 @@ pub const ChildProcess = struct { self.term = (%Term)({ var exit_code: windows.DWORD = undefined; if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) { - Term.Unknown{0} + Term { .Unknown = 0 } } else { - Term.Exited {@bitCast(i32, exit_code)} + Term { .Exited = @bitCast(i32, exit_code)} } }); @@ -281,13 +281,13 @@ pub const ChildProcess = struct { fn statusToTerm(status: i32) -> Term { return if (posix.WIFEXITED(status)) { - Term.Exited { posix.WEXITSTATUS(status) } + Term { .Exited = posix.WEXITSTATUS(status) } } else if (posix.WIFSIGNALED(status)) { - Term.Signal { posix.WTERMSIG(status) } + Term { .Signal = posix.WTERMSIG(status) } } else if (posix.WIFSTOPPED(status)) { - Term.Stopped { posix.WSTOPSIG(status) } + Term { .Stopped = posix.WSTOPSIG(status) } } else { - Term.Unknown { status } + Term { .Unknown = status } }; } @@ -722,14 +722,14 @@ const ErrInt = @IntType(false, @sizeOf(error) * 8); fn writeIntFd(fd: i32, value: ErrInt) -> %void { var bytes: [@sizeOf(ErrInt)]u8 = undefined; - mem.writeInt(bytes[0..], value, true); + mem.writeInt(bytes[0..], value, builtin.endian); os.posixWrite(fd, bytes[0..]) %% return error.SystemResources; } fn readIntFd(fd: i32) -> %ErrInt { var bytes: [@sizeOf(ErrInt)]u8 = undefined; os.posixRead(fd, bytes[0..]) %% return error.SystemResources; - return mem.readInt(bytes[0..], ErrInt, true); + return mem.readInt(bytes[0..], ErrInt, builtin.endian); } extern fn sigchld_handler(_: i32) { diff --git a/std/os/index.zig b/std/os/index.zig index e6a5fc4d15..361750aedc 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -939,6 +939,7 @@ start_over: } pub const Dir = struct { + // See man getdents fd: i32, allocator: &Allocator, buf: []u8, @@ -981,7 +982,7 @@ pub const Dir = struct { pub fn close(self: &Dir) { self.allocator.free(self.buf); - close(self.fd); + os.close(self.fd); } /// Memory such as file names referenced in this returned entry becomes invalid @@ -1013,7 +1014,7 @@ pub const Dir = struct { break; } } - const linux_entry = @ptrCast(&LinuxEntry, &self.buf[self.index]); + const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]); const next_index = self.index + linux_entry.d_reclen; self.index = next_index; diff --git a/std/os/path.zig b/std/os/path.zig index a372b5b077..3fd7b5e2db 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -1016,7 +1016,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 { return os.readLink(allocator, proc_path); }, - else => @compileError("TODO implement os.path.real for " ++ @enumTagName(builtin.os)), + else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)), } } diff --git a/std/rand.zig b/std/rand.zig index d7798df3da..09e0c8ac78 100644 --- a/std/rand.zig +++ b/std/rand.zig @@ -50,12 +50,12 @@ pub const Rand = struct { pub fn fillBytes(r: &Rand, buf: []u8) { var bytes_left = buf.len; while (bytes_left >= @sizeOf(usize)) { - mem.writeInt(buf[buf.len - bytes_left..], r.rng.get(), false); + mem.writeInt(buf[buf.len - bytes_left..], r.rng.get(), builtin.Endian.Little); bytes_left -= @sizeOf(usize); } if (bytes_left > 0) { var rand_val_array: [@sizeOf(usize)]u8 = undefined; - mem.writeInt(rand_val_array[0..], r.rng.get(), false); + mem.writeInt(rand_val_array[0..], r.rng.get(), builtin.Endian.Little); while (bytes_left > 0) { buf[buf.len - bytes_left] = rand_val_array[@sizeOf(usize) - bytes_left]; bytes_left -= 1; @@ -98,7 +98,7 @@ pub const Rand = struct { while (true) { r.fillBytes(rand_val_array[0..]); - const rand_val = mem.readInt(rand_val_array, T, false); + const rand_val = mem.readInt(rand_val_array, T, builtin.Endian.Little); if (rand_val < upper_bound) { return start + (rand_val % total_range); } diff --git a/std/sort.zig b/std/sort.zig index 1d7ca507ee..d02d685e07 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -4,6 +4,19 @@ const math = @import("math/index.zig"); pub const Cmp = math.Cmp; +/// Stable sort using O(1) space. Currently implemented as insertion sort. +pub fn sort_stable(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b: &const T)->Cmp) { + {var i: usize = 1; while (i < array.len) : (i += 1) { + const x = array[i]; + var j: usize = i; + while (j > 0 and cmp(array[j - 1], x) == Cmp.Greater) : (j -= 1) { + array[j] = array[j - 1]; + } + array[j] = x; + }} +} + +/// Unstable sort using O(n) stack space. Currently implemented as quicksort. pub fn sort(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b: &const T)->Cmp) { if (array.len > 0) { quicksort(T, array, 0, array.len - 1, cmp); @@ -58,6 +71,63 @@ fn reverse(was: Cmp) -> Cmp { // --------------------------------------- // tests +test "stable sort" { + testStableSort(); + // TODO: uncomment this after https://github.com/zig-lang/zig/issues/639 + //comptime testStableSort(); +} +fn testStableSort() { + var expected = []IdAndValue { + IdAndValue{.id = 0, .value = 0}, + IdAndValue{.id = 1, .value = 0}, + IdAndValue{.id = 2, .value = 0}, + IdAndValue{.id = 0, .value = 1}, + IdAndValue{.id = 1, .value = 1}, + IdAndValue{.id = 2, .value = 1}, + IdAndValue{.id = 0, .value = 2}, + IdAndValue{.id = 1, .value = 2}, + IdAndValue{.id = 2, .value = 2}, + }; + var cases = [][9]IdAndValue { + []IdAndValue { + IdAndValue{.id = 0, .value = 0}, + IdAndValue{.id = 0, .value = 1}, + IdAndValue{.id = 0, .value = 2}, + IdAndValue{.id = 1, .value = 0}, + IdAndValue{.id = 1, .value = 1}, + IdAndValue{.id = 1, .value = 2}, + IdAndValue{.id = 2, .value = 0}, + IdAndValue{.id = 2, .value = 1}, + IdAndValue{.id = 2, .value = 2}, + }, + []IdAndValue { + IdAndValue{.id = 0, .value = 2}, + IdAndValue{.id = 0, .value = 1}, + IdAndValue{.id = 0, .value = 0}, + IdAndValue{.id = 1, .value = 2}, + IdAndValue{.id = 1, .value = 1}, + IdAndValue{.id = 1, .value = 0}, + IdAndValue{.id = 2, .value = 2}, + IdAndValue{.id = 2, .value = 1}, + IdAndValue{.id = 2, .value = 0}, + }, + }; + for (cases) |*case| { + sort_stable(IdAndValue, (*case)[0..], cmpByValue); + for (*case) |item, i| { + assert(item.id == expected[i].id); + assert(item.value == expected[i].value); + } + } +} +const IdAndValue = struct { + id: i32, + value: i32, +}; +fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) -> Cmp { + return i32asc(a.value, b.value); +} + test "testSort" { const u8cases = [][]const []const u8 { [][]const u8{"", ""}, diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig index c6a2babbf6..7e09c3d4d7 100644 --- a/std/special/compiler_rt/udivmod.zig +++ b/std/special/compiler_rt/udivmod.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); const is_test = builtin.is_test; -const low = if (builtin.is_big_endian) 1 else 0; +const low = switch (builtin.endian) { builtin.Endian.Big => 1, builtin.Endian.Little => 0 }; const high = 1 - low; pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?&DoubleInt) -> DoubleInt { diff --git a/test/cases/bugs/394.zig b/test/cases/bugs/394.zig index aaa7de6f88..071619d59c 100644 --- a/test/cases/bugs/394.zig +++ b/test/cases/bugs/394.zig @@ -1,9 +1,9 @@ -const E = enum { A: [9]u8, B: u64, }; +const E = union(enum) { A: [9]u8, B: u64, }; const S = struct { x: u8, y: E, }; const assert = @import("std").debug.assert; test "bug 394 fixed" { - const x = S { .x = 3, .y = E.B {1} }; + const x = S { .x = 3, .y = E {.B = 1 } }; assert(x.x == 3); } diff --git a/test/cases/enum.zig b/test/cases/enum.zig index f2d8ded0d6..ec900511eb 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -2,8 +2,8 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; test "enum type" { - const foo1 = Foo.One {13}; - const foo2 = Foo.Two { Point { .x = 1234, .y = 5678, }}; + const foo1 = Foo{ .One = 13}; + const foo2 = Foo{. Two = Point { .x = 1234, .y = 5678, }}; const bar = Bar.B; assert(bar == Bar.B); @@ -24,12 +24,12 @@ const Point = struct { x: u64, y: u64, }; -const Foo = enum { +const Foo = union(enum) { One: i32, Two: Point, Three: void, }; -const FooNoVoid = enum { +const FooNoVoid = union(enum) { One: i32, Two: Point, }; @@ -41,13 +41,13 @@ const Bar = enum { }; fn returnAnInt(x: i32) -> Foo { - Foo.One { x } + Foo { .One = x } } test "constant enum with payload" { - var empty = AnEnumWithPayload.Empty; - var full = AnEnumWithPayload.Full {13}; + var empty = AnEnumWithPayload {.Empty = {}}; + var full = AnEnumWithPayload {.Full = 13}; shouldBeEmpty(empty); shouldBeNotEmpty(full); } @@ -66,8 +66,8 @@ fn shouldBeNotEmpty(x: &const AnEnumWithPayload) { } } -const AnEnumWithPayload = enum { - Empty, +const AnEnumWithPayload = union(enum) { + Empty: void, Full: i32, }; @@ -89,8 +89,8 @@ test "enum to int" { shouldEqual(Number.Four, 4); } -fn shouldEqual(n: Number, expected: usize) { - assert(usize(n) == expected); +fn shouldEqual(n: Number, expected: u3) { + assert(u3(n) == expected); } @@ -98,7 +98,7 @@ test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) { - assert(IntToEnumNumber(x) == IntToEnumNumber.Three); + assert(IntToEnumNumber(u3(x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -109,13 +109,13 @@ const IntToEnumNumber = enum { }; -test "@enumTagName" { +test "@tagName" { assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); } fn testEnumTagNameBare(n: BareNumber) -> []const u8 { - return @enumTagName(n); + return @tagName(n); } const BareNumber = enum { @@ -132,12 +132,11 @@ test "enum alignment" { } } -const AlignTestEnum = enum { +const AlignTestEnum = union(enum) { A: [9]u8, B: u64, }; -const ValueCount0 = enum {}; const ValueCount1 = enum { I0 }; const ValueCount2 = enum { I0, I1 }; const ValueCount256 = enum { @@ -183,10 +182,165 @@ const ValueCount257 = enum { test "enum sizes" { comptime { - assert(@sizeOf(ValueCount0) == 0); assert(@sizeOf(ValueCount1) == 0); assert(@sizeOf(ValueCount2) == 1); assert(@sizeOf(ValueCount256) == 1); assert(@sizeOf(ValueCount257) == 2); } } + +const Small2 = enum (u2) { + One, + Two, +}; +const Small = enum (u2) { + One, + Two, + Three, + Four, +}; + +test "set enum tag type" { + { + var x = Small.One; + x = Small.Two; + comptime assert(@TagType(Small) == u2); + } + { + var x = Small2.One; + x = Small2.Two; + comptime assert(@TagType(Small2) == u2); + } +} + + +const A = enum (u3) { + One, + Two, + Three, + Four, + One2, + Two2, + Three2, + Four2, +}; + +const B = enum (u3) { + One3, + Two3, + Three3, + Four3, + One23, + Two23, + Three23, + Four23, +}; + +const C = enum (u2) { + One4, + Two4, + Three4, + Four4, +}; + +const BitFieldOfEnums = packed struct { + a: A, + b: B, + c: C, +}; + +const bit_field_1 = BitFieldOfEnums { + .a = A.Two, + .b = B.Three3, + .c = C.Four4, +}; + +test "bit field access with enum fields" { + var data = bit_field_1; + assert(getA(&data) == A.Two); + assert(getB(&data) == B.Three3); + assert(getC(&data) == C.Four4); + comptime assert(@sizeOf(BitFieldOfEnums) == 1); + + data.b = B.Four3; + assert(data.b == B.Four3); + + data.a = A.Three; + assert(data.a == A.Three); + assert(data.b == B.Four3); +} + +fn getA(data: &const BitFieldOfEnums) -> A { + return data.a; +} + +fn getB(data: &const BitFieldOfEnums) -> B { + return data.b; +} + +fn getC(data: &const BitFieldOfEnums) -> C { + return data.c; +} + +test "casting enum to its tag type" { + testCastEnumToTagType(Small2.Two); + comptime testCastEnumToTagType(Small2.Two); +} + +fn testCastEnumToTagType(value: Small2) { + assert(u2(value) == 1); +} + +const MultipleChoice = enum(u32) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; + +test "enum with specified tag values" { + testEnumWithSpecifiedTagValues(MultipleChoice.C); + comptime testEnumWithSpecifiedTagValues(MultipleChoice.C); +} + +fn testEnumWithSpecifiedTagValues(x: MultipleChoice) { + assert(u32(x) == 60); + assert(1234 == switch (x) { + MultipleChoice.A => 1, + MultipleChoice.B => 2, + MultipleChoice.C => u32(1234), + MultipleChoice.D => 4, + }); +} + +const MultipleChoice2 = enum(u32) { + Unspecified1, + A = 20, + Unspecified2, + B = 40, + Unspecified3, + C = 60, + Unspecified4, + D = 1000, + Unspecified5, +}; + +test "enum with specified and unspecified tag values" { + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); +} + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) { + assert(u32(x) == 1000); + assert(1234 == switch (x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => 3, + MultipleChoice2.D => u32(1234), + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 67ed0410bc..ae48a266d0 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -2,7 +2,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; const fmt = @import("std").fmt; -const ET = enum { +const ET = union(enum) { SINT: i32, UINT: u32, @@ -15,8 +15,8 @@ const ET = enum { }; test "enum with members" { - const a = ET.SINT { -42 }; - const b = ET.UINT { 42 }; + const a = ET { .SINT = -42 }; + const b = ET { .UINT = 42 }; var buf: [20]u8 = undefined; assert(%%a.print(buf[0..]) == 3); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 76e1b829ee..9f4f064f6b 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -324,8 +324,8 @@ test "constant enum initialization with differing sizes" { test3_1(test3_foo); test3_2(test3_bar); } -const Test3Foo = enum { - One, +const Test3Foo = union(enum) { + One: void, Two: f32, Three: Test3Point, }; @@ -333,8 +333,8 @@ const Test3Point = struct { x: i32, y: i32, }; -const test3_foo = Test3Foo.Three{Test3Point {.x = 3, .y = 4}}; -const test3_bar = Test3Foo.Two{13}; +const test3_foo = Test3Foo { .Three = Test3Point {.x = 3, .y = 4}}; +const test3_bar = Test3Foo { .Two = 13}; fn test3_1(f: &const Test3Foo) { switch (*f) { Test3Foo.Three => |pt| { @@ -449,7 +449,8 @@ fn testArray2DConstDoublePtr(ptr: &const f32) { const Tid = builtin.TypeId; const AStruct = struct { x: i32, }; const AnEnum = enum { One, Two, }; -const AnEnumWithPayload = enum { One: i32, Two, }; +const AUnionEnum = union(enum) { One: i32, Two: void, }; +const AUnion = union { One: void, Two: void }; test "@typeId" { comptime { @@ -474,8 +475,9 @@ test "@typeId" { assert(@typeId(%i32) == Tid.ErrorUnion); assert(@typeId(error) == Tid.Error); assert(@typeId(AnEnum) == Tid.Enum); - assert(@typeId(@typeOf(AnEnumWithPayload.One)) == Tid.EnumTag); - // TODO union + assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); + assert(@typeId(AUnionEnum) == Tid.Union); + assert(@typeId(AUnion) == Tid.Union); assert(@typeId(fn()) == Tid.Fn); assert(@typeId(@typeOf(builtin)) == Tid.Namespace); assert(@typeId(@typeOf({this})) == Tid.Block); diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 4227f79a04..cbd98d212f 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -62,8 +62,8 @@ const Foo = struct { three: void, }; -const Bar = enum { - One, +const Bar = union(enum) { + One: void, Two: i32, Three: bool, Four: f64, diff --git a/test/cases/switch.zig b/test/cases/switch.zig index 154e251a04..11c2178427 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -83,14 +83,14 @@ const SwitchStatmentFoo = enum { test "switch prong with variable" { - switchProngWithVarFn(SwitchProngWithVarEnum.One {13}); - switchProngWithVarFn(SwitchProngWithVarEnum.Two {13.0}); - switchProngWithVarFn(SwitchProngWithVarEnum.Meh); + switchProngWithVarFn(SwitchProngWithVarEnum { .One = 13}); + switchProngWithVarFn(SwitchProngWithVarEnum { .Two = 13.0}); + switchProngWithVarFn(SwitchProngWithVarEnum { .Meh = {}}); } -const SwitchProngWithVarEnum = enum { +const SwitchProngWithVarEnum = union(enum) { One: i32, Two: f32, - Meh, + Meh: void, }; fn switchProngWithVarFn(a: &const SwitchProngWithVarEnum) { switch(*a) { @@ -112,7 +112,7 @@ test "switch on enum using pointer capture" { } fn testSwitchEnumPtrCapture() { - var value = SwitchProngWithVarEnum.One { 1234 }; + var value = SwitchProngWithVarEnum { .One = 1234 }; switch (value) { SwitchProngWithVarEnum.One => |*x| *x += 1, else => unreachable, @@ -136,13 +136,13 @@ fn returnsFive() -> i32 { } -const Number = enum { +const Number = union(enum) { One: u64, Two: u8, Three: f32, }; -const number = Number.Three { 1.23 }; +const number = Number { .Three = 1.23 }; fn returnsFalse() -> bool { switch (number) { @@ -224,3 +224,14 @@ fn switchWithUnreachable(x: i32) -> i32 { } return 10; } + +fn return_a_number() -> %i32 { + return 1; +} + +test "capture value of switch with all unreachable prongs" { + const x = return_a_number() %% |err| switch (err) { + else => unreachable, + }; + assert(x == 1); +} diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig index d565de8c71..21f6b04037 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/cases/switch_prong_err_enum.zig @@ -9,14 +9,14 @@ fn readOnce() -> %u64 { error InvalidDebugInfo; -const FormValue = enum { +const FormValue = union(enum) { Address: u64, Other: bool, }; fn doThing(form_id: u64) -> %FormValue { return switch (form_id) { - 17 => FormValue.Address { %return readOnce() }, + 17 => FormValue { .Address = %return readOnce() }, else => error.InvalidDebugInfo, } } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig index 131c5e1c80..e7fe53b841 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/cases/switch_prong_implicit_cast.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; -const FormValue = enum { - One, +const FormValue = union(enum) { + One: void, Two: bool, }; @@ -9,8 +9,8 @@ error Whatever; fn foo(id: u64) -> %FormValue { switch (id) { - 2 => FormValue.Two { true }, - 1 => FormValue.One, + 2 => FormValue { .Two = true }, + 1 => FormValue { .One = {} }, else => return error.Whatever, } } diff --git a/test/cases/union.zig b/test/cases/union.zig index 4044721582..1db9a1bef1 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; -const Value = enum { +const Value = union(enum) { Int: u64, Array: [9]u8, }; @@ -10,8 +10,8 @@ const Agg = struct { val2: Value, }; -const v1 = Value.Int { 1234 }; -const v2 = Value.Array { []u8{3} ** 9 }; +const v1 = Value { .Int = 1234 }; +const v2 = Value { .Array = []u8{3} ** 9 }; const err = (%Agg)(Agg { .val1 = v1, @@ -75,3 +75,118 @@ test "basic extern unions" { assert(foo.float == 12.34); } + +const Letter = enum { + A, + B, + C, +}; +const Payload = union(Letter) { + A: i32, + B: f64, + C: bool, +}; + +test "union with specified enum tag" { + doTest(); + comptime doTest(); +} + +fn doTest() { + assert(bar(Payload {.A = 1234}) == -10); +} + +fn bar(value: &const Payload) -> i32 { + assert(Letter(*value) == Letter.A); + return switch (*value) { + Payload.A => |x| return x - 1244, + Payload.B => |x| if (x == 12.34) i32(20) else 21, + Payload.C => |x| if (x) i32(30) else 31, + }; +} + +const MultipleChoice = union(enum(u32)) { + A = 20, + B = 40, + C = 60, + D = 1000, +}; +test "simple union(enum(u32))" { + var x = MultipleChoice.C; + assert(x == MultipleChoice.C); + assert(u32(@TagType(MultipleChoice)(x)) == 60); +} + +const MultipleChoice2 = union(enum(u32)) { + Unspecified1: i32, + A: f32 = 20, + Unspecified2: void, + B: bool = 40, + Unspecified3: i32, + C: i8 = 60, + Unspecified4: void, + D: void = 1000, + Unspecified5: i32, +}; + +test "union(enum(u32)) with specified and unspecified tag values" { + comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2 {.C = 123}); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2 { .C = 123} ); +} + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) { + assert(u32(@TagType(MultipleChoice2)(*x)) == 60); + assert(1123 == switch (*x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => |v| i32(1000) + v, + MultipleChoice2.D => 4, + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} + + +const ExternPtrOrInt = extern union { + ptr: &u8, + int: u64 +}; +test "extern union size" { + comptime assert(@sizeOf(ExternPtrOrInt) == 8); +} + +const PackedPtrOrInt = packed union { + ptr: &u8, + int: u64 +}; +test "extern union size" { + comptime assert(@sizeOf(PackedPtrOrInt) == 8); +} + +const ZeroBits = union { + OnlyField: void, +}; +test "union with only 1 field which is void should be zero bits" { + comptime assert(@sizeOf(ZeroBits) == 0); +} + +const TheTag = enum {A, B, C}; +const TheUnion = union(TheTag) { A: i32, B: i32, C: i32 }; +test "union field access gives the enum values" { + assert(TheUnion.A == TheTag.A); + assert(TheUnion.B == TheTag.B); + assert(TheUnion.C == TheTag.C); +} + +test "cast union to tag type of union" { + testCastUnionToTagType(TheUnion {.B = 1234}); + comptime testCastUnionToTagType(TheUnion {.B = 1234}); +} + +fn testCastUnionToTagType(x: &const TheUnion) { + assert(TheTag(*x) == TheTag.B); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 3ef4a63e5f..5d13ed8d48 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -930,8 +930,8 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\fn bad_eql_1(a: []u8, b: []u8) -> bool { \\ a == b \\} - \\const EnumWithData = enum { - \\ One, + \\const EnumWithData = union(enum) { + \\ One: void, \\ Two: i32, \\}; \\fn bad_eql_2(a: &const EnumWithData, b: &const EnumWithData) -> bool { @@ -1145,19 +1145,19 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\const JasonHM = u8; \\const JasonList = &JsonNode; \\ - \\const JsonOA = enum { + \\const JsonOA = union(enum) { \\ JSONArray: JsonList, \\ JSONObject: JasonHM, \\}; \\ - \\const JsonType = enum { + \\const JsonType = union(enum) { \\ JSONNull: void, \\ JSONInteger: isize, \\ JSONDouble: f64, \\ JSONBool: bool, \\ JSONString: []u8, - \\ JSONArray, - \\ JSONObject, + \\ JSONArray: void, + \\ JSONObject: void, \\}; \\ \\pub const JsonNode = struct { @@ -2138,7 +2138,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\ \\const MdText = ArrayList(u8); \\ - \\const MdNode = enum { + \\const MdNode = union(enum) { \\ Header: struct { \\ text: MdText, \\ weight: HeaderValue, @@ -2297,6 +2297,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:2:21: error: type 'i32' does not support @memberType"); + cases.add("@memberType on enum", + \\comptime { + \\ _ = @memberType(Foo, 0); + \\} + \\const Foo = enum {A,}; + , + ".tmp_source.zig:2:21: error: type 'Foo' does not support @memberType"); + cases.add("@memberType struct out of bounds", \\comptime { \\ _ = @memberType(Foo, 0); @@ -2305,13 +2313,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) { , ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); - cases.add("@memberType enum out of bounds", + cases.add("@memberType union out of bounds", \\comptime { - \\ _ = @memberType(Foo, 0); + \\ _ = @memberType(Foo, 1); \\} - \\const Foo = enum {}; + \\const Foo = union {A: void,}; , - ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); + ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); cases.add("@memberName on unsupported type", \\comptime { @@ -2330,11 +2338,19 @@ pub fn addCases(cases: &tests.CompileErrorContext) { cases.add("@memberName enum out of bounds", \\comptime { - \\ _ = @memberName(Foo, 0); + \\ _ = @memberName(Foo, 1); \\} - \\const Foo = enum {}; + \\const Foo = enum {A,}; , - ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members"); + ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); + + cases.add("@memberName union out of bounds", + \\comptime { + \\ _ = @memberName(Foo, 1); + \\} + \\const Foo = union {A:i32,}; + , + ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members"); cases.add("calling var args extern function, passing array instead of pointer", \\export fn entry() { @@ -2362,4 +2378,310 @@ pub fn addCases(cases: &tests.CompileErrorContext) { ".tmp_source.zig:4:25: error: aoeu", ".tmp_source.zig:1:36: note: called from here", ".tmp_source.zig:12:20: note: referenced here"); + + cases.add("specify enum tag type that is too small", + \\const Small = enum (u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\ Five, + \\}; + \\ + \\export fn entry() { + \\ var x = Small.One; + \\} + , + ".tmp_source.zig:1:20: error: 'u2' too small to hold all bits; must be at least 'u3'"); + + cases.add("specify non-integer enum tag type", + \\const Small = enum (f32) { + \\ One, + \\ Two, + \\ Three, + \\}; + \\ + \\export fn entry() { + \\ var x = Small.One; + \\} + , + ".tmp_source.zig:1:20: error: expected integer, found 'f32'"); + + cases.add("implicitly casting enum to tag type", + \\const Small = enum(u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var x: u2 = Small.Two; + \\} + , + ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'"); + + cases.add("explicitly casting enum to non tag type", + \\const Small = enum(u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var x = u3(Small.Two); + \\} + , + ".tmp_source.zig:9:15: error: enum to integer cast to 'u3' instead of its tag type, 'u2'"); + + cases.add("explicitly casting non tag type to enum", + \\const Small = enum(u2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var y = u3(3); + \\ var x = Small(y); + \\} + , + ".tmp_source.zig:10:18: error: integer to enum cast from 'u3' instead of its tag type, 'u2'"); + + cases.add("non unsigned integer enum tag type", + \\const Small = enum(i2) { + \\ One, + \\ Two, + \\ Three, + \\ Four, + \\}; + \\ + \\export fn entry() { + \\ var y = Small.Two; + \\} + , + ".tmp_source.zig:1:19: error: expected unsigned integer, found 'i2'"); + + cases.add("struct fields with value assignments", + \\const MultipleChoice = struct { + \\ A: i32 = 20, + \\}; + \\export fn entry() { + \\ var x: MultipleChoice = undefined; + \\} + , + ".tmp_source.zig:2:14: error: enums, not structs, support field assignment"); + + cases.add("union fields with value assignments", + \\const MultipleChoice = union { + \\ A: i32 = 20, + \\}; + \\export fn entry() { + \\ var x: MultipleChoice = undefined; + \\} + , + ".tmp_source.zig:2:14: error: non-enum union field assignment", + ".tmp_source.zig:1:24: note: consider 'union(enum)' here"); + + cases.add("enum with 0 fields", + \\const Foo = enum {}; + \\export fn entry() -> usize { + \\ return @sizeOf(Foo); + \\} + , + ".tmp_source.zig:1:13: error: enums must have 1 or more fields"); + + cases.add("union with 0 fields", + \\const Foo = union {}; + \\export fn entry() -> usize { + \\ return @sizeOf(Foo); + \\} + , + ".tmp_source.zig:1:13: error: unions must have 1 or more fields"); + + cases.add("enum value already taken", + \\const MultipleChoice = enum(u32) { + \\ A = 20, + \\ B = 40, + \\ C = 60, + \\ D = 1000, + \\ E = 60, + \\}; + \\export fn entry() { + \\ var x = MultipleChoice.C; + \\} + , + ".tmp_source.zig:6:9: error: enum tag value 60 already taken", + ".tmp_source.zig:4:9: note: other occurrence here"); + + cases.add("union with specified enum omits field", + \\const Letter = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\const Payload = union(Letter) { + \\ A: i32, + \\ B: f64, + \\}; + \\export fn entry() -> usize { + \\ return @sizeOf(Payload); + \\} + , + ".tmp_source.zig:6:17: error: enum field missing: 'C'", + ".tmp_source.zig:4:5: note: declared here"); + + cases.add("@TagType when union has no attached enum", + \\const Foo = union { + \\ A: i32, + \\}; + \\export fn entry() { + \\ const x = @TagType(Foo); + \\} + , + ".tmp_source.zig:5:24: error: union 'Foo' has no tag", + ".tmp_source.zig:1:13: note: consider 'union(enum)' here"); + + cases.add("non-integer tag type to automatic union enum", + \\const Foo = union(enum(f32)) { + \\ A: i32, + \\}; + \\export fn entry() { + \\ const x = @TagType(Foo); + \\} + , + ".tmp_source.zig:1:23: error: expected integer tag type, found 'f32'"); + + cases.add("non-enum tag type passed to union", + \\const Foo = union(u32) { + \\ A: i32, + \\}; + \\export fn entry() { + \\ const x = @TagType(Foo); + \\} + , + ".tmp_source.zig:1:18: error: expected enum tag type, found 'u32'"); + + cases.add("union auto-enum value already taken", + \\const MultipleChoice = union(enum(u32)) { + \\ A = 20, + \\ B = 40, + \\ C = 60, + \\ D = 1000, + \\ E = 60, + \\}; + \\export fn entry() { + \\ var x = MultipleChoice { .C = {} }; + \\} + , + ".tmp_source.zig:6:9: error: enum tag value 60 already taken", + ".tmp_source.zig:4:9: note: other occurrence here"); + + cases.add("union enum field does not match enum", + \\const Letter = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\const Payload = union(Letter) { + \\ A: i32, + \\ B: f64, + \\ C: bool, + \\ D: bool, + \\}; + \\export fn entry() { + \\ var a = Payload {.A = 1234}; + \\} + , + ".tmp_source.zig:10:5: error: enum field not found: 'D'", + ".tmp_source.zig:1:16: note: enum declared here"); + + cases.add("field type supplied in an enum", + \\const Letter = enum { + \\ A: void, + \\ B, + \\ C, + \\}; + \\export fn entry() { + \\ var b = Letter.B; + \\} + , + ".tmp_source.zig:2:8: error: structs and unions, not enums, support field types", + ".tmp_source.zig:1:16: note: consider 'union(enum)' here"); + + cases.add("struct field missing type", + \\const Letter = struct { + \\ A, + \\}; + \\export fn entry() { + \\ var a = Letter { .A = {} }; + \\} + , + ".tmp_source.zig:2:5: error: struct field missing type"); + + cases.add("extern union field missing type", + \\const Letter = extern union { + \\ A, + \\}; + \\export fn entry() { + \\ var a = Letter { .A = {} }; + \\} + , + ".tmp_source.zig:2:5: error: union field missing type"); + + cases.add("extern union given enum tag type", + \\const Letter = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\const Payload = extern union(Letter) { + \\ A: i32, + \\ B: f64, + \\ C: bool, + \\}; + \\export fn entry() { + \\ var a = Payload { .A = { 1234 } }; + \\} + , + ".tmp_source.zig:6:29: error: extern union does not support enum tag type"); + + cases.add("packed union given enum tag type", + \\const Letter = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\const Payload = packed union(Letter) { + \\ A: i32, + \\ B: f64, + \\ C: bool, + \\}; + \\export fn entry() { + \\ var a = Payload { .A = { 1234 } }; + \\} + , + ".tmp_source.zig:6:29: error: packed union does not support enum tag type"); + + cases.add("switch on union with no attached enum", + \\const Payload = union { + \\ A: i32, + \\ B: f64, + \\ C: bool, + \\}; + \\export fn entry() { + \\ const a = Payload { .A = { 1234 } }; + \\ foo(a); + \\} + \\fn foo(a: &const Payload) { + \\ switch (*a) { + \\ Payload.A => {}, + \\ else => unreachable, + \\ } + \\} + , + ".tmp_source.zig:11:13: error: switch on union which has no attached enum", + ".tmp_source.zig:1:17: note: consider 'union(enum)' here"); } diff --git a/test/tests.zig b/test/tests.zig index 73d9646552..e74afa1755 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -150,8 +150,8 @@ pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []cons continue; } const these_tests = b.addTest(root_src); - these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @enumTagName(test_target.os), - @enumTagName(test_target.arch), @enumTagName(mode), if (link_libc) "c" else "bare")); + these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), + @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare")); these_tests.setFilter(test_filter); these_tests.setBuildMode(mode); if (!is_native) { @@ -428,7 +428,7 @@ pub const CompareOutputContext = struct { Special.None => { for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} {} ({})", - "compare-output", case.name, @enumTagName(mode)); + "compare-output", case.name, @tagName(mode)); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; @@ -682,7 +682,7 @@ pub const CompileErrorContext = struct { for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "compile-error {} ({})", - case.name, @enumTagName(mode)); + case.name, @tagName(mode)); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; @@ -750,7 +750,7 @@ pub const BuildExamplesContext = struct { for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| { const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "build {} ({})", - root_src, @enumTagName(mode)); + root_src, @tagName(mode)); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; diff --git a/test/translate_c.zig b/test/translate_c.zig index 90b99b5faf..d4974109da 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1178,4 +1178,14 @@ pub fn addCases(cases: &tests.TranslateCContext) { , \\pub var v0: ?&const u8 = c"0.0.0"; ); + + cases.add("static incomplete array inside function", + \\void foo(void) { + \\ static const char v2[] = "2.2.2"; + \\} + , + \\pub fn foo() { + \\ const v2: &const u8 = c"2.2.2"; + \\} + ); }