]> git.proxmox.com Git - rustc.git/commitdiff
Imported Upstream version 1.11.0+dfsg1
authorXimin Luo <infinity0@debian.org>
Mon, 22 Aug 2016 21:21:25 +0000 (23:21 +0200)
committerXimin Luo <infinity0@debian.org>
Mon, 22 Aug 2016 21:21:25 +0000 (23:21 +0200)
1620 files changed:
CONTRIBUTING.md
Makefile.in
README.md
configure
mk/cfg/i686-unknown-linux-gnu.mk
mk/cfg/i686-unknown-linux-musl.mk
mk/cfg/mips-unknown-linux-gnu.mk
mk/cfg/x86_64-unknown-linux-musl.mk
mk/crates.mk
mk/llvm.mk
mk/main.mk
mk/rt.mk
mk/tests.mk
src/bootstrap/README.md
src/bootstrap/bootstrap.py
src/bootstrap/build/cc.rs
src/bootstrap/build/check.rs
src/bootstrap/build/clean.rs
src/bootstrap/build/config.rs
src/bootstrap/build/dist.rs
src/bootstrap/build/mod.rs
src/bootstrap/build/native.rs
src/bootstrap/build/sanity.rs
src/bootstrap/build/step.rs
src/bootstrap/mk/Makefile.in
src/build_helper/lib.rs
src/compiler-rt/CMakeLists.txt
src/compiler-rt/cmake/Modules/AddCompilerRT.cmake
src/compiler-rt/cmake/Modules/CompilerRTCompile.cmake
src/compiler-rt/cmake/Modules/CompilerRTDarwinUtils.cmake
src/compiler-rt/cmake/Modules/CompilerRTUtils.cmake
src/compiler-rt/cmake/config-ix.cmake
src/compiler-rt/include/sanitizer/common_interface_defs.h
src/compiler-rt/include/sanitizer/coverage_interface.h
src/compiler-rt/lib/CMakeLists.txt
src/compiler-rt/lib/asan/.clang-format [new file with mode: 0644]
src/compiler-rt/lib/asan/README.txt
src/compiler-rt/lib/asan/asan_activation.cc
src/compiler-rt/lib/asan/asan_flags.cc
src/compiler-rt/lib/asan/asan_flags.inc
src/compiler-rt/lib/asan/asan_interceptors.cc
src/compiler-rt/lib/asan/asan_interface_internal.h
src/compiler-rt/lib/asan/asan_internal.h
src/compiler-rt/lib/asan/asan_linux.cc
src/compiler-rt/lib/asan/asan_mac.cc
src/compiler-rt/lib/asan/asan_malloc_linux.cc
src/compiler-rt/lib/asan/asan_malloc_mac.cc
src/compiler-rt/lib/asan/asan_mapping.h
src/compiler-rt/lib/asan/asan_new_delete.cc
src/compiler-rt/lib/asan/asan_poisoning.cc
src/compiler-rt/lib/asan/asan_posix.cc
src/compiler-rt/lib/asan/asan_report.cc
src/compiler-rt/lib/asan/asan_report.h
src/compiler-rt/lib/asan/asan_rtl.cc
src/compiler-rt/lib/asan/asan_win.cc
src/compiler-rt/lib/asan/asan_win_dll_thunk.cc
src/compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cc
src/compiler-rt/lib/asan/scripts/asan_device_setup
src/compiler-rt/lib/asan/scripts/asan_symbolize.py
src/compiler-rt/lib/asan/tests/asan_mac_test.cc
src/compiler-rt/lib/asan/tests/asan_noinst_test.cc
src/compiler-rt/lib/asan/tests/asan_test.cc
src/compiler-rt/lib/asan/tests/asan_test_main.cc
src/compiler-rt/lib/builtins/CMakeLists.txt
src/compiler-rt/lib/builtins/Darwin-excludes/ios-armv7.txt
src/compiler-rt/lib/builtins/Darwin-excludes/ios-armv7s.txt
src/compiler-rt/lib/builtins/arm/adddf3vfp.S
src/compiler-rt/lib/builtins/arm/addsf3vfp.S
src/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S
src/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S
src/compiler-rt/lib/builtins/arm/aeabi_dcmp.S
src/compiler-rt/lib/builtins/arm/aeabi_fcmp.S
src/compiler-rt/lib/builtins/arm/aeabi_idivmod.S
src/compiler-rt/lib/builtins/arm/aeabi_ldivmod.S
src/compiler-rt/lib/builtins/arm/aeabi_memcmp.S
src/compiler-rt/lib/builtins/arm/aeabi_memcpy.S
src/compiler-rt/lib/builtins/arm/aeabi_memmove.S
src/compiler-rt/lib/builtins/arm/aeabi_memset.S
src/compiler-rt/lib/builtins/arm/aeabi_uidivmod.S
src/compiler-rt/lib/builtins/arm/aeabi_uldivmod.S
src/compiler-rt/lib/builtins/arm/bswapdi2.S
src/compiler-rt/lib/builtins/arm/bswapsi2.S
src/compiler-rt/lib/builtins/arm/clzdi2.S
src/compiler-rt/lib/builtins/arm/clzsi2.S
src/compiler-rt/lib/builtins/arm/comparesf2.S
src/compiler-rt/lib/builtins/arm/divdf3vfp.S
src/compiler-rt/lib/builtins/arm/divmodsi4.S
src/compiler-rt/lib/builtins/arm/divsf3vfp.S
src/compiler-rt/lib/builtins/arm/divsi3.S
src/compiler-rt/lib/builtins/arm/eqdf2vfp.S
src/compiler-rt/lib/builtins/arm/eqsf2vfp.S
src/compiler-rt/lib/builtins/arm/extendsfdf2vfp.S
src/compiler-rt/lib/builtins/arm/fixdfsivfp.S
src/compiler-rt/lib/builtins/arm/fixsfsivfp.S
src/compiler-rt/lib/builtins/arm/fixunsdfsivfp.S
src/compiler-rt/lib/builtins/arm/fixunssfsivfp.S
src/compiler-rt/lib/builtins/arm/floatsidfvfp.S
src/compiler-rt/lib/builtins/arm/floatsisfvfp.S
src/compiler-rt/lib/builtins/arm/floatunssidfvfp.S
src/compiler-rt/lib/builtins/arm/floatunssisfvfp.S
src/compiler-rt/lib/builtins/arm/gedf2vfp.S
src/compiler-rt/lib/builtins/arm/gesf2vfp.S
src/compiler-rt/lib/builtins/arm/gtdf2vfp.S
src/compiler-rt/lib/builtins/arm/gtsf2vfp.S
src/compiler-rt/lib/builtins/arm/ledf2vfp.S
src/compiler-rt/lib/builtins/arm/lesf2vfp.S
src/compiler-rt/lib/builtins/arm/ltdf2vfp.S
src/compiler-rt/lib/builtins/arm/ltsf2vfp.S
src/compiler-rt/lib/builtins/arm/modsi3.S
src/compiler-rt/lib/builtins/arm/muldf3vfp.S
src/compiler-rt/lib/builtins/arm/mulsf3vfp.S
src/compiler-rt/lib/builtins/arm/nedf2vfp.S
src/compiler-rt/lib/builtins/arm/negdf2vfp.S
src/compiler-rt/lib/builtins/arm/negsf2vfp.S
src/compiler-rt/lib/builtins/arm/nesf2vfp.S
src/compiler-rt/lib/builtins/arm/restore_vfp_d8_d15_regs.S
src/compiler-rt/lib/builtins/arm/save_vfp_d8_d15_regs.S
src/compiler-rt/lib/builtins/arm/subdf3vfp.S
src/compiler-rt/lib/builtins/arm/subsf3vfp.S
src/compiler-rt/lib/builtins/arm/switch16.S
src/compiler-rt/lib/builtins/arm/switch32.S
src/compiler-rt/lib/builtins/arm/switch8.S
src/compiler-rt/lib/builtins/arm/switchu8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_add_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_add_8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_and_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_and_8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_max_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_max_8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_min_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_min_8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_nand_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_nand_8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_or_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_or_8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_sub_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_sub_8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_umax_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_umax_8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_umin_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_umin_8.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_xor_4.S
src/compiler-rt/lib/builtins/arm/sync_fetch_and_xor_8.S
src/compiler-rt/lib/builtins/arm/sync_synchronize.S
src/compiler-rt/lib/builtins/arm/truncdfsf2vfp.S
src/compiler-rt/lib/builtins/arm/udivmodsi4.S
src/compiler-rt/lib/builtins/arm/udivsi3.S
src/compiler-rt/lib/builtins/arm/umodsi3.S
src/compiler-rt/lib/builtins/arm/unorddf2vfp.S
src/compiler-rt/lib/builtins/arm/unordsf2vfp.S
src/compiler-rt/lib/builtins/assembly.h
src/compiler-rt/lib/builtins/divtc3.c [new file with mode: 0644]
src/compiler-rt/lib/builtins/gcc_personality_v0.c
src/compiler-rt/lib/builtins/i386/ashldi3.S
src/compiler-rt/lib/builtins/i386/ashrdi3.S
src/compiler-rt/lib/builtins/i386/divdi3.S
src/compiler-rt/lib/builtins/i386/floatdidf.S
src/compiler-rt/lib/builtins/i386/floatdisf.S
src/compiler-rt/lib/builtins/i386/floatdixf.S
src/compiler-rt/lib/builtins/i386/floatundidf.S
src/compiler-rt/lib/builtins/i386/floatundisf.S
src/compiler-rt/lib/builtins/i386/floatundixf.S
src/compiler-rt/lib/builtins/i386/lshrdi3.S
src/compiler-rt/lib/builtins/i386/moddi3.S
src/compiler-rt/lib/builtins/i386/muldi3.S
src/compiler-rt/lib/builtins/i386/udivdi3.S
src/compiler-rt/lib/builtins/i386/umoddi3.S
src/compiler-rt/lib/builtins/int_endianness.h
src/compiler-rt/lib/builtins/int_lib.h
src/compiler-rt/lib/builtins/int_types.h
src/compiler-rt/lib/builtins/ppc/DD.h
src/compiler-rt/lib/builtins/ppc/divtc3.c
src/compiler-rt/lib/builtins/ppc/multc3.c
src/compiler-rt/lib/builtins/ppc/restFP.S
src/compiler-rt/lib/builtins/ppc/saveFP.S
src/compiler-rt/lib/builtins/x86_64/floatundidf.S
src/compiler-rt/lib/builtins/x86_64/floatundisf.S
src/compiler-rt/lib/builtins/x86_64/floatundixf.S
src/compiler-rt/lib/cfi/CMakeLists.txt
src/compiler-rt/lib/cfi/cfi.cc [new file with mode: 0644]
src/compiler-rt/lib/dfsan/.clang-format [new file with mode: 0644]
src/compiler-rt/lib/dfsan/dfsan.cc
src/compiler-rt/lib/dfsan/dfsan.h
src/compiler-rt/lib/dfsan/dfsan_platform.h [new file with mode: 0644]
src/compiler-rt/lib/interception/.clang-format [new file with mode: 0644]
src/compiler-rt/lib/lsan/.clang-format [new file with mode: 0644]
src/compiler-rt/lib/lsan/lsan_common.cc
src/compiler-rt/lib/msan/.clang-format [new file with mode: 0644]
src/compiler-rt/lib/msan/msan.cc
src/compiler-rt/lib/msan/msan_interceptors.cc
src/compiler-rt/lib/msan/tests/msan_test.cc
src/compiler-rt/lib/profile/CMakeLists.txt
src/compiler-rt/lib/profile/GCDAProfiling.c
src/compiler-rt/lib/profile/InstrProfData.inc [new file with mode: 0644]
src/compiler-rt/lib/profile/InstrProfiling.c
src/compiler-rt/lib/profile/InstrProfiling.h
src/compiler-rt/lib/profile/InstrProfilingBuffer.c
src/compiler-rt/lib/profile/InstrProfilingFile.c
src/compiler-rt/lib/profile/InstrProfilingInternal.h
src/compiler-rt/lib/profile/InstrProfilingPlatformDarwin.c
src/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
src/compiler-rt/lib/profile/InstrProfilingPlatformOther.c
src/compiler-rt/lib/profile/InstrProfilingPort.h [new file with mode: 0644]
src/compiler-rt/lib/profile/InstrProfilingRuntime.cc
src/compiler-rt/lib/profile/InstrProfilingUtil.c
src/compiler-rt/lib/profile/InstrProfilingValue.c [new file with mode: 0644]
src/compiler-rt/lib/profile/InstrProfilingWriter.c [new file with mode: 0644]
src/compiler-rt/lib/profile/WindowsMMap.c [new file with mode: 0644]
src/compiler-rt/lib/profile/WindowsMMap.h [new file with mode: 0644]
src/compiler-rt/lib/safestack/.clang-format [new file with mode: 0644]
src/compiler-rt/lib/safestack/safestack.cc
src/compiler-rt/lib/sanitizer_common/.clang-format [new file with mode: 0644]
src/compiler-rt/lib/sanitizer_common/sanitizer_asm.h
src/compiler-rt/lib/sanitizer_common/sanitizer_common.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_common.h
src/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
src/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_common_nolibc.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
src/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h
src/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_libc.h
src/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_linux.h
src/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_mac.h
src/compiler-rt/lib/sanitizer_common/sanitizer_malloc_mac.inc
src/compiler-rt/lib/sanitizer_common/sanitizer_platform.h
src/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
src/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_posix.h
src/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_printf.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_linux.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h
src/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h
src/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_tls_get_addr.cc
src/compiler-rt/lib/sanitizer_common/sanitizer_win.cc
src/compiler-rt/lib/sanitizer_common/tests/sanitizer_common_test.cc
src/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc
src/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
src/compiler-rt/lib/tsan/.clang-format [new file with mode: 0644]
src/compiler-rt/lib/tsan/CMakeLists.txt
src/compiler-rt/lib/tsan/Makefile.old [deleted file]
src/compiler-rt/lib/tsan/analyze_libtsan.sh
src/compiler-rt/lib/tsan/check_analyze.sh
src/compiler-rt/lib/tsan/check_memcpy.sh [deleted file]
src/compiler-rt/lib/tsan/go/buildgo.sh
src/compiler-rt/lib/tsan/rtl/Makefile.old [deleted file]
src/compiler-rt/lib/tsan/rtl/tsan_defs.h
src/compiler-rt/lib/tsan/rtl/tsan_dense_alloc.h
src/compiler-rt/lib/tsan/rtl/tsan_flags.cc
src/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
src/compiler-rt/lib/tsan/rtl/tsan_interceptors.h
src/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc [new file with mode: 0644]
src/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc [new file with mode: 0644]
src/compiler-rt/lib/tsan/rtl/tsan_malloc_mac.cc
src/compiler-rt/lib/tsan/rtl/tsan_mman.cc
src/compiler-rt/lib/tsan/rtl/tsan_new_delete.cc
src/compiler-rt/lib/tsan/rtl/tsan_platform.h
src/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc
src/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc
src/compiler-rt/lib/tsan/rtl/tsan_platform_posix.cc
src/compiler-rt/lib/tsan/rtl/tsan_platform_windows.cc
src/compiler-rt/lib/tsan/rtl/tsan_ppc_regs.h [new file with mode: 0644]
src/compiler-rt/lib/tsan/rtl/tsan_report.cc
src/compiler-rt/lib/tsan/rtl/tsan_rtl.cc
src/compiler-rt/lib/tsan/rtl/tsan_rtl.h
src/compiler-rt/lib/tsan/rtl/tsan_rtl_aarch64.S [new file with mode: 0644]
src/compiler-rt/lib/tsan/rtl/tsan_rtl_amd64.S
src/compiler-rt/lib/tsan/rtl/tsan_rtl_ppc64.S [new file with mode: 0644]
src/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc
src/compiler-rt/lib/tsan/rtl/tsan_suppressions.cc
src/compiler-rt/lib/tsan/rtl/tsan_symbolize.cc
src/compiler-rt/lib/tsan/tests/CMakeLists.txt
src/compiler-rt/lib/tsan/tests/rtl/CMakeLists.txt
src/compiler-rt/lib/tsan/tests/rtl/tsan_posix.cc
src/compiler-rt/lib/tsan/tests/rtl/tsan_test.cc
src/compiler-rt/lib/tsan/tests/rtl/tsan_test_util.h
src/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_linux.cc [deleted file]
src/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cc [new file with mode: 0644]
src/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc
src/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc
src/compiler-rt/lib/ubsan/ubsan_checks.inc
src/compiler-rt/lib/ubsan/ubsan_diag.cc
src/compiler-rt/lib/ubsan/ubsan_diag.h
src/compiler-rt/lib/ubsan/ubsan_handlers.cc
src/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc
src/compiler-rt/make/config.mk
src/compiler-rt/make/platform/clang_darwin.mk [new file with mode: 0644]
src/compiler-rt/make/platform/clang_darwin_test_input.c [new file with mode: 0644]
src/compiler-rt/make/platform/clang_linux.mk [new file with mode: 0644]
src/compiler-rt/make/platform/clang_linux_test_input.c [new file with mode: 0644]
src/compiler-rt/make/platform/clang_macho_embedded.mk [new file with mode: 0644]
src/compiler-rt/make/platform/clang_macho_embedded_test_input.c [new file with mode: 0644]
src/compiler-rt/make/platform/clang_mingw.mk [new file with mode: 0644]
src/compiler-rt/make/platform/darwin_bni.mk [new file with mode: 0644]
src/compiler-rt/make/platform/multi_arch.mk [new file with mode: 0644]
src/compiler-rt/make/platform/triple.mk [deleted file]
src/compiler-rt/test/CMakeLists.txt
src/compiler-rt/test/asan/TestCases/Darwin/atos-symbolizer-dyld-root-path.cc
src/compiler-rt/test/asan/TestCases/Darwin/crashlog-stacktraces.c
src/compiler-rt/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc
src/compiler-rt/test/asan/TestCases/Darwin/interface_symbols_darwin.c
src/compiler-rt/test/asan/TestCases/Linux/calloc-preload.c [new file with mode: 0644]
src/compiler-rt/test/asan/TestCases/Linux/interface_symbols_linux.c
src/compiler-rt/test/asan/TestCases/Linux/mincore.cc [new file with mode: 0644]
src/compiler-rt/test/asan/TestCases/Linux/static_tls.cc
src/compiler-rt/test/asan/TestCases/Posix/halt_on_error-signals.c [new file with mode: 0644]
src/compiler-rt/test/asan/TestCases/Posix/halt_on_error-torture.cc [new file with mode: 0644]
src/compiler-rt/test/asan/TestCases/Posix/halt_on_error_suppress_equal_pcs.cc [new file with mode: 0644]
src/compiler-rt/test/asan/TestCases/alloca_vla_interact.cc
src/compiler-rt/test/asan/TestCases/coverage-pc-buffer.cc [new file with mode: 0644]
src/compiler-rt/test/asan/TestCases/coverage-reset.cc
src/compiler-rt/test/asan/TestCases/halt_on_error-1.c [new file with mode: 0644]
src/compiler-rt/test/asan/TestCases/speculative_load2.cc [new file with mode: 0644]
src/compiler-rt/test/asan/TestCases/throw_catch.cc
src/compiler-rt/test/asan/lit.cfg
src/compiler-rt/test/builtins/Unit/divtc3_test.c
src/compiler-rt/test/cfi/CMakeLists.txt
src/compiler-rt/test/cfi/base-derived-destructor.cpp
src/compiler-rt/test/cfi/cross-dso/icall/icall-from-dso.cpp [new file with mode: 0644]
src/compiler-rt/test/cfi/cross-dso/icall/icall.cpp [new file with mode: 0644]
src/compiler-rt/test/cfi/cross-dso/icall/lit.local.cfg [new file with mode: 0644]
src/compiler-rt/test/cfi/cross-dso/lit.local.cfg [new file with mode: 0644]
src/compiler-rt/test/cfi/cross-dso/simple-fail.cpp [new file with mode: 0644]
src/compiler-rt/test/cfi/cross-dso/simple-pass.cpp [new file with mode: 0644]
src/compiler-rt/test/cfi/lit.cfg
src/compiler-rt/test/lsan/TestCases/cleanup_in_tsd_destructor.c [new file with mode: 0644]
src/compiler-rt/test/lsan/TestCases/cleanup_in_tsd_destructor.cc [deleted file]
src/compiler-rt/test/lsan/TestCases/disabler.c [new file with mode: 0644]
src/compiler-rt/test/lsan/TestCases/disabler.cc
src/compiler-rt/test/lsan/TestCases/disabler_in_tsd_destructor.c [new file with mode: 0644]
src/compiler-rt/test/lsan/TestCases/disabler_in_tsd_destructor.cc [deleted file]
src/compiler-rt/test/lsan/TestCases/ignore_object.c [new file with mode: 0644]
src/compiler-rt/test/lsan/TestCases/ignore_object.cc [deleted file]
src/compiler-rt/test/msan/ctermid.cc [new file with mode: 0644]
src/compiler-rt/test/msan/dlopen_executable.cc [new file with mode: 0644]
src/compiler-rt/test/msan/fork.cc
src/compiler-rt/test/msan/insertvalue_origin.cc
src/compiler-rt/test/msan/signal_stress_test.cc
src/compiler-rt/test/profile/Inputs/instrprof-shared-lib.c [new file with mode: 0644]
src/compiler-rt/test/profile/Inputs/instrprof-shared-main.c [new file with mode: 0644]
src/compiler-rt/test/profile/Linux/instrprof-basic.c [new file with mode: 0644]
src/compiler-rt/test/profile/Linux/instrprof-dlopen.test [new file with mode: 0644]
src/compiler-rt/test/profile/Linux/instrprof-dynamic-one-shared.test [new file with mode: 0644]
src/compiler-rt/test/profile/Linux/instrprof-dynamic-two-shared.test [new file with mode: 0644]
src/compiler-rt/test/profile/Linux/lit.local.cfg [new file with mode: 0644]
src/compiler-rt/test/profile/instrprof-bufferio.c [new file with mode: 0644]
src/compiler-rt/test/profile/instrprof-error.c [new file with mode: 0644]
src/compiler-rt/test/profile/instrprof-shared.test [new file with mode: 0644]
src/compiler-rt/test/profile/instrprof-value-prof-2.c [new file with mode: 0644]
src/compiler-rt/test/profile/instrprof-value-prof.c [new file with mode: 0644]
src/compiler-rt/test/profile/instrprof-version-mismatch.c [new file with mode: 0644]
src/compiler-rt/test/profile/instrprof-without-libc.c
src/compiler-rt/test/safestack/lit.cfg
src/compiler-rt/test/safestack/overflow.c
src/compiler-rt/test/sanitizer_common/TestCases/Linux/closedir.c [new file with mode: 0644]
src/compiler-rt/test/sanitizer_common/TestCases/Linux/ill.cc [new file with mode: 0644]
src/compiler-rt/test/sanitizer_common/TestCases/fopen_nullptr.c [new file with mode: 0644]
src/compiler-rt/test/tsan/CMakeLists.txt
src/compiler-rt/test/tsan/Darwin/gcd-async-norace.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/gcd-async-race.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/gcd-groups-norace.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/gcd-groups-stress.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/gcd-once.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/gcd-semaphore-norace.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/gcd-serial-queue-norace.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/gcd-sync-norace.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/gcd-sync-race.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/lit.local.cfg [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/objc-race.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/objc-simple.mm [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/osspinlock-norace.cc [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/symbolizer-atos.cc [new file with mode: 0644]
src/compiler-rt/test/tsan/Darwin/symbolizer-dladdr.cc [new file with mode: 0644]
src/compiler-rt/test/tsan/Linux/check_memcpy.cc [new file with mode: 0644]
src/compiler-rt/test/tsan/allocator_returns_null.cc
src/compiler-rt/test/tsan/barrier.cc
src/compiler-rt/test/tsan/bench_acquire_only.cc
src/compiler-rt/test/tsan/bench_acquire_release.cc
src/compiler-rt/test/tsan/bench_local_mutex.cc
src/compiler-rt/test/tsan/bench_mutex.cc
src/compiler-rt/test/tsan/bench_release_only.cc
src/compiler-rt/test/tsan/bench_rwmutex.cc
src/compiler-rt/test/tsan/bench_single_writer.cc
src/compiler-rt/test/tsan/bench_ten_mutexes.cc
src/compiler-rt/test/tsan/cond_cancel.c
src/compiler-rt/test/tsan/cond_version.c
src/compiler-rt/test/tsan/deadlock_detector_stress_test.cc
src/compiler-rt/test/tsan/dl_iterate_phdr.cc
src/compiler-rt/test/tsan/dlclose.cc
src/compiler-rt/test/tsan/fd_tid_recycled.cc [new file with mode: 0644]
src/compiler-rt/test/tsan/fork_atexit.cc
src/compiler-rt/test/tsan/fork_deadlock.cc
src/compiler-rt/test/tsan/fork_multithreaded.cc
src/compiler-rt/test/tsan/fork_multithreaded3.cc
src/compiler-rt/test/tsan/free_race.c
src/compiler-rt/test/tsan/getline_nohang.cc
src/compiler-rt/test/tsan/global_race.cc
src/compiler-rt/test/tsan/global_race2.cc
src/compiler-rt/test/tsan/global_race3.cc
src/compiler-rt/test/tsan/halt_on_error.cc
src/compiler-rt/test/tsan/ignore_lib0.cc
src/compiler-rt/test/tsan/ignore_lib1.cc
src/compiler-rt/test/tsan/ignore_lib2.cc
src/compiler-rt/test/tsan/ignore_lib3.cc
src/compiler-rt/test/tsan/inlined_memcpy_race.cc
src/compiler-rt/test/tsan/java_race_pc.cc
src/compiler-rt/test/tsan/lit.cfg
src/compiler-rt/test/tsan/lit.site.cfg.in
src/compiler-rt/test/tsan/load_shared_lib.cc
src/compiler-rt/test/tsan/longjmp.cc
src/compiler-rt/test/tsan/longjmp2.cc
src/compiler-rt/test/tsan/longjmp3.cc
src/compiler-rt/test/tsan/longjmp4.cc
src/compiler-rt/test/tsan/malloc_overflow.cc
src/compiler-rt/test/tsan/map32bit.cc
src/compiler-rt/test/tsan/memcmp_race.cc
src/compiler-rt/test/tsan/memcpy_race.cc
src/compiler-rt/test/tsan/mmap_large.cc
src/compiler-rt/test/tsan/mmap_stress.cc
src/compiler-rt/test/tsan/mop_with_offset.cc
src/compiler-rt/test/tsan/mop_with_offset2.cc
src/compiler-rt/test/tsan/mutex_cycle2.c
src/compiler-rt/test/tsan/mutexset1.cc
src/compiler-rt/test/tsan/mutexset2.cc
src/compiler-rt/test/tsan/mutexset3.cc
src/compiler-rt/test/tsan/mutexset4.cc
src/compiler-rt/test/tsan/mutexset5.cc
src/compiler-rt/test/tsan/mutexset6.cc
src/compiler-rt/test/tsan/mutexset8.cc
src/compiler-rt/test/tsan/pie_test.cc [new file with mode: 0644]
src/compiler-rt/test/tsan/printf-1.c
src/compiler-rt/test/tsan/pthread_atfork_deadlock.c
src/compiler-rt/test/tsan/race_on_barrier.c
src/compiler-rt/test/tsan/race_on_barrier2.c
src/compiler-rt/test/tsan/race_on_heap.cc
src/compiler-rt/test/tsan/race_on_mutex.c
src/compiler-rt/test/tsan/race_on_speculative_load.cc
src/compiler-rt/test/tsan/race_top_suppression.cc
src/compiler-rt/test/tsan/race_top_suppression1.cc
src/compiler-rt/test/tsan/real_deadlock_detector_stress_test.cc
src/compiler-rt/test/tsan/setuid2.c
src/compiler-rt/test/tsan/signal_cond.cc
src/compiler-rt/test/tsan/signal_errno.cc
src/compiler-rt/test/tsan/signal_longjmp.cc
src/compiler-rt/test/tsan/signal_recursive.cc
src/compiler-rt/test/tsan/signal_reset.cc
src/compiler-rt/test/tsan/signal_sync.cc
src/compiler-rt/test/tsan/signal_thread.cc
src/compiler-rt/test/tsan/stack_sync_reuse.cc
src/compiler-rt/test/tsan/suppressions_global.cc
src/compiler-rt/test/tsan/suppressions_race.cc
src/compiler-rt/test/tsan/suppressions_race2.cc
src/compiler-rt/test/tsan/test.h
src/compiler-rt/test/tsan/test_output.sh [deleted file]
src/compiler-rt/test/tsan/thread_name2.cc
src/compiler-rt/test/tsan/tls_race.cc
src/compiler-rt/test/tsan/tls_race2.cc
src/compiler-rt/test/tsan/vfork.cc
src/compiler-rt/test/tsan/virtual_inheritance_compile_bug.cc
src/compiler-rt/test/tsan/vptr_benign_race.cc
src/compiler-rt/test/ubsan/TestCases/Integer/summary.cpp
src/compiler-rt/test/ubsan/TestCases/Integer/suppressions.cpp [new file with mode: 0644]
src/compiler-rt/test/ubsan/TestCases/Misc/Linux/ubsan_options.cc
src/compiler-rt/test/ubsan/lit.common.cfg
src/doc/book/advanced-linking.md
src/doc/book/choosing-your-guarantees.md
src/doc/book/closures.md
src/doc/book/compiler-plugins.md
src/doc/book/crates-and-modules.md
src/doc/book/documentation.md
src/doc/book/error-handling.md
src/doc/book/functions.md
src/doc/book/glossary.md
src/doc/book/guessing-game.md
src/doc/book/lifetimes.md
src/doc/book/loops.md
src/doc/book/no-stdlib.md
src/doc/book/ownership.md
src/doc/book/patterns.md
src/doc/book/primitive-types.md
src/doc/book/references-and-borrowing.md
src/doc/book/slice-patterns.md
src/doc/book/strings.md
src/doc/book/testing.md
src/doc/book/traits.md
src/doc/book/using-rust-without-the-standard-library.md
src/doc/book/variable-bindings.md
src/doc/book/vectors.md
src/doc/footer.inc
src/doc/nomicon/README.md
src/doc/nomicon/safe-unsafe-meaning.md
src/doc/reference.md
src/doc/rust.css
src/doc/rustc-ux-guidelines.md
src/etc/Dockerfile [new file with mode: 0644]
src/etc/debugger_pretty_printers_common.py
src/etc/gdb_rust_pretty_printing.py
src/etc/get-stage0.py
src/etc/htmldocck.py
src/etc/rust-lldb
src/jemalloc/src/zone.c
src/liballoc/arc.rs
src/liballoc/boxed.rs
src/liballoc/boxed_test.rs
src/liballoc/heap.rs
src/liballoc/raw_vec.rs
src/liballoc/rc.rs
src/liballoc_jemalloc/build.rs
src/liballoc_jemalloc/lib.rs
src/libcollections/binary_heap.rs
src/libcollections/borrow.rs
src/libcollections/btree/map.rs
src/libcollections/btree/node.rs
src/libcollections/btree/set.rs
src/libcollections/fmt.rs
src/libcollections/lib.rs
src/libcollections/str.rs
src/libcollections/vec.rs
src/libcollectionstest/binary_heap.rs
src/libcollectionstest/btree/map.rs
src/libcollectionstest/btree/mod.rs
src/libcollectionstest/btree/set.rs
src/libcollectionstest/enum_set.rs
src/libcollectionstest/lib.rs
src/libcollectionstest/linked_list.rs
src/libcollectionstest/slice.rs
src/libcollectionstest/str.rs
src/libcollectionstest/string.rs
src/libcollectionstest/vec.rs
src/libcollectionstest/vec_deque.rs
src/libcore/Cargo.toml
src/libcore/any.rs
src/libcore/build.rs [deleted file]
src/libcore/cell.rs
src/libcore/char.rs
src/libcore/clone.rs
src/libcore/cmp.rs
src/libcore/default.rs
src/libcore/fmt/mod.rs
src/libcore/fmt/num.rs
src/libcore/hash/mod.rs
src/libcore/hash/sip.rs
src/libcore/intrinsics.rs
src/libcore/iter/iterator.rs
src/libcore/iter/mod.rs
src/libcore/iter/range.rs
src/libcore/iter/traits.rs
src/libcore/iter_private.rs [new file with mode: 0644]
src/libcore/lib.rs
src/libcore/macros.rs
src/libcore/marker.rs
src/libcore/mem.rs
src/libcore/num/dec2flt/algorithm.rs
src/libcore/num/dec2flt/mod.rs
src/libcore/num/dec2flt/rawfp.rs
src/libcore/num/f32.rs
src/libcore/num/f64.rs
src/libcore/num/flt2dec/decoder.rs
src/libcore/num/int_macros.rs
src/libcore/num/isize.rs
src/libcore/num/mod.rs
src/libcore/num/uint_macros.rs
src/libcore/num/usize.rs
src/libcore/num/wrapping.rs
src/libcore/ops.rs
src/libcore/raw.rs
src/libcore/result.rs
src/libcore/slice.rs
src/libcore/str/mod.rs
src/libcore/str/pattern.rs
src/libcore/sync/atomic.rs
src/libcoretest/cell.rs
src/libcoretest/char.rs
src/libcoretest/hash/sip.rs
src/libcoretest/iter.rs
src/libcoretest/lib.rs
src/libcoretest/mem.rs
src/libcoretest/num/dec2flt/rawfp.rs
src/libcoretest/num/flt2dec/estimator.rs
src/libcoretest/num/flt2dec/mod.rs
src/libflate/lib.rs
src/liblibc/libc-test/build.rs
src/liblibc/src/unix/bsd/apple/mod.rs
src/liblibc/src/unix/bsd/freebsdlike/mod.rs
src/liblibc/src/unix/bsd/openbsdlike/bitrig.rs
src/liblibc/src/unix/bsd/openbsdlike/mod.rs
src/liblibc/src/unix/bsd/openbsdlike/netbsd.rs
src/liblibc/src/unix/bsd/openbsdlike/openbsd.rs
src/liblibc/src/unix/mod.rs
src/liblibc/src/unix/notbsd/android/b32.rs
src/liblibc/src/unix/notbsd/android/b64.rs
src/liblibc/src/unix/notbsd/android/mod.rs
src/liblibc/src/unix/notbsd/linux/mips.rs
src/liblibc/src/unix/notbsd/linux/mod.rs
src/liblibc/src/unix/notbsd/linux/musl/b32/arm.rs
src/liblibc/src/unix/notbsd/linux/musl/b32/asmjs.rs
src/liblibc/src/unix/notbsd/linux/musl/b32/mips.rs
src/liblibc/src/unix/notbsd/linux/musl/b32/x86.rs
src/liblibc/src/unix/notbsd/linux/musl/b64/mod.rs
src/liblibc/src/unix/notbsd/linux/musl/mod.rs
src/liblibc/src/unix/notbsd/linux/other/b32/mod.rs
src/liblibc/src/unix/notbsd/linux/other/b64/mod.rs
src/liblibc/src/unix/notbsd/linux/other/mod.rs
src/liblibc/src/unix/notbsd/mod.rs
src/liblibc/src/unix/solaris/mod.rs
src/liblog/lib.rs
src/libpanic_abort/lib.rs
src/libpanic_unwind/dwarf/eh.rs
src/libpanic_unwind/dwarf/mod.rs
src/libpanic_unwind/gcc.rs
src/libpanic_unwind/lib.rs
src/libpanic_unwind/seh.rs
src/libpanic_unwind/seh64_gnu.rs
src/libpanic_unwind/windows.rs
src/librand/chacha.rs
src/librand/distributions/exponential.rs
src/librand/distributions/gamma.rs
src/librand/distributions/mod.rs
src/librand/distributions/normal.rs
src/librand/distributions/range.rs
src/librand/isaac.rs
src/librand/lib.rs
src/librand/rand_impls.rs
src/librand/reseeding.rs
src/librustc/Cargo.toml
src/librustc/cfg/construct.rs
src/librustc/dep_graph/dep_node.rs
src/librustc/dep_graph/query.rs
src/librustc/dep_graph/raii.rs
src/librustc/dep_graph/thread.rs
src/librustc/dep_graph/visit.rs
src/librustc/diagnostics.rs
src/librustc/hir/check_attr.rs
src/librustc/hir/def.rs
src/librustc/hir/fold.rs
src/librustc/hir/intravisit.rs
src/librustc/hir/lowering.rs
src/librustc/hir/map/blocks.rs
src/librustc/hir/map/collector.rs
src/librustc/hir/map/def_collector.rs
src/librustc/hir/map/definitions.rs
src/librustc/hir/map/mod.rs
src/librustc/hir/mod.rs
src/librustc/hir/pat_util.rs
src/librustc/hir/print.rs
src/librustc/infer/combine.rs
src/librustc/infer/error_reporting.rs
src/librustc/infer/freshen.rs
src/librustc/infer/higher_ranked/mod.rs
src/librustc/infer/mod.rs
src/librustc/infer/region_inference/graphviz.rs
src/librustc/infer/region_inference/mod.rs
src/librustc/infer/type_variable.rs
src/librustc/lib.rs
src/librustc/lint/builtin.rs
src/librustc/lint/context.rs
src/librustc/lint/mod.rs
src/librustc/middle/astconv_util.rs
src/librustc/middle/const_val.rs
src/librustc/middle/cstore.rs
src/librustc/middle/dataflow.rs
src/librustc/middle/dead.rs
src/librustc/middle/dependency_format.rs
src/librustc/middle/effect.rs
src/librustc/middle/entry.rs
src/librustc/middle/expr_use_visitor.rs
src/librustc/middle/intrinsicck.rs
src/librustc/middle/lang_items.rs
src/librustc/middle/liveness.rs
src/librustc/middle/mem_categorization.rs
src/librustc/middle/reachable.rs
src/librustc/middle/region.rs
src/librustc/middle/resolve_lifetime.rs
src/librustc/middle/stability.rs
src/librustc/middle/weak_lang_items.rs
src/librustc/mir/cache.rs [new file with mode: 0644]
src/librustc/mir/repr.rs
src/librustc/mir/tcx.rs
src/librustc/mir/transform.rs
src/librustc/mir/traversal.rs [new file with mode: 0644]
src/librustc/mir/visit.rs
src/librustc/session/config.rs
src/librustc/session/mod.rs
src/librustc/traits/coherence.rs
src/librustc/traits/error_reporting.rs
src/librustc/traits/fulfill.rs
src/librustc/traits/mod.rs
src/librustc/traits/project.rs
src/librustc/traits/select.rs
src/librustc/traits/specialize/mod.rs
src/librustc/traits/util.rs
src/librustc/ty/adjustment.rs
src/librustc/ty/contents.rs
src/librustc/ty/context.rs
src/librustc/ty/error.rs
src/librustc/ty/flags.rs
src/librustc/ty/fold.rs
src/librustc/ty/item_path.rs
src/librustc/ty/layout.rs
src/librustc/ty/mod.rs
src/librustc/ty/structural_impls.rs
src/librustc/ty/sty.rs
src/librustc/ty/subst.rs
src/librustc/ty/util.rs
src/librustc/ty/wf.rs
src/librustc/util/common.rs
src/librustc/util/ppaux.rs
src/librustc_bitflags/lib.rs
src/librustc_borrowck/Cargo.toml
src/librustc_borrowck/bitslice.rs
src/librustc_borrowck/borrowck/check_loans.rs
src/librustc_borrowck/borrowck/fragments.rs
src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
src/librustc_borrowck/borrowck/gather_loans/lifetime.rs
src/librustc_borrowck/borrowck/gather_loans/mod.rs
src/librustc_borrowck/borrowck/gather_loans/move_error.rs
src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
src/librustc_borrowck/borrowck/mir/abs_domain.rs
src/librustc_borrowck/borrowck/mir/dataflow.rs [deleted file]
src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/mir/dataflow/impls.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/mir/dataflow/mod.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/mir/elaborate_drops.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/mir/gather_moves.rs
src/librustc_borrowck/borrowck/mir/graphviz.rs [deleted file]
src/librustc_borrowck/borrowck/mir/mod.rs
src/librustc_borrowck/borrowck/mir/patch.rs [new file with mode: 0644]
src/librustc_borrowck/borrowck/mod.rs
src/librustc_borrowck/borrowck/move_data.rs
src/librustc_borrowck/diagnostics.rs
src/librustc_borrowck/indexed_set.rs [new file with mode: 0644]
src/librustc_borrowck/lib.rs
src/librustc_const_eval/Cargo.toml
src/librustc_const_eval/check_match.rs
src/librustc_const_eval/diagnostics.rs
src/librustc_const_eval/eval.rs
src/librustc_const_eval/lib.rs
src/librustc_const_math/err.rs
src/librustc_const_math/float.rs [new file with mode: 0644]
src/librustc_const_math/int.rs
src/librustc_const_math/is.rs
src/librustc_const_math/lib.rs
src/librustc_const_math/us.rs
src/librustc_data_structures/bitvec.rs
src/librustc_data_structures/control_flow_graph/dominators/mod.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/dominators/test.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/iterate/mod.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/iterate/test.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/mod.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/reachable/mod.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/reachable/test.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/reference.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/test.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/transpose.rs [new file with mode: 0644]
src/librustc_data_structures/indexed_vec.rs [new file with mode: 0644]
src/librustc_data_structures/lib.rs
src/librustc_data_structures/snapshot_map/mod.rs [new file with mode: 0644]
src/librustc_data_structures/snapshot_map/test.rs [new file with mode: 0644]
src/librustc_data_structures/tuple_slice.rs
src/librustc_driver/Cargo.toml
src/librustc_driver/driver.rs
src/librustc_driver/lib.rs
src/librustc_driver/pretty.rs
src/librustc_driver/test.rs
src/librustc_errors/Cargo.toml [new file with mode: 0644]
src/librustc_errors/emitter.rs [new file with mode: 0644]
src/librustc_errors/lib.rs [new file with mode: 0644]
src/librustc_errors/registry.rs [new file with mode: 0644]
src/librustc_errors/snippet.rs [new file with mode: 0644]
src/librustc_incremental/Cargo.toml
src/librustc_incremental/assert_dep_graph.rs
src/librustc_incremental/calculate_svh.rs
src/librustc_incremental/lib.rs
src/librustc_incremental/persist/directory.rs
src/librustc_incremental/persist/hash.rs
src/librustc_incremental/persist/load.rs
src/librustc_incremental/persist/save.rs
src/librustc_lint/Cargo.toml
src/librustc_lint/bad_style.rs
src/librustc_lint/builtin.rs
src/librustc_lint/lib.rs
src/librustc_lint/types.rs
src/librustc_lint/unused.rs
src/librustc_llvm/archive_ro.rs
src/librustc_llvm/build.rs
src/librustc_llvm/diagnostic.rs
src/librustc_llvm/lib.rs
src/librustc_metadata/Cargo.toml
src/librustc_metadata/astencode.rs
src/librustc_metadata/common.rs
src/librustc_metadata/creader.rs
src/librustc_metadata/csearch.rs
src/librustc_metadata/cstore.rs
src/librustc_metadata/decoder.rs
src/librustc_metadata/encoder.rs
src/librustc_metadata/lib.rs
src/librustc_metadata/loader.rs
src/librustc_metadata/macro_import.rs
src/librustc_metadata/tydecode.rs
src/librustc_metadata/tyencode.rs
src/librustc_mir/Cargo.toml
src/librustc_mir/build/block.rs
src/librustc_mir/build/cfg.rs
src/librustc_mir/build/expr/as_lvalue.rs
src/librustc_mir/build/expr/as_operand.rs
src/librustc_mir/build/expr/as_rvalue.rs
src/librustc_mir/build/expr/as_temp.rs
src/librustc_mir/build/expr/into.rs
src/librustc_mir/build/expr/stmt.rs
src/librustc_mir/build/matches/mod.rs
src/librustc_mir/build/matches/simplify.rs
src/librustc_mir/build/matches/test.rs
src/librustc_mir/build/matches/util.rs
src/librustc_mir/build/misc.rs
src/librustc_mir/build/mod.rs
src/librustc_mir/build/scope.rs
src/librustc_mir/graphviz.rs
src/librustc_mir/hair/cx/expr.rs
src/librustc_mir/hair/cx/mod.rs
src/librustc_mir/hair/cx/pattern.rs
src/librustc_mir/hair/mod.rs
src/librustc_mir/lib.rs
src/librustc_mir/mir_map.rs
src/librustc_mir/pretty.rs
src/librustc_mir/transform/add_call_guards.rs [new file with mode: 0644]
src/librustc_mir/transform/break_cleanup_edges.rs [deleted file]
src/librustc_mir/transform/dump_mir.rs [new file with mode: 0644]
src/librustc_mir/transform/mod.rs
src/librustc_mir/transform/no_landing_pads.rs
src/librustc_mir/transform/promote_consts.rs
src/librustc_mir/transform/qualify_consts.rs
src/librustc_mir/transform/remove_dead_blocks.rs [deleted file]
src/librustc_mir/transform/simplify_branches.rs [new file with mode: 0644]
src/librustc_mir/transform/simplify_cfg.rs
src/librustc_mir/transform/type_check.rs
src/librustc_mir/traversal.rs [deleted file]
src/librustc_passes/Cargo.toml
src/librustc_passes/ast_validation.rs [new file with mode: 0644]
src/librustc_passes/consts.rs
src/librustc_passes/diagnostics.rs
src/librustc_passes/lib.rs
src/librustc_passes/loops.rs
src/librustc_passes/no_asm.rs
src/librustc_passes/rvalues.rs
src/librustc_passes/static_recursion.rs
src/librustc_plugin/Cargo.toml
src/librustc_plugin/build.rs
src/librustc_plugin/lib.rs
src/librustc_plugin/load.rs
src/librustc_plugin/registry.rs
src/librustc_privacy/Cargo.toml
src/librustc_privacy/diagnostics.rs
src/librustc_privacy/lib.rs
src/librustc_resolve/Cargo.toml
src/librustc_resolve/build_reduced_graph.rs
src/librustc_resolve/check_unused.rs
src/librustc_resolve/diagnostics.rs
src/librustc_resolve/lib.rs
src/librustc_resolve/resolve_imports.rs
src/librustc_save_analysis/Cargo.toml
src/librustc_save_analysis/data.rs
src/librustc_save_analysis/dump_visitor.rs
src/librustc_save_analysis/external_data.rs
src/librustc_save_analysis/json_dumper.rs
src/librustc_save_analysis/lib.rs
src/librustc_save_analysis/span_utils.rs
src/librustc_trans/Cargo.toml
src/librustc_trans/_match.rs
src/librustc_trans/adt.rs
src/librustc_trans/attributes.rs
src/librustc_trans/back/archive.rs
src/librustc_trans/back/link.rs
src/librustc_trans/back/linker.rs
src/librustc_trans/back/msvc/arch.rs [new file with mode: 0644]
src/librustc_trans/back/msvc/mod.rs
src/librustc_trans/back/symbol_names.rs
src/librustc_trans/back/write.rs
src/librustc_trans/base.rs
src/librustc_trans/build.rs
src/librustc_trans/builder.rs
src/librustc_trans/callee.rs
src/librustc_trans/closure.rs
src/librustc_trans/collector.rs
src/librustc_trans/common.rs
src/librustc_trans/consts.rs
src/librustc_trans/context.rs
src/librustc_trans/controlflow.rs
src/librustc_trans/datum.rs
src/librustc_trans/debuginfo/create_scope_map.rs
src/librustc_trans/debuginfo/metadata.rs
src/librustc_trans/debuginfo/mod.rs
src/librustc_trans/debuginfo/namespace.rs
src/librustc_trans/debuginfo/source_loc.rs
src/librustc_trans/debuginfo/utils.rs
src/librustc_trans/diagnostics.rs
src/librustc_trans/expr.rs
src/librustc_trans/glue.rs
src/librustc_trans/intrinsic.rs
src/librustc_trans/lib.rs
src/librustc_trans/meth.rs
src/librustc_trans/mir/analyze.rs
src/librustc_trans/mir/block.rs
src/librustc_trans/mir/constant.rs
src/librustc_trans/mir/drop.rs [deleted file]
src/librustc_trans/mir/lvalue.rs
src/librustc_trans/mir/mod.rs
src/librustc_trans/mir/operand.rs
src/librustc_trans/mir/rvalue.rs
src/librustc_trans/mir/statement.rs
src/librustc_trans/monomorphize.rs
src/librustc_trans/partitioning.rs
src/librustc_trans/symbol_names_test.rs
src/librustc_trans/type_.rs
src/librustc_typeck/Cargo.toml
src/librustc_typeck/astconv.rs
src/librustc_typeck/check/_match.rs
src/librustc_typeck/check/assoc.rs
src/librustc_typeck/check/autoderef.rs [new file with mode: 0644]
src/librustc_typeck/check/callee.rs
src/librustc_typeck/check/cast.rs
src/librustc_typeck/check/coercion.rs
src/librustc_typeck/check/compare_method.rs
src/librustc_typeck/check/demand.rs
src/librustc_typeck/check/dropck.rs
src/librustc_typeck/check/intrinsic.rs
src/librustc_typeck/check/method/confirm.rs
src/librustc_typeck/check/method/mod.rs
src/librustc_typeck/check/method/probe.rs
src/librustc_typeck/check/method/suggest.rs
src/librustc_typeck/check/mod.rs
src/librustc_typeck/check/regionck.rs
src/librustc_typeck/check/upvar.rs
src/librustc_typeck/check/wfcheck.rs
src/librustc_typeck/check/writeback.rs
src/librustc_typeck/check_unused.rs
src/librustc_typeck/coherence/mod.rs
src/librustc_typeck/coherence/orphan.rs
src/librustc_typeck/collect.rs
src/librustc_typeck/diagnostics.rs
src/librustc_typeck/lib.rs
src/librustc_typeck/rscope.rs
src/librustc_typeck/variance/constraints.rs
src/librustc_unicode/char.rs
src/librustc_unicode/lib.rs
src/librustc_unicode/u_str.rs
src/librustdoc/Cargo.toml
src/librustdoc/clean/inline.rs
src/librustdoc/clean/mod.rs
src/librustdoc/core.rs
src/librustdoc/doctree.rs
src/librustdoc/html/format.rs
src/librustdoc/html/highlight.rs
src/librustdoc/html/markdown.rs
src/librustdoc/html/render.rs
src/librustdoc/html/static/main.js
src/librustdoc/html/static/rustdoc.css
src/librustdoc/html/static/styles/main.css
src/librustdoc/lib.rs
src/librustdoc/passes.rs
src/librustdoc/test.rs
src/librustdoc/visit_ast.rs
src/libserialize/json.rs
src/libstd/build.rs
src/libstd/collections/hash/map.rs
src/libstd/collections/hash/table.rs
src/libstd/collections/mod.rs
src/libstd/error.rs
src/libstd/ffi/c_str.rs
src/libstd/fs.rs
src/libstd/io/cursor.rs
src/libstd/io/util.rs
src/libstd/lib.rs
src/libstd/macros.rs
src/libstd/net/addr.rs
src/libstd/net/ip.rs
src/libstd/net/mod.rs
src/libstd/num/f32.rs
src/libstd/num/f64.rs
src/libstd/num/mod.rs
src/libstd/os/android/raw.rs
src/libstd/os/bitrig/raw.rs
src/libstd/os/dragonfly/raw.rs
src/libstd/os/emscripten/raw.rs
src/libstd/os/freebsd/raw.rs
src/libstd/os/ios/raw.rs
src/libstd/os/linux/raw.rs
src/libstd/os/macos/raw.rs
src/libstd/os/nacl/raw.rs
src/libstd/os/netbsd/raw.rs
src/libstd/os/openbsd/raw.rs
src/libstd/os/solaris/raw.rs
src/libstd/panic.rs
src/libstd/path.rs
src/libstd/primitive_docs.rs
src/libstd/process.rs
src/libstd/rt.rs
src/libstd/sync/mpsc/blocking.rs
src/libstd/sync/mpsc/mod.rs
src/libstd/sync/mpsc/oneshot.rs
src/libstd/sync/mpsc/shared.rs
src/libstd/sync/mpsc/spsc_queue.rs
src/libstd/sync/mpsc/stream.rs
src/libstd/sync/mpsc/sync.rs
src/libstd/sync/mutex.rs
src/libstd/sync/rwlock.rs
src/libstd/sys/common/mutex.rs
src/libstd/sys/common/net.rs
src/libstd/sys/common/util.rs
src/libstd/sys/common/wtf8.rs
src/libstd/sys/unix/ext/process.rs
src/libstd/sys/unix/ext/raw.rs
src/libstd/sys/unix/fd.rs
src/libstd/sys/unix/fs.rs
src/libstd/sys/unix/mod.rs
src/libstd/sys/unix/mutex.rs
src/libstd/sys/unix/net.rs
src/libstd/sys/unix/pipe.rs
src/libstd/sys/unix/process.rs
src/libstd/sys/unix/rwlock.rs
src/libstd/sys/unix/thread.rs
src/libstd/sys/windows/c.rs
src/libstd/sys/windows/fs.rs
src/libstd/sys/windows/mod.rs
src/libstd/sys/windows/mutex.rs
src/libstd/sys/windows/net.rs
src/libstd/thread/local.rs
src/libstd/thread/mod.rs
src/libstd/thread/scoped_tls.rs [deleted file]
src/libstd/time/mod.rs
src/libsyntax/Cargo.toml
src/libsyntax/ast.rs
src/libsyntax/attr.rs
src/libsyntax/codemap.rs
src/libsyntax/config.rs
src/libsyntax/diagnostic_list.rs [new file with mode: 0644]
src/libsyntax/diagnostics/metadata.rs
src/libsyntax/diagnostics/plugin.rs
src/libsyntax/diagnostics/registry.rs [deleted file]
src/libsyntax/errors/emitter.rs [deleted file]
src/libsyntax/errors/json.rs [deleted file]
src/libsyntax/errors/mod.rs [deleted file]
src/libsyntax/errors/snippet/mod.rs [deleted file]
src/libsyntax/errors/snippet/test.rs [deleted file]
src/libsyntax/ext/base.rs
src/libsyntax/ext/build.rs
src/libsyntax/ext/expand.rs
src/libsyntax/ext/mtwt.rs
src/libsyntax/ext/quote.rs
src/libsyntax/ext/source_util.rs
src/libsyntax/ext/tt/macro_parser.rs
src/libsyntax/ext/tt/macro_rules.rs
src/libsyntax/ext/tt/transcribe.rs
src/libsyntax/feature_gate.rs
src/libsyntax/fold.rs
src/libsyntax/json.rs [new file with mode: 0644]
src/libsyntax/lib.rs
src/libsyntax/parse/attr.rs
src/libsyntax/parse/classify.rs
src/libsyntax/parse/lexer/comments.rs
src/libsyntax/parse/lexer/mod.rs
src/libsyntax/parse/lexer/unicode_chars.rs
src/libsyntax/parse/mod.rs
src/libsyntax/parse/obsolete.rs
src/libsyntax/parse/parser.rs
src/libsyntax/parse/token.rs
src/libsyntax/print/pp.rs
src/libsyntax/print/pprust.rs
src/libsyntax/show_span.rs
src/libsyntax/std_inject.rs
src/libsyntax/test.rs
src/libsyntax/tokenstream.rs [new file with mode: 0644]
src/libsyntax/util/interner.rs
src/libsyntax/util/node_count.rs
src/libsyntax/util/parser_testing.rs
src/libsyntax/util/small_vector.rs
src/libsyntax/util/thin_vec.rs [new file with mode: 0644]
src/libsyntax/visit.rs
src/libsyntax_ext/Cargo.toml
src/libsyntax_ext/asm.rs
src/libsyntax_ext/cfg.rs
src/libsyntax_ext/concat.rs
src/libsyntax_ext/concat_idents.rs
src/libsyntax_ext/deriving/bounds.rs
src/libsyntax_ext/deriving/clone.rs
src/libsyntax_ext/deriving/cmp/eq.rs
src/libsyntax_ext/deriving/cmp/ord.rs
src/libsyntax_ext/deriving/cmp/partial_eq.rs
src/libsyntax_ext/deriving/cmp/partial_ord.rs
src/libsyntax_ext/deriving/debug.rs
src/libsyntax_ext/deriving/decodable.rs
src/libsyntax_ext/deriving/default.rs
src/libsyntax_ext/deriving/encodable.rs
src/libsyntax_ext/deriving/generic/mod.rs
src/libsyntax_ext/deriving/generic/ty.rs
src/libsyntax_ext/deriving/hash.rs
src/libsyntax_ext/deriving/mod.rs
src/libsyntax_ext/env.rs
src/libsyntax_ext/format.rs
src/libsyntax_ext/lib.rs
src/libsyntax_ext/log_syntax.rs
src/libsyntax_ext/trace_macros.rs
src/libsyntax_pos/Cargo.toml [new file with mode: 0644]
src/libsyntax_pos/lib.rs [new file with mode: 0644]
src/libtest/lib.rs
src/libtest/stats.rs
src/libunwind/lib.rs
src/libunwind/libunwind.rs
src/rt/hoedown/src/document.c
src/rt/hoedown/src/html_blocks.c [deleted file]
src/rt/miniz.c
src/rtstartup/rsbegin.rs
src/rtstartup/rsend.rs
src/rust-installer/test/rust-installer-v1/README.md [new file with mode: 0644]
src/rust-installer/test/rust-installer-v1/gen-install-script.sh [new file with mode: 0755]
src/rust-installer/test/rust-installer-v1/gen-installer.sh [new file with mode: 0755]
src/rust-installer/test/rust-installer-v1/install-template.sh [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/.gitmodules [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/.travis.yml [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/README.md [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/combine-installers.sh [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/gen-install-script.sh [new file with mode: 0755]
src/rust-installer/test/rust-installer-v2/gen-installer.sh [new file with mode: 0755]
src/rust-installer/test/rust-installer-v2/install-template.sh [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/rust-installer-version [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test.sh [new file with mode: 0755]
src/rust-installer/test/rust-installer-v2/test/image1/bin/bad-bin [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/image1/bin/program [new file with mode: 0755]
src/rust-installer/test/rust-installer-v2/test/image1/bin/program2 [new file with mode: 0755]
src/rust-installer/test/rust-installer-v2/test/image1/dir-to-install/foo [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/image1/dir-to-not-install/foo [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/image1/something-to-install [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/image1/something-to-not-install [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/image2/bin/oldprogram [new file with mode: 0755]
src/rust-installer/test/rust-installer-v2/test/image2/dir-to-install/bar [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/image2/something-to-install [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/image3/bin/cargo [new file with mode: 0755]
src/rust-installer/test/rust-installer-v2/test/image4/baz [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/image4/dir-to-install/qux/bar [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/image5/dir-to-install/foo [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/README.md [new file with mode: 0644]
src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/gen-install-script.sh [new file with mode: 0755]
src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/gen-installer.sh [new file with mode: 0755]
src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/install-template.sh [new file with mode: 0644]
src/rustc/Cargo.lock
src/rustllvm/ArchiveWrapper.cpp
src/rustllvm/ExecutionEngineWrapper.cpp
src/rustllvm/PassWrapper.cpp
src/rustllvm/RustWrapper.cpp
src/stage0.txt
src/test/codegen-units/item-collection/cross-crate-trait-method.rs
src/test/codegen-units/item-collection/generic-functions.rs
src/test/codegen-units/item-collection/generic-impl.rs
src/test/codegen-units/item-collection/instantiation-through-vtable.rs
src/test/codegen-units/item-collection/unsizing.rs
src/test/codegen-units/partitioning/local-generic.rs
src/test/codegen/issue-32031.rs [new file with mode: 0644]
src/test/codegen/issue-32364.rs [new file with mode: 0644]
src/test/codegen/loads.rs
src/test/codegen/naked-functions.rs
src/test/codegen/zip.rs [new file with mode: 0644]
src/test/compile-fail-fulldeps/auxiliary/macro_crate_test.rs
src/test/compile-fail-fulldeps/gated-quote.rs
src/test/compile-fail-fulldeps/issue-18986.rs
src/test/compile-fail-fulldeps/qquote.rs
src/test/compile-fail/E0024.rs [deleted file]
src/test/compile-fail/E0055.rs
src/test/compile-fail/E0062.rs [new file with mode: 0644]
src/test/compile-fail/E0063.rs [new file with mode: 0644]
src/test/compile-fail/E0067.rs [new file with mode: 0644]
src/test/compile-fail/E0069.rs [new file with mode: 0644]
src/test/compile-fail/E0070.rs [new file with mode: 0644]
src/test/compile-fail/E0071.rs [new file with mode: 0644]
src/test/compile-fail/E0072.rs [new file with mode: 0644]
src/test/compile-fail/E0075.rs [new file with mode: 0644]
src/test/compile-fail/E0076.rs [new file with mode: 0644]
src/test/compile-fail/E0077.rs [new file with mode: 0644]
src/test/compile-fail/E0079.rs [new file with mode: 0644]
src/test/compile-fail/E0080.rs [new file with mode: 0644]
src/test/compile-fail/E0081.rs [new file with mode: 0644]
src/test/compile-fail/E0084.rs [new file with mode: 0644]
src/test/compile-fail/E0087.rs [new file with mode: 0644]
src/test/compile-fail/E0088.rs [new file with mode: 0644]
src/test/compile-fail/E0089.rs [new file with mode: 0644]
src/test/compile-fail/E0091.rs [new file with mode: 0644]
src/test/compile-fail/E0092.rs [new file with mode: 0644]
src/test/compile-fail/E0093.rs [new file with mode: 0644]
src/test/compile-fail/E0094.rs [new file with mode: 0644]
src/test/compile-fail/E0101.rs [new file with mode: 0644]
src/test/compile-fail/E0102.rs [new file with mode: 0644]
src/test/compile-fail/E0106.rs [new file with mode: 0644]
src/test/compile-fail/E0107.rs [new file with mode: 0644]
src/test/compile-fail/E0109.rs [new file with mode: 0644]
src/test/compile-fail/E0110.rs [new file with mode: 0644]
src/test/compile-fail/E0116.rs [new file with mode: 0644]
src/test/compile-fail/E0117.rs [new file with mode: 0644]
src/test/compile-fail/E0118.rs [new file with mode: 0644]
src/test/compile-fail/E0119.rs [new file with mode: 0644]
src/test/compile-fail/E0120.rs [new file with mode: 0644]
src/test/compile-fail/E0121.rs [new file with mode: 0644]
src/test/compile-fail/E0124.rs [new file with mode: 0644]
src/test/compile-fail/E0128.rs [new file with mode: 0644]
src/test/compile-fail/E0130.rs [new file with mode: 0644]
src/test/compile-fail/E0131.rs [new file with mode: 0644]
src/test/compile-fail/E0132.rs [new file with mode: 0644]
src/test/compile-fail/E0133.rs [new file with mode: 0644]
src/test/compile-fail/E0137.rs [new file with mode: 0644]
src/test/compile-fail/E0138.rs [new file with mode: 0644]
src/test/compile-fail/E0152.rs [new file with mode: 0644]
src/test/compile-fail/E0161.rs [new file with mode: 0644]
src/test/compile-fail/E0162.rs [new file with mode: 0644]
src/test/compile-fail/E0163.rs [new file with mode: 0644]
src/test/compile-fail/E0164.rs [new file with mode: 0644]
src/test/compile-fail/E0165.rs [new file with mode: 0644]
src/test/compile-fail/E0166.rs [new file with mode: 0644]
src/test/compile-fail/E0172.rs [new file with mode: 0644]
src/test/compile-fail/E0178.rs [new file with mode: 0644]
src/test/compile-fail/E0184.rs [new file with mode: 0644]
src/test/compile-fail/E0185.rs [new file with mode: 0644]
src/test/compile-fail/E0186.rs [new file with mode: 0644]
src/test/compile-fail/E0191.rs [new file with mode: 0644]
src/test/compile-fail/E0192.rs [new file with mode: 0644]
src/test/compile-fail/E0194.rs [new file with mode: 0644]
src/test/compile-fail/E0195.rs [new file with mode: 0644]
src/test/compile-fail/E0197.rs [new file with mode: 0644]
src/test/compile-fail/E0199.rs [new file with mode: 0644]
src/test/compile-fail/E0200.rs [new file with mode: 0644]
src/test/compile-fail/array_const_index-0.rs
src/test/compile-fail/array_const_index-1.rs
src/test/compile-fail/asm-misplaced-option.rs
src/test/compile-fail/associated-const-private-impl.rs
src/test/compile-fail/associated-types/cache/chrono-scan.rs [new file with mode: 0644]
src/test/compile-fail/associated-types/cache/elision.rs [new file with mode: 0644]
src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs [new file with mode: 0644]
src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs [new file with mode: 0644]
src/test/compile-fail/associated-types/cache/wasm-issue-32330.rs [new file with mode: 0644]
src/test/compile-fail/auxiliary/recursive_reexports.rs [new file with mode: 0644]
src/test/compile-fail/bad-format-args.rs [new file with mode: 0644]
src/test/compile-fail/blind-item-block-middle.rs
src/test/compile-fail/borrowck/borrowck-borrow-overloaded-auto-deref-mut.rs
src/test/compile-fail/borrowck/borrowck-move-out-of-vec-tail.rs
src/test/compile-fail/borrowck/borrowck-vec-pattern-element-loan.rs
src/test/compile-fail/borrowck/borrowck-vec-pattern-loan-from-mut.rs
src/test/compile-fail/borrowck/borrowck-vec-pattern-move-tail.rs
src/test/compile-fail/borrowck/borrowck-vec-pattern-nesting.rs
src/test/compile-fail/borrowck/borrowck-vec-pattern-tail-element-loan.rs
src/test/compile-fail/cfg-non-opt-expr.rs
src/test/compile-fail/const-array-oob.rs
src/test/compile-fail/const-err-early.rs
src/test/compile-fail/const-err.rs
src/test/compile-fail/const-eval-overflow.rs
src/test/compile-fail/const-pattern-irrefutable.rs
src/test/compile-fail/const-slice-oob.rs
src/test/compile-fail/const-unsized.rs [new file with mode: 0644]
src/test/compile-fail/custom_attribute.rs
src/test/compile-fail/empty-struct-braces-pat-2.rs
src/test/compile-fail/enum-in-scope.rs
src/test/compile-fail/enums-pats-not-idents.rs
src/test/compile-fail/expanded-cfg.rs
src/test/compile-fail/hr-subtype.rs [new file with mode: 0644]
src/test/compile-fail/ifmt-bad-arg.rs
src/test/compile-fail/invalid-macro-matcher.rs
src/test/compile-fail/issue-10200.rs
src/test/compile-fail/issue-12369.rs
src/test/compile-fail/issue-12560-1.rs
src/test/compile-fail/issue-12567.rs
src/test/compile-fail/issue-12863.rs
src/test/compile-fail/issue-13482-2.rs
src/test/compile-fail/issue-13482.rs
src/test/compile-fail/issue-13727.rs [new file with mode: 0644]
src/test/compile-fail/issue-15381.rs
src/test/compile-fail/issue-16048.rs
src/test/compile-fail/issue-16149.rs
src/test/compile-fail/issue-16338.rs
src/test/compile-fail/issue-16401.rs
src/test/compile-fail/issue-17405.rs
src/test/compile-fail/issue-17718-const-privacy.rs
src/test/compile-fail/issue-17718-patterns.rs
src/test/compile-fail/issue-17933.rs
src/test/compile-fail/issue-18819.rs
src/test/compile-fail/issue-20831-debruijn.rs
src/test/compile-fail/issue-22434.rs [new file with mode: 0644]
src/test/compile-fail/issue-23122-1.rs [new file with mode: 0644]
src/test/compile-fail/issue-23122-2.rs [new file with mode: 0644]
src/test/compile-fail/issue-23281.rs [new file with mode: 0644]
src/test/compile-fail/issue-2356.rs
src/test/compile-fail/issue-23716.rs
src/test/compile-fail/issue-24424.rs [new file with mode: 0644]
src/test/compile-fail/issue-24446.rs
src/test/compile-fail/issue-24819.rs [new file with mode: 0644]
src/test/compile-fail/issue-25579.rs [new file with mode: 0644]
src/test/compile-fail/issue-26459.rs
src/test/compile-fail/issue-26480.rs [deleted file]
src/test/compile-fail/issue-26548.rs
src/test/compile-fail/issue-27033.rs
src/test/compile-fail/issue-27815.rs
src/test/compile-fail/issue-28992-empty.rs
src/test/compile-fail/issue-29161.rs
src/test/compile-fail/issue-30240.rs [new file with mode: 0644]
src/test/compile-fail/issue-3044.rs
src/test/compile-fail/issue-30715.rs [deleted file]
src/test/compile-fail/issue-32004.rs
src/test/compile-fail/issue-32086.rs [new file with mode: 0644]
src/test/compile-fail/issue-32829.rs [new file with mode: 0644]
src/test/compile-fail/issue-32950.rs [new file with mode: 0644]
src/test/compile-fail/issue-33293.rs [new file with mode: 0644]
src/test/compile-fail/issue-33571.rs [new file with mode: 0644]
src/test/compile-fail/issue-33819.rs [new file with mode: 0644]
src/test/compile-fail/issue-33876.rs [new file with mode: 0644]
src/test/compile-fail/issue-34028.rs [new file with mode: 0644]
src/test/compile-fail/issue-34047.rs [new file with mode: 0644]
src/test/compile-fail/issue-34171.rs [new file with mode: 0644]
src/test/compile-fail/issue-34194.rs [new file with mode: 0644]
src/test/compile-fail/issue-34209.rs [new file with mode: 0644]
src/test/compile-fail/issue-34334.rs [new file with mode: 0644]
src/test/compile-fail/issue-34349.rs [new file with mode: 0644]
src/test/compile-fail/issue-34418.rs [new file with mode: 0644]
src/test/compile-fail/issue-35044.rs [new file with mode: 0644]
src/test/compile-fail/issue-4935.rs
src/test/compile-fail/issue-5927.rs
src/test/compile-fail/label-static.rs [new file with mode: 0644]
src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-3.rs
src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs
src/test/compile-fail/lifetime-underscore.rs [new file with mode: 0644]
src/test/compile-fail/macro-backtrace-println.rs
src/test/compile-fail/macro-error.rs
src/test/compile-fail/macro-expanded-include/file.txt [new file with mode: 0644]
src/test/compile-fail/macro-expanded-include/foo/mod.rs [new file with mode: 0644]
src/test/compile-fail/macro-expanded-include/test.rs [new file with mode: 0644]
src/test/compile-fail/macro-follow.rs
src/test/compile-fail/macro-invalid-fragment-spec.rs [new file with mode: 0644]
src/test/compile-fail/macro-missing-delimiters.rs [new file with mode: 0644]
src/test/compile-fail/macro-use-scope.rs [new file with mode: 0644]
src/test/compile-fail/macro-with-seps-err-msg.rs
src/test/compile-fail/macros-nonfatal-errors.rs
src/test/compile-fail/malformed_macro_lhs.rs
src/test/compile-fail/match-pattern-field-mismatch-2.rs
src/test/compile-fail/match-vec-mismatch-2.rs
src/test/compile-fail/match-vec-mismatch.rs
src/test/compile-fail/match-vec-unreachable.rs
src/test/compile-fail/method-call-err-msg.rs
src/test/compile-fail/method-resolvable-path-in-pattern.rs
src/test/compile-fail/mir-dataflow/README.md [new file with mode: 0644]
src/test/compile-fail/mir-dataflow/def-inits-1.rs [new file with mode: 0644]
src/test/compile-fail/mir-dataflow/inits-1.rs [new file with mode: 0644]
src/test/compile-fail/mir-dataflow/uninits-1.rs [new file with mode: 0644]
src/test/compile-fail/mir-dataflow/uninits-2.rs [new file with mode: 0644]
src/test/compile-fail/name-clash-nullary.rs
src/test/compile-fail/nested-cfg-attrs.rs [new file with mode: 0644]
src/test/compile-fail/no-warn-on-field-replace-issue-34101.rs [new file with mode: 0644]
src/test/compile-fail/non-exhaustive-match-nested.rs
src/test/compile-fail/non-exhaustive-match.rs
src/test/compile-fail/non-exhaustive-pattern-witness.rs
src/test/compile-fail/non-interger-atomic.rs
src/test/compile-fail/not-enough-arguments.rs
src/test/compile-fail/not-panic-safe-2.rs
src/test/compile-fail/not-panic-safe-3.rs
src/test/compile-fail/not-panic-safe-4.rs
src/test/compile-fail/not-panic-safe-5.rs
src/test/compile-fail/not-panic-safe-6.rs
src/test/compile-fail/overloaded-calls-bad.rs
src/test/compile-fail/pat-shadow-in-nested-binding.rs
src/test/compile-fail/pat-slice-old-style.rs [new file with mode: 0644]
src/test/compile-fail/pat-tuple-bad-type.rs [new file with mode: 0644]
src/test/compile-fail/pat-tuple-feature-gate.rs [new file with mode: 0644]
src/test/compile-fail/pat-tuple-overfield.rs [new file with mode: 0644]
src/test/compile-fail/pattern-error-continue.rs
src/test/compile-fail/priv-in-bad-locations.rs
src/test/compile-fail/privacy/restricted/ty-params.rs
src/test/compile-fail/qualified-path-params.rs
src/test/compile-fail/range-1.rs
src/test/compile-fail/range_traits-1.rs [new file with mode: 0644]
src/test/compile-fail/range_traits-2.rs [new file with mode: 0644]
src/test/compile-fail/range_traits-3.rs [new file with mode: 0644]
src/test/compile-fail/range_traits-4.rs [new file with mode: 0644]
src/test/compile-fail/range_traits-5.rs [new file with mode: 0644]
src/test/compile-fail/range_traits-6.rs [new file with mode: 0644]
src/test/compile-fail/range_traits-7.rs [new file with mode: 0644]
src/test/compile-fail/recursive-reexports.rs [new file with mode: 0644]
src/test/compile-fail/regions-bound-missing-bound-in-impl.rs
src/test/compile-fail/regions-close-over-type-parameter-1.rs
src/test/compile-fail/regions-early-bound-error-method.rs
src/test/compile-fail/regions-early-bound-error.rs
src/test/compile-fail/regions-infer-invariance-due-to-mutability-4.rs
src/test/compile-fail/regions-trait-1.rs
src/test/compile-fail/stability-attribute-sanity.rs
src/test/compile-fail/stable-features.rs
src/test/compile-fail/static-mut-not-pat.rs
src/test/compile-fail/suggest-path-instead-of-mod-dot-item.rs
src/test/compile-fail/symbol-names/impl1.rs
src/test/compile-fail/syntax-extension-minor.rs
src/test/compile-fail/trace_macros-gate.rs
src/test/compile-fail/trait-impl-for-module.rs
src/test/compile-fail/unresolved_static_type_field.rs [new file with mode: 0644]
src/test/compile-fail/use-super-global-path.rs
src/test/compile-fail/useless-pub.rs
src/test/compile-fail/variadic-ffi-3.rs
src/test/debuginfo/function-arg-initialization.rs
src/test/debuginfo/function-prologue-stepping-no-stack-check.rs
src/test/debuginfo/pretty-huge-vec.rs [new file with mode: 0644]
src/test/debuginfo/pretty-uninitialized-vec.rs [new file with mode: 0644]
src/test/debuginfo/vec-slices.rs
src/test/incremental/struct_add_field.rs [new file with mode: 0644]
src/test/incremental/struct_change_field_name.rs [new file with mode: 0644]
src/test/incremental/struct_change_field_type.rs [new file with mode: 0644]
src/test/incremental/struct_change_field_type_cross_crate/auxiliary/a.rs [new file with mode: 0644]
src/test/incremental/struct_change_field_type_cross_crate/b.rs [new file with mode: 0644]
src/test/incremental/struct_change_nothing.rs [new file with mode: 0644]
src/test/incremental/struct_remove_field.rs [new file with mode: 0644]
src/test/incremental/type_alias_cross_crate/auxiliary/a.rs
src/test/incremental/type_alias_cross_crate/b.rs
src/test/parse-fail/issue-33455.rs [new file with mode: 0644]
src/test/parse-fail/issue-33569.rs [new file with mode: 0644]
src/test/parse-fail/pat-lt-bracket-6.rs
src/test/parse-fail/pat-lt-bracket-7.rs
src/test/parse-fail/pat-tuple-1.rs [new file with mode: 0644]
src/test/parse-fail/pat-tuple-2.rs [new file with mode: 0644]
src/test/parse-fail/pat-tuple-3.rs [new file with mode: 0644]
src/test/parse-fail/pat-tuple-4.rs [new file with mode: 0644]
src/test/parse-fail/pat-tuple-5.rs [new file with mode: 0644]
src/test/parse-fail/pat-tuple-6.rs [new file with mode: 0644]
src/test/parse-fail/trait-non-item-macros.rs [new file with mode: 0644]
src/test/pretty/attr-variant-data.rs [new file with mode: 0644]
src/test/pretty/lifetime.rs [new file with mode: 0644]
src/test/pretty/where-clauses.rs
src/test/run-fail-fulldeps/qquote.rs
src/test/run-fail/args-panic.rs
src/test/run-fail/assert-eq-macro-panic.rs
src/test/run-fail/binop-fail-3.rs
src/test/run-fail/binop-panic.rs
src/test/run-fail/bug-2470-bounds-check-overflow.rs
src/test/run-fail/bug-811.rs
src/test/run-fail/divide-by-zero.rs
src/test/run-fail/doublepanic.rs
src/test/run-fail/explicit-panic-msg.rs
src/test/run-fail/explicit-panic.rs
src/test/run-fail/expr-fn-panic.rs
src/test/run-fail/expr-if-panic-fn.rs
src/test/run-fail/expr-if-panic.rs
src/test/run-fail/expr-match-panic-fn.rs
src/test/run-fail/expr-match-panic.rs
src/test/run-fail/for-each-loop-panic.rs
src/test/run-fail/if-check-panic.rs
src/test/run-fail/if-cond-bot.rs
src/test/run-fail/issue-12920.rs
src/test/run-fail/issue-18576.rs
src/test/run-fail/issue-20971.rs
src/test/run-fail/issue-2444.rs
src/test/run-fail/issue-28934.rs
src/test/run-fail/issue-3029.rs
src/test/run-fail/issue-30380.rs [new file with mode: 0644]
src/test/run-fail/issue-6458-1.rs
src/test/run-fail/issue-948.rs
src/test/run-fail/main-panic.rs
src/test/run-fail/match-bot-panic.rs
src/test/run-fail/match-disc-bot.rs
src/test/run-fail/match-wildcards.rs
src/test/run-fail/meta-revision-bad.rs
src/test/run-fail/meta-revision-ok.rs
src/test/run-fail/mir_dynamic_drops_1.rs
src/test/run-fail/mir_dynamic_drops_2.rs
src/test/run-fail/mir_dynamic_drops_3.rs
src/test/run-fail/mod-zero.rs
src/test/run-fail/overflowing-add.rs
src/test/run-fail/overflowing-lsh-1.rs
src/test/run-fail/overflowing-lsh-2.rs
src/test/run-fail/overflowing-lsh-3.rs
src/test/run-fail/overflowing-lsh-4.rs
src/test/run-fail/overflowing-mul.rs
src/test/run-fail/overflowing-neg.rs
src/test/run-fail/overflowing-pow.rs
src/test/run-fail/overflowing-rsh-1.rs
src/test/run-fail/overflowing-rsh-2.rs
src/test/run-fail/overflowing-rsh-3.rs
src/test/run-fail/overflowing-rsh-4.rs
src/test/run-fail/overflowing-rsh-5.rs
src/test/run-fail/overflowing-rsh-6.rs
src/test/run-fail/overflowing-sub.rs
src/test/run-fail/panic-arg.rs
src/test/run-fail/panic-macro-any.rs
src/test/run-fail/panic-main.rs
src/test/run-fail/panic-parens.rs
src/test/run-fail/panic-set-handler.rs
src/test/run-fail/panic-set-unset-handler.rs
src/test/run-fail/panic-take-handler-nop.rs
src/test/run-fail/panic-task-name-none.rs
src/test/run-fail/panic-task-name-owned.rs
src/test/run-fail/panic.rs
src/test/run-fail/result-get-panic.rs
src/test/run-fail/rhs-type.rs
src/test/run-fail/run-unexported-tests.rs
src/test/run-fail/unimplemented-macro-panic.rs
src/test/run-fail/unique-panic.rs
src/test/run-fail/unreachable-macro-panic.rs
src/test/run-fail/unreachable-static-msg.rs
src/test/run-fail/unreachable.rs
src/test/run-fail/unwind-interleaved.rs
src/test/run-fail/unwind-rec.rs
src/test/run-fail/unwind-rec2.rs
src/test/run-fail/vec-overrun.rs
src/test/run-fail/while-body-panics.rs
src/test/run-fail/while-panic.rs
src/test/run-make/atomic-lock-free/atomic_lock_free.rs
src/test/run-make/debug-assertions/debug.rs
src/test/run-make/dep-info-no-analysis/Makefile [deleted file]
src/test/run-make/dep-info-no-analysis/input.dd [deleted file]
src/test/run-make/dep-info-no-analysis/input.rs [deleted file]
src/test/run-make/execution-engine/test.rs
src/test/run-make/issue-19371/foo.rs
src/test/run-make/llvm-phase/Makefile [new file with mode: 0644]
src/test/run-make/llvm-phase/test.rs [new file with mode: 0644]
src/test/run-make/unicode-input/span_length.rs
src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs
src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin.rs
src/test/run-pass-fulldeps/auxiliary/custom_derive_plugin_attr.rs
src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs
src/test/run-pass-fulldeps/auxiliary/issue_16723_multiple_items_syntax_ext.rs
src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs
src/test/run-pass-fulldeps/auxiliary/plugin_args.rs
src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs
src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs
src/test/run-pass-fulldeps/auxiliary/syntax_extension_with_dll_deps_2.rs
src/test/run-pass-fulldeps/compiler-calls.rs
src/test/run-pass-fulldeps/macro-crate.rs
src/test/run-pass-fulldeps/qquote.rs
src/test/run-pass-fulldeps/quote-tokens.rs
src/test/run-pass/assert-eq-macro-success.rs
src/test/run-pass/auxiliary/issue34796aux.rs [new file with mode: 0644]
src/test/run-pass/auxiliary/xcrate_generic_fn_nested_return.rs [new file with mode: 0644]
src/test/run-pass/binary-heap-panic-safe.rs
src/test/run-pass/coherence-subtyping.rs
src/test/run-pass/const-byte-str-cast.rs [new file with mode: 0644]
src/test/run-pass/dropck_legal_cycles.rs
src/test/run-pass/dynamic-drop.rs [new file with mode: 0644]
src/test/run-pass/exhaustive-bool-match-sanity.rs [new file with mode: 0644]
src/test/run-pass/hygiene.rs [new file with mode: 0644]
src/test/run-pass/issue-15080.rs
src/test/run-pass/issue-15104.rs
src/test/run-pass/issue-16648.rs
src/test/run-pass/issue-21058.rs
src/test/run-pass/issue-21350.rs [deleted file]
src/test/run-pass/issue-23477.rs [new file with mode: 0644]
src/test/run-pass/issue-23598.rs [deleted file]
src/test/run-pass/issue-23958.rs [new file with mode: 0644]
src/test/run-pass/issue-30240.rs [new file with mode: 0644]
src/test/run-pass/issue-30276.rs [new file with mode: 0644]
src/test/run-pass/issue-32805.rs [new file with mode: 0644]
src/test/run-pass/issue-33770.rs [new file with mode: 0644]
src/test/run-pass/issue-34074.rs [new file with mode: 0644]
src/test/run-pass/issue-34932.rs [new file with mode: 0644]
src/test/run-pass/issue-7784.rs
src/test/run-pass/issue-8460.rs
src/test/run-pass/issue34796.rs [new file with mode: 0644]
src/test/run-pass/match-unsized.rs [new file with mode: 0644]
src/test/run-pass/match-vec-alternatives.rs
src/test/run-pass/mir_call_with_associated_type.rs [new file with mode: 0644]
src/test/run-pass/mir_cast_fn_ret.rs [new file with mode: 0644]
src/test/run-pass/mir_constval_adts.rs
src/test/run-pass/mir_trans_calls.rs
src/test/run-pass/multi-panic.rs
src/test/run-pass/panic-recover-propagate.rs
src/test/run-pass/pat-tuple-1.rs [new file with mode: 0644]
src/test/run-pass/pat-tuple-2.rs [new file with mode: 0644]
src/test/run-pass/pat-tuple-3.rs [new file with mode: 0644]
src/test/run-pass/pat-tuple-4.rs [new file with mode: 0644]
src/test/run-pass/pat-tuple-5.rs [new file with mode: 0644]
src/test/run-pass/pat-tuple-6.rs [new file with mode: 0644]
src/test/run-pass/project-cache-issue-31849.rs [new file with mode: 0644]
src/test/run-pass/range_inclusive.rs
src/test/run-pass/reachable-unnameable-items.rs
src/test/run-pass/running-with-no-runtime.rs
src/test/run-pass/sleep.rs [new file with mode: 0644]
src/test/run-pass/test-vs-cfg-test.rs [new file with mode: 0644]
src/test/run-pass/thread-local-syntax.rs [new file with mode: 0644]
src/test/run-pass/trait-item-inside-macro.rs [new file with mode: 0644]
src/test/run-pass/utf8_chars.rs
src/test/run-pass/variadic-ffi.rs
src/test/run-pass/vec-matching-fold.rs
src/test/run-pass/vec-matching-legal-tail-element-borrow.rs
src/test/run-pass/vec-matching.rs
src/test/run-pass/vec-tail-matching.rs
src/test/run-pass/xcrate_generic_fn_nested_return.rs [new file with mode: 0644]
src/test/run-pass/zero_sized_subslice_match.rs
src/test/rustdoc/auxiliary/issue-34274.rs [new file with mode: 0644]
src/test/rustdoc/auxiliary/src-links-external.rs [new file with mode: 0644]
src/test/rustdoc/deprecated-impls.rs [new file with mode: 0644]
src/test/rustdoc/hidden-impls.rs [new file with mode: 0644]
src/test/rustdoc/hidden-methods.rs [new file with mode: 0644]
src/test/rustdoc/inline_cross/hidden-use.rs [new file with mode: 0644]
src/test/rustdoc/inline_local/hidden-use.rs [new file with mode: 0644]
src/test/rustdoc/issue-34025.rs [new file with mode: 0644]
src/test/rustdoc/issue-34274.rs [new file with mode: 0644]
src/test/rustdoc/issue-34423.rs [new file with mode: 0644]
src/test/rustdoc/issue-34473.rs [new file with mode: 0644]
src/test/rustdoc/module-impls.rs [new file with mode: 0644]
src/test/rustdoc/redirect-const.rs [new file with mode: 0644]
src/test/rustdoc/redirect-rename.rs [new file with mode: 0644]
src/test/rustdoc/src-links-external.rs [new file with mode: 0644]
src/test/ui/mismatched_types/issue-26480.rs [new file with mode: 0644]
src/test/ui/mismatched_types/issue-26480.stderr [new file with mode: 0644]
src/test/ui/mismatched_types/main.stderr
src/tools/cargotest/main.rs
src/tools/compiletest/src/json.rs
src/tools/compiletest/src/main.rs
src/tools/compiletest/src/runtest.rs
src/tools/linkchecker/main.rs
src/tools/rustbook/main.rs
src/tools/tidy/src/cargo.rs

index 9d61feef81a80ec2fdc1a753f4e275f185543441..60935770781817a63c3ea9b6a19260ee33118b54 100644 (file)
@@ -108,7 +108,8 @@ root.
 There are large number of options accepted by this script to alter the
 configuration used later in the build process. Some options to note:
 
-- `--enable-debug` - Build a debug version of the compiler (disables optimizations)
+- `--enable-debug` - Build a debug version of the compiler (disables optimizations,
+    which speeds up compilation of stage1 rustc)
 - `--enable-optimize` - Enable optimizations (can be used with `--enable-debug`
     to make a debug build with optimizations)
 - `--disable-valgrind-rpass` - Don't run tests with valgrind
@@ -122,10 +123,18 @@ To see a full list of options, run `./configure --help`.
 
 Some common make targets are:
 
+- `make tips` - show useful targets, variables and other tips for working with
+   the build system.
 - `make rustc-stage1` - build up to (and including) the first stage. For most
   cases we don't need to build the stage2 compiler, so we can save time by not
   building it. The stage1 compiler is a fully functioning compiler and
   (probably) will be enough to determine if your change works as expected.
+- `make $host/stage1/bin/rustc` - Where $host is a target triple like x86_64-unknown-linux-gnu.
+  This will build just rustc, without libstd. This is the fastest way to recompile after
+  you changed only rustc source code. Note however that the resulting rustc binary
+  won't have a stdlib to link against by default. You can build libstd once with
+  `make rustc-stage1`, rustc will pick it up afterwards. libstd is only guaranteed to
+  work if recompiled, so if there are any issues recompile it.
 - `make check` - build the full compiler & run all tests (takes a while). This
   is what gets run by the continuous integration system against your pull
   request. You should run this before submitting to make sure your tests pass
index 7425e9bd73e95913b75af6d7b8d345a05218b819..9e87ce1d9e69a198cfc87d5f200cb82be5bba46c 100644 (file)
@@ -62,6 +62,8 @@
 #   * tidy - Basic style check, show highest rustc error code and
 #     the status of language and lib features
 #   * rustc-stage$(stage) - Only build up to a specific stage
+#   * $host/stage1/bin/rustc - Only build stage1 rustc, not libstd. For further
+#     information see "Rust recipes for build system success" below.
 #
 # Then mix in some of these environment variables to harness the
 # ultimate power of The Rust Build System.
 #     // Modifying libstd? Use this command to run unit tests just on your change
 #     make check-stage1-std NO_REBUILD=1 NO_BENCH=1
 #
+#     // Modifying just rustc?
+#     // Compile rustc+libstd once
+#     make rustc-stage1
+#     // From now on use this command to rebuild just rustc and reuse the previously built libstd
+#     // $host is a target triple, eg. x86_64-unknown-linux-gnu
+#     // The resulting binary is located at $host/stage1/bin/rustc.
+#     // If there are any issues with libstd recompile it with the command above.
+#     make $host/stage1/bin/rustc
+#
 #     // Added a run-pass test? Use this to test running your test
 #     make check-stage1-rpass TESTNAME=my-shiny-new-test
 #
@@ -266,13 +277,17 @@ endif
 
 # CTAGS building
 ifneq ($(strip $(findstring TAGS.emacs,$(MAKECMDGOALS)) \
-               $(findstring TAGS.vi,$(MAKECMDGOALS))),)
+               $(findstring TAGS.vi,$(MAKECMDGOALS)) \
+               $(findstring TAGS.rustc.emacs,$(MAKECMDGOALS)) \
+               $(findstring TAGS.rustc.vi,$(MAKECMDGOALS))),)
   CFG_INFO := $(info cfg: including ctags rules)
   include $(CFG_SRC_DIR)mk/ctags.mk
 endif
 
 .DEFAULT:
-       @echo "\n======================================================"
+       @echo
+       @echo "======================================================"
        @echo "== If you need help, run 'make help' or 'make tips' =="
-       @echo "======================================================\n"
+       @echo "======================================================"
+       @echo
        exit 1
index 4e476b4f357ec945f44747f8526cde2f4032088a..49236d6b671ef74cb8f4bb8f90d35171d243dc88 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # The Rust Programming Language
 
-This is the main source code repository for [Rust]. It contains the compiler, standard library,
-and documentation.
+This is the main source code repository for [Rust]. It contains the compiler,
+standard library, and documentation.
 
 [Rust]: https://www.rust-lang.org
 
@@ -19,6 +19,7 @@ Read ["Installing Rust"] from [The Book].
    * `g++` 4.7 or later or `clang++` 3.x
    * `python` 2.7 (but not 3.x)
    * GNU `make` 3.81 or later
+   * `cmake` 2.8.8 or later
    * `curl`
    * `git`
 
@@ -63,35 +64,37 @@ build.
 
 #### MinGW
 
-[MSYS2](http://msys2.github.io/) can be used to easily build Rust on Windows:
+[MSYS2][msys2] can be used to easily build Rust on Windows:
 
-1. Grab the latest MSYS2 installer and go through the installer.
+msys2: https://msys2.github.io/
 
-2. From the MSYS2 terminal, install the `mingw64` toolchain and other required
-   tools.
+1. Grab the latest [MSYS2 installer][msys2] and go through the installer.
+
+2. Run `mingw32_shell.bat` or `mingw64_shell.bat` from wherever you installed
+   MSYS2 (i.e. `C:\msys64`), depending on whether you want 32-bit or 64-bit
+   Rust. (As of the latest version of MSYS2 you have to run `msys2_shell.cmd
+   -mingw32` or `msys2_shell.cmd -mingw64` from the command line instead)
+
+3. From this terminal, install the required tools:
 
    ```sh
    # Update package mirrors (may be needed if you have a fresh install of MSYS2)
    $ pacman -Sy pacman-mirrors
-   ```
-
-Download [MinGW from
-here](http://mingw-w64.org/doku.php/download/mingw-builds), and choose the
-`version=4.9.x,threads=win32,exceptions=dwarf/seh` flavor when installing. Also, make sure to install to a path without spaces in it. After installing,
-add its `bin` directory to your `PATH`. This is due to [#28260](https://github.com/rust-lang/rust/issues/28260), in the future,
-installing from pacman should be just fine.
-
-   ```
-   # Make git available in MSYS2 (if not already available on path)
-   $ pacman -S git
 
-   $ pacman -S base-devel
+   # Install build tools needed for Rust. If you're building a 32-bit compiler,
+   # then replace "x86_64" below with "i686". If you've already got git, python,
+   # or CMake installed and in PATH you can remove them from this list. Note
+   # that it is important that the `python2` and `cmake` packages **not** used.
+   # The build has historically been known to fail with these packages.
+   $ pacman -S git \
+               make \
+               diffutils \
+               mingw-w64-x86_64-python2 \
+               mingw-w64-x86_64-cmake \
+               mingw-w64-x86_64-gcc
    ```
 
-3. Run `mingw32_shell.bat` or `mingw64_shell.bat` from wherever you installed
-   MSYS2 (i.e. `C:\msys`), depending on whether you want 32-bit or 64-bit Rust.
-
-4. Navigate to Rust's source code, configure and build it:
+4. Navigate to Rust's source code (or clone it), then configure and build it:
 
    ```sh
    $ ./configure
@@ -111,12 +114,25 @@ $ ./configure
 $ make && make install
 ```
 
+#### MSVC with rustbuild
+
+For those who don't want the hassle of MSYS or MinGW, you can invoke rustbuild
+directly. All you need are Python 2, CMake, and Git in your PATH (make sure you
+do __not__ use the ones from MSYS!). You'll also need Visual Studio 2013 or
+newer with the C++ tools. Then all you need to do is invoke the appropriate
+vcvars bat file and kick off rustbuild.
+
+```bat
+CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat"
+python .\src\bootstrap\bootstrap.py
+```
+
 ## Building Documentation
 
 If you’d like to build the documentation, it’s almost the same:
 
 ```sh
-./configure
+./configure
 $ make docs
 ```
 
@@ -151,8 +167,8 @@ Snapshot binaries are currently built and tested on several platforms:
 You may find that other platforms work, but these are our officially
 supported build environments that are most likely to work.
 
-Rust currently needs between 600MiB and 1.5GiB to build, depending on platform. If it hits
-swap, it will take a very long time to build.
+Rust currently needs between 600MiB and 1.5GiB to build, depending on platform.
+If it hits swap, it will take a very long time to build.
 
 There is more advice about hacking on Rust in [CONTRIBUTING.md].
 
@@ -189,4 +205,5 @@ Rust is primarily distributed under the terms of both the MIT license
 and the Apache License (Version 2.0), with portions covered by various
 BSD-like licenses.
 
-See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT), and [COPYRIGHT](COPYRIGHT) for details.
+See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT), and
+[COPYRIGHT](COPYRIGHT) for details.
index d4178963f86913a04f95e8f794063887b7b76128..6451b87673f628c230d2686f3f7dc3773506bd10 100755 (executable)
--- a/configure
+++ b/configure
@@ -133,12 +133,13 @@ probe() {
 }
 
 probe_need() {
-    local V=$1
     probe $*
+    local V=$1
+    shift
     eval VV=\$$V
     if [ -z "$VV" ]
     then
-        err "needed, but unable to find any of: $*"
+        err "$V needed, but unable to find any of: $*"
     fi
 }
 
@@ -593,12 +594,13 @@ opt docs     1 "build standard library documentation"
 opt compiler-docs     0 "build compiler documentation"
 opt optimize-tests 1 "build tests with optimizations"
 opt debuginfo-tests 0 "build tests with debugger metadata"
-opt libcpp 1 "build with llvm with libc++ instead of libstdc++ when using clang"
+opt libcpp 1 "build llvm with libc++ instead of libstdc++ when using clang"
 opt llvm-assertions 0 "build LLVM with assertions"
 opt debug-assertions 0 "build with debugging assertions"
 opt fast-make 0 "use .gitmodules as timestamp for submodule deps"
 opt ccache 0 "invoke gcc/clang via ccache to reuse object files between builds"
 opt local-rust 0 "use an installed rustc rather than downloading a snapshot"
+opt local-rebuild 0 "use an installed rustc matching the current version, for rebuilds"
 opt llvm-static-stdcpp 0 "statically link to libstdc++ for LLVM"
 opt rpath 1 "build rpaths into rustc itself"
 opt stage0-landing-pads 1 "enable landing pads during bootstrap with stage0"
@@ -724,7 +726,7 @@ if [ -n "$CFG_ENABLE_ORBIT" ]; then putvar CFG_ENABLE_ORBIT; fi
 
 step_msg "looking for build programs"
 
-probe_need CFG_CURLORWGET  curl wget
+probe_need CFG_CURL curl
 if [ -z "$CFG_PYTHON_PROVIDED" ]; then
     probe_need CFG_PYTHON      python2.7 python2 python
 fi
@@ -773,6 +775,9 @@ probe CFG_BISON            bison
 probe CFG_GDB              gdb
 probe CFG_LLDB             lldb
 
+# For building LLVM
+probe_need CFG_CMAKE cmake
+
 # On MacOS X, invoking `javac` pops up a dialog if the JDK is not
 # installed. Since `javac` is only used if `antlr4` is available,
 # probe for it only in this case.
@@ -847,6 +852,16 @@ then
     BIN_SUF=.exe
 fi
 
+# --enable-local-rebuild implies --enable-local-rust too
+if [ -n "$CFG_ENABLE_LOCAL_REBUILD" ]
+then
+    if [ -z "$CFG_ENABLE_LOCAL_RUST" ]
+    then
+        CFG_ENABLE_LOCAL_RUST=1
+        putvar CFG_ENABLE_LOCAL_RUST
+    fi
+fi
+
 if [ -n "$CFG_ENABLE_LOCAL_RUST" ]
 then
     system_rustc=$(which rustc)
@@ -976,11 +991,11 @@ then
     LLVM_VERSION=$($LLVM_CONFIG --version)
 
     case $LLVM_VERSION in
-        (3.[6-8]*)
+        (3.[7-8]*)
             msg "found ok version of LLVM: $LLVM_VERSION"
             ;;
         (*)
-            err "bad LLVM version: $LLVM_VERSION, need >=3.6"
+            err "bad LLVM version: $LLVM_VERSION, need >=3.7"
             ;;
     esac
 fi
@@ -1163,36 +1178,6 @@ do
             ;;
 
         *-msvc)
-            # There are some MSYS python builds which will auto-translate
-            # windows-style paths to MSYS-style paths in Python itself.
-            # Unfortunately this breaks LLVM's build system as somewhere along
-            # the line LLVM prints a path into a file from Python and then CMake
-            # later tries to interpret that path. If Python prints a MSYS path
-            # and CMake tries to use it as a Windows path, you're gonna have a
-            # Bad Time.
-            #
-            # Consequently here we try to detect when that happens and print an
-            # error if it does.
-            if $CFG_PYTHON -c 'import sys; print sys.argv[1]' `pwd` | grep '^/' > /dev/null
-            then
-                err "
-
-python is silently translating windows paths to MSYS paths \
-and the build will fail if this python is used.
-
-Either an official python install must be used or an \
-alternative python package in MinGW must be used.
-
-If you are building under msys2 try installing the mingw-w64-x86_64-python2 \
-package instead of python2:
-
-$ pacman -R python2 && pacman -S mingw-w64-x86_64-python2
-"
-            fi
-
-            # MSVC requires cmake because that's how we're going to build LLVM
-            probe_need CFG_CMAKE cmake
-
             # There are three builds of cmake on windows: MSVC, MinGW and Cygwin
             # The Cygwin build does not have generators for Visual Studio, so
             # detect that here and error.
@@ -1276,6 +1261,36 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
     esac
 done
 
+if [ "$CFG_OSTYPE" = "pc-windows-gnu" ] || [ "$CFG_OSTYPE" = "pc-windows-msvc" ]
+then
+    # There are some MSYS python builds which will auto-translate
+    # windows-style paths to MSYS-style paths in Python itself.
+    # Unfortunately this breaks LLVM's build system as somewhere along
+    # the line LLVM prints a path into a file from Python and then CMake
+    # later tries to interpret that path. If Python prints a MSYS path
+    # and CMake tries to use it as a Windows path, you're gonna have a
+    # Bad Time.
+    #
+    # Consequently here we try to detect when that happens and print an
+    # error if it does.
+    if $CFG_PYTHON -c 'import sys; print sys.argv[1]' `pwd` | grep '^/' > /dev/null
+    then
+        err "
+
+python is silently translating windows paths to MSYS paths \
+and the build will fail if this python is used.
+
+Either an official python install must be used or an \
+alternative python package in MinGW must be used.
+
+If you are building under msys2 try installing the mingw-w64-x86_64-python2 \
+package instead of python2:
+
+$ pacman -S mingw-w64-x86_64-python2
+"
+    fi
+fi
+
 if [ -n "$CFG_PERF" ]
 then
     HAVE_PERF_LOGFD=`$CFG_PERF stat --log-fd 2>&1 | grep 'unknown option'`
@@ -1465,27 +1480,16 @@ do
     elif [ -z $CFG_LLVM_ROOT ]
     then
         LLVM_BUILD_DIR=${CFG_BUILD_DIR}$t/llvm
-        if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]
-        then
-            LLVM_DBG_OPTS="--enable-debug-symbols --disable-optimized"
-            # Just use LLVM straight from its build directory to
-            # avoid 'make install' time
-            LLVM_INST_DIR=$LLVM_BUILD_DIR/Debug
-        else
-            LLVM_DBG_OPTS="--enable-optimized"
-            LLVM_INST_DIR=$LLVM_BUILD_DIR/Release
-        fi
-        if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ]
-        then
-            LLVM_ASSERTION_OPTS="--disable-assertions"
-        else
-            LLVM_ASSERTION_OPTS="--enable-assertions"
-
-            # Apparently even if we request assertions be enabled for MSVC,
-            # LLVM's CMake build system ignore this and outputs in `Release`
-            # anyway.
-            if [ ${is_msvc} -eq 0 ]; then
-                LLVM_INST_DIR=${LLVM_INST_DIR}+Asserts
+        LLVM_INST_DIR=$LLVM_BUILD_DIR
+        # For some crazy reason the MSVC output dir is different than Unix
+        if [ ${is_msvc} -ne 0 ]; then
+            if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]
+            then
+                # Just use LLVM straight from its build directory to
+                # avoid 'make install' time
+                LLVM_INST_DIR=$LLVM_BUILD_DIR/Debug
+            else
+                LLVM_INST_DIR=$LLVM_BUILD_DIR/Release
             fi
         fi
     else
@@ -1543,88 +1547,60 @@ do
                 err "can only build LLVM for x86 platforms"
                 ;;
         esac
-        CFG_CMAKE_GENERATOR=$generator
-        putvar CFG_CMAKE_GENERATOR
-    fi
-
-    if [ ${do_reconfigure} -ne 0 ] && [ ${is_msvc} -ne 0 ]
-    then
-        msg "configuring LLVM for $t with cmake"
-
-        CMAKE_ARGS="-DLLVM_INCLUDE_TESTS=OFF"
-        if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then
-            CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug"
-        else
-            CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release"
-        fi
-        if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ]
-        then
-            CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=OFF"
-        else
-            CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=ON"
-        fi
-
-        msg "configuring LLVM with:"
-        msg "$CMAKE_ARGS"
-
-        (cd $LLVM_BUILD_DIR && "$CFG_CMAKE" $CFG_LLVM_SRC_DIR \
-                                            -G "$CFG_CMAKE_GENERATOR" \
-                                            $CMAKE_ARGS)
-        need_ok "LLVM cmake configure failed"
+    else
+        generator="Unix Makefiles"
     fi
-
-    if [ ${do_reconfigure} -ne 0 ] && [ ${is_msvc} -eq 0 ]
-    then
-        # LLVM's configure doesn't recognize the new Windows triples yet
-        gnu_t=$(to_gnu_triple $t)
-
-        msg "configuring LLVM for $gnu_t"
-
-        LLVM_TARGETS="--enable-targets=x86,x86_64,arm,aarch64,mips,powerpc"
-        LLVM_BUILD="--build=$gnu_t"
-        LLVM_HOST="--host=$gnu_t"
-        LLVM_TARGET="--target=$gnu_t"
-
-        # Disable unused LLVM features
-        LLVM_OPTS="$LLVM_DBG_OPTS $LLVM_ASSERTION_OPTS --disable-docs --enable-bindings=none"
-        # Disable term-info, linkage of which comes in multiple forms,
-        # making our snapshots incompatible (#9334)
-        LLVM_OPTS="$LLVM_OPTS --disable-terminfo"
-        # Try to have LLVM pull in as few dependencies as possible (#9397)
-        LLVM_OPTS="$LLVM_OPTS --disable-zlib --disable-libffi"
-
-        # Use win32 native thread/lock apis instead of pthread wrapper.
-        # (llvm's configure tries to find pthread first, so we have to disable it explicitly.)
-        # Also note that pthreads works badly on mingw-w64 systems: #8996
-        case "$CFG_BUILD" in
-            (*-windows-gnu)
-            LLVM_OPTS="$LLVM_OPTS --disable-pthreads"
+    CFG_CMAKE_GENERATOR=$generator
+    putvar CFG_CMAKE_GENERATOR
+
+    msg "configuring LLVM for $t"
+
+    LLVM_CFLAGS_32=""
+    LLVM_CXXFLAGS_32=""
+    LLVM_LDFLAGS_32=""
+    LLVM_CFLAGS_64=""
+    LLVM_CXXFLAGS_64=""
+    LLVM_LDFLAGS_64=""
+
+    case "$CFG_CC" in
+        ("ccache clang")
+            LLVM_CXX_32="ccache"
+            LLVM_CC_32="ccache"
+            LLVM_CXX_32_ARG1="clang++"
+            LLVM_CC_32_ARG1="clang"
+            LLVM_CFLAGS_32="-Qunused-arguments"
+            LLVM_CXXFLAGS_32="-Qunused-arguments"
+
+            LLVM_CXX_64="ccache"
+            LLVM_CC_64="ccache"
+            LLVM_CXX_64_ARG1="clang++"
+            LLVM_CC_64_ARG1="clang"
+            LLVM_CFLAGS_64="-Qunused-arguments"
+            LLVM_CXXFLAGS_64="-Qunused-arguments"
             ;;
-        esac
-
-        case "$CFG_CC" in
-            ("ccache clang")
-            LLVM_CXX_32="ccache clang++ -Qunused-arguments"
-            LLVM_CC_32="ccache clang -Qunused-arguments"
-
-            LLVM_CXX_64="ccache clang++ -Qunused-arguments"
-            LLVM_CC_64="ccache clang -Qunused-arguments"
+        ("clang")
+            LLVM_CXX_32="clang++"
+            LLVM_CC_32="clang"
+            LLVM_CFLAGS_32="-Qunused-arguments"
+            LLVM_CXXFLAGS_32="-Qunused-arguments"
+
+            LLVM_CXX_64="clang++"
+            LLVM_CC_64="clang"
+            LLVM_CFLAGS_64="-Qunused-arguments"
+            LLVM_CXXFLAGS_64="-Qunused-arguments"
             ;;
-            ("clang")
-            LLVM_CXX_32="clang++ -Qunused-arguments"
-            LLVM_CC_32="clang -Qunused-arguments"
-
-            LLVM_CXX_64="clang++ -Qunused-arguments"
-            LLVM_CC_64="clang -Qunused-arguments"
+        ("ccache gcc")
+            LLVM_CXX_32="ccache"
+            LLVM_CC_32="ccache"
+            LLVM_CXX_32_ARG1="clang++"
+            LLVM_CC_32_ARG1="clang"
+
+            LLVM_CXX_64="ccache"
+            LLVM_CC_64="ccache"
+            LLVM_CXX_64_ARG1="g++"
+            LLVM_CC_64_ARG1="gcc"
             ;;
-            ("ccache gcc")
-            LLVM_CXX_32="ccache g++"
-            LLVM_CC_32="ccache gcc"
-
-            LLVM_CXX_64="ccache g++"
-            LLVM_CC_64="ccache gcc"
-            ;;
-            ("gcc")
+        ("gcc")
             LLVM_CXX_32="g++"
             LLVM_CC_32="gcc"
 
@@ -1632,7 +1608,7 @@ do
             LLVM_CC_64="gcc"
             ;;
 
-            (*)
+        (*)
             msg "inferring LLVM_CXX/CC from CXX/CC = $CXX/$CC"
             if [ -n "$CFG_ENABLE_CCACHE" ]
             then
@@ -1641,11 +1617,15 @@ do
                     err "ccache requested but not found"
                 fi
 
-                LLVM_CXX_32="ccache $CXX"
-                LLVM_CC_32="ccache $CC"
+                LLVM_CXX_32="ccache"
+                LLVM_CC_32="ccache"
+                LLVM_CXX_32_ARG1="$CXX"
+                LLVM_CC_32_ARG1="$CC"
 
-                LLVM_CXX_64="ccache $CXX"
-                LLVM_CC_64="ccache $CC"
+                LLVM_CXX_64="ccache"
+                LLVM_CC_64="ccache"
+                LLVM_CXX_64_ARG1="$CXX"
+                LLVM_CC_64_ARG1="$CC"
             else
                 LLVM_CXX_32="$CXX"
                 LLVM_CC_32="$CC"
             fi
 
             ;;
-        esac
+    esac
+
+    case "$CFG_CPUTYPE" in
+        (x86*)
+            LLVM_CFLAGS_32="$LLVM_CFLAGS_32 -m32"
+            LLVM_CXXFLAGS_32="$LLVM_CXXFLAGS_32 -m32"
+            LLVM_LDFLAGS_32="$LLVM_LDFLAGS_32 -m32"
+            ;;
+    esac
 
-        case "$CFG_CPUTYPE" in
-            (x86*)
-                LLVM_CXX_32="$LLVM_CXX_32 -m32"
-                LLVM_CC_32="$LLVM_CC_32 -m32"
+    if echo $t | grep -q x86_64
+    then
+        LLVM_CXX=$LLVM_CXX_64
+        LLVM_CC=$LLVM_CC_64
+        LLVM_CXX_ARG1=$LLVM_CXX_64_ARG1
+        LLVM_CC_ARG1=$LLVM_CC_64_ARG1
+        LLVM_CFLAGS=$LLVM_CFLAGS_64
+        LLVM_CXXFLAGS=$LLVM_CXXFLAGS_64
+        LLVM_LDFLAGS=$LLVM_LDFLAGS_64
+    else
+        LLVM_CXX=$LLVM_CXX_32
+        LLVM_CC=$LLVM_CC_32
+        LLVM_CXX_ARG1=$LLVM_CXX_32_ARG1
+        LLVM_CC_ARG1=$LLVM_CC_32_ARG1
+        LLVM_CFLAGS=$LLVM_CFLAGS_32
+        LLVM_CXXFLAGS=$LLVM_CXXFLAGS_32
+        LLVM_LDFLAGS=$LLVM_LDFLAGS_32
+    fi
 
-                LLVM_CFLAGS_32="-m32"
-                LLVM_CXXFLAGS_32="-m32"
-                LLVM_LDFLAGS_32="-m32"
+    if [ "$CFG_USING_LIBCPP" != "0" ]; then
+        CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_LIBCXX=ON"
+    fi
 
-                LLVM_CFLAGS_64=""
-                LLVM_CXXFLAGS_64=""
-                LLVM_LDFLAGS_64=""
+    # Turn off things we don't need
+    CMAKE_ARGS="$CMAKE_ARGS -DLLVM_INCLUDE_TESTS=OFF"
+    CMAKE_ARGS="$CMAKE_ARGS -DLLVM_INCLUDE_EXAMPLES=OFF"
+    CMAKE_ARGS="$CMAKE_ARGS -DLLVM_INCLUDE_DOCS=OFF"
+    CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ZLIB=OFF"
+    CMAKE_ARGS="$CMAKE_ARGS -DWITH_POLY=OFF"
+    CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_TERMINFO=OFF"
+    CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_LIBEDIT=OFF"
 
-                LLVM_CXX_32="$LLVM_CXX_32 -m32"
-                LLVM_CC_32="$LLVM_CC_32 -m32"
-                ;;
+    arch="$(echo "$t" | cut -d - -f 1)"
 
-            (*)
-                LLVM_CFLAGS_32=""
-                LLVM_CXXFLAGS_32=""
-                LLVM_LDFLAGS_32=""
+    if [ "$arch" = i686 ]; then
+        CMAKE_ARGS="$CMAKE_ARGS -DLLVM_BUILD_32_BITS=ON"
+    fi
 
-                LLVM_CFLAGS_64=""
-                LLVM_CXXFLAGS_64=""
-                LLVM_LDFLAGS_64=""
-                ;;
-        esac
+    if [ "$t" != "$CFG_BUILD" ]; then
+        # see http://llvm.org/docs/HowToCrossCompileLLVM.html
+        CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CROSSCOMPILING=True"
+        CMAKE_ARGS="$CMAKE_ARGS -DLLVM_TARGET_ARCH=$arch"
+        CMAKE_ARGS="$CMAKE_ARGS -DLLVM_TABLEGEN=$CFG_BUILD_DIR/$CFG_BUILD/llvm/bin/llvm-tblgen"
+        CMAKE_ARGS="$CMAKE_ARGS -DLLVM_DEFAULT_TARGET_TRIPLE=$t"
+    fi
 
-        if echo $t | grep -q x86_64
-        then
-            LLVM_CXX=$LLVM_CXX_64
-            LLVM_CC=$LLVM_CC_64
-            LLVM_CFLAGS=$LLVM_CFLAGS_64
-            LLVM_CXXFLAGS=$LLVM_CXXFLAGS_64
-            LLVM_LDFLAGS=$LLVM_LDFLAGS_64
-        else
-            LLVM_CXX=$LLVM_CXX_32
-            LLVM_CC=$LLVM_CC_32
-            LLVM_CFLAGS=$LLVM_CFLAGS_32
-            LLVM_CXXFLAGS=$LLVM_CXXFLAGS_32
-            LLVM_LDFLAGS=$LLVM_LDFLAGS_32
+    # MSVC handles compiler business itself
+    if [ ${is_msvc} -eq 0 ]; then
+        CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_C_COMPILER=$LLVM_CC"
+        CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CXX_COMPILER=$LLVM_CXX"
+        CMAKE_ARGS="$CMAKE_ARGS '-DCMAKE_C_FLAGS=$LLVM_CFLAGS'"
+        CMAKE_ARGS="$CMAKE_ARGS '-DCMAKE_CXX_FLAGS=$LLVM_CXXFLAGS'"
+        if [ -n "$LLVM_CC_ARG1" ]; then
+            CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_C_COMPILER_ARG1=$LLVM_CC_ARG1"
+        fi
+        if [ -n "$LLVM_CXX_ARG1" ]; then
+            CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CXX_COMPILER_ARG1=$LLVM_CXX_ARG1"
         fi
+        # FIXME: What about LDFLAGS?
+    fi
 
-        CXX=$LLVM_CXX
-        CC=$LLVM_CC
-        CFLAGS="$CFLAGS $LLVM_CFLAGS"
-        CXXFLAGS="$CXXFLAGS $LLVM_CXXFLAGS"
-        LDFLAGS="$LDFLAGS $LLVM_LDFLAGS"
+    if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then
+        CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug"
+    else
+        CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release"
+    fi
+    if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ]
+    then
+        CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=OFF"
+    else
+        CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=ON"
+    fi
 
-        if [ "$CFG_USING_LIBCPP" != "0" ]; then
-            LLVM_OPTS="$LLVM_OPTS --enable-libcpp"
-        fi
+    CMAKE_ARGS="$CMAKE_ARGS -DLLVM_TARGETS_TO_BUILD='X86;ARM;AArch64;Mips;PowerPC'"
+    CMAKE_ARGS="$CMAKE_ARGS -G '$CFG_CMAKE_GENERATOR'"
+    CMAKE_ARGS="$CMAKE_ARGS $CFG_LLVM_SRC_DIR"
 
-        LLVM_FLAGS="$LLVM_TARGETS $LLVM_OPTS $LLVM_BUILD \
-                        $LLVM_HOST $LLVM_TARGET --with-python=$CFG_PYTHON"
+    if [ ${do_reconfigure} -ne 0 ]
+    then
+        msg "configuring LLVM for $t with cmake"
 
         msg "configuring LLVM with:"
-        msg "$LLVM_FLAGS"
-
-        export CXX
-        export CC
-        export CFLAGS
-        export CXXFLAGS
-        export LDFLAGS
-
-        cd $LLVM_BUILD_DIR
-        case $CFG_SRC_DIR in
-            /* | [a-z]:* | [A-Z]:*)
-                ${CFG_LLVM_SRC_DIR}configure $LLVM_FLAGS
-                ;;
-            *)
-                ${CFG_BUILD_DIR}${CFG_LLVM_SRC_DIR}configure \
-                    $LLVM_FLAGS
-                ;;
-        esac
-        need_ok "LLVM configure failed"
+        msg "$CMAKE_ARGS"
 
-        cd $CFG_BUILD_DIR
+        (cd $LLVM_BUILD_DIR && eval "\"$CFG_CMAKE\"" $CMAKE_ARGS)
+        need_ok "LLVM cmake configure failed"
     fi
 
     # Construct variables for LLVM build and install directories for
index 88c0907f63b2ac8f8be2e4d30078d31fe75db184..129af8ac69609396b0b31cd496f294e0e9ed4d18 100644 (file)
@@ -8,7 +8,7 @@ CFG_STATIC_LIB_NAME_i686-unknown-linux-gnu=lib$(1).a
 CFG_LIB_GLOB_i686-unknown-linux-gnu=lib$(1)-*.so
 CFG_LIB_DSYM_GLOB_i686-unknown-linux-gnu=lib$(1)-*.dylib.dSYM
 CFG_JEMALLOC_CFLAGS_i686-unknown-linux-gnu := -m32 $(CFLAGS)
-CFG_GCCISH_CFLAGS_i686-unknown-linux-gnu := -Wall -Werror -g -fPIC -m32 $(CFLAGS)
+CFG_GCCISH_CFLAGS_i686-unknown-linux-gnu := -Wall -Werror -g -fPIC -m32 $(CFLAGS) -march=i686
 CFG_GCCISH_CXXFLAGS_i686-unknown-linux-gnu := -fno-rtti $(CXXFLAGS)
 CFG_GCCISH_LINK_FLAGS_i686-unknown-linux-gnu := -shared -fPIC -ldl -pthread  -lrt -g -m32
 CFG_GCCISH_DEF_FLAG_i686-unknown-linux-gnu := -Wl,--export-dynamic,--dynamic-list=
index 4c64402a73eadda8056959b571c1e2deb080464d..c1cd20a843cec5e2233296dc865acc612c309c2a 100644 (file)
@@ -1,6 +1,6 @@
 # i686-unknown-linux-musl configuration
 CC_i686-unknown-linux-musl=$(CFG_MUSL_ROOT)/bin/musl-gcc
-CXX_i686-unknown-linux-musl=notaprogram
+CXX_i686-unknown-linux-musl=$(CXX)
 CPP_i686-unknown-linux-musl=$(CFG_MUSL_ROOT)/bin/musl-gcc -E
 AR_i686-unknown-linux-musl=$(AR)
 CFG_INSTALL_ONLY_RLIB_i686-unknown-linux-musl = 1
index 9e7042befa975b0e013623162e8b48297fed2f49..0783a4c17a4f2153393e5f17171e78793c2e6f94 100644 (file)
@@ -7,10 +7,10 @@ CFG_LIB_NAME_mips-unknown-linux-gnu=lib$(1).so
 CFG_STATIC_LIB_NAME_mips-unknown-linux-gnu=lib$(1).a
 CFG_LIB_GLOB_mips-unknown-linux-gnu=lib$(1)-*.so
 CFG_LIB_DSYM_GLOB_mips-unknown-linux-gnu=lib$(1)-*.dylib.dSYM
-CFG_JEMALLOC_CFLAGS_mips-unknown-linux-gnu := -mips32r2 -msoft-float -mabi=32 $(CFLAGS)
-CFG_GCCISH_CFLAGS_mips-unknown-linux-gnu := -Wall -g -fPIC -mips32r2 -msoft-float -mabi=32 $(CFLAGS)
+CFG_JEMALLOC_CFLAGS_mips-unknown-linux-gnu := -mips32r2 -mabi=32 $(CFLAGS)
+CFG_GCCISH_CFLAGS_mips-unknown-linux-gnu := -Wall -g -fPIC -mips32r2 -mabi=32 $(CFLAGS)
 CFG_GCCISH_CXXFLAGS_mips-unknown-linux-gnu := -fno-rtti $(CXXFLAGS)
-CFG_GCCISH_LINK_FLAGS_mips-unknown-linux-gnu := -shared -fPIC -g -mips32r2 -msoft-float -mabi=32
+CFG_GCCISH_LINK_FLAGS_mips-unknown-linux-gnu := -shared -fPIC -g -mips32r2 -mabi=32
 CFG_GCCISH_DEF_FLAG_mips-unknown-linux-gnu := -Wl,--export-dynamic,--dynamic-list=
 CFG_LLC_FLAGS_mips-unknown-linux-gnu :=
 CFG_INSTALL_NAME_mips-unknown-linux-gnu =
index 62a884874bb429f78baf7371de8193e0fd99736c..dfe9de18f57878b4db44b7be3e87b75c083b757f 100644 (file)
@@ -1,6 +1,6 @@
 # x86_64-unknown-linux-musl configuration
 CC_x86_64-unknown-linux-musl=$(CFG_MUSL_ROOT)/bin/musl-gcc
-CXX_x86_64-unknown-linux-musl=notaprogram
+CXX_x86_64-unknown-linux-musl=$(CXX)
 CPP_x86_64-unknown-linux-musl=$(CFG_MUSL_ROOT)/bin/musl-gcc -E
 AR_x86_64-unknown-linux-musl=$(AR)
 CFG_INSTALL_ONLY_RLIB_x86_64-unknown-linux-musl = 1
index 1583515014a3941184ac5973e38e68f75073a079..0bd0c70bd0519bdf39fd4315d3ed82d354c9735c 100644 (file)
@@ -57,10 +57,10 @@ TARGET_CRATES := libc std term \
                 panic_abort panic_unwind unwind
 RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_driver \
                 rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \
-                rustc_data_structures rustc_platform_intrinsics \
+                rustc_data_structures rustc_platform_intrinsics rustc_errors \
                 rustc_plugin rustc_metadata rustc_passes rustc_save_analysis \
                 rustc_const_eval rustc_const_math rustc_incremental
-HOST_CRATES := syntax syntax_ext $(RUSTC_CRATES) rustdoc fmt_macros \
+HOST_CRATES := syntax syntax_ext syntax_pos $(RUSTC_CRATES) rustdoc fmt_macros \
                flate arena graphviz rbml log serialize
 TOOLS := compiletest rustdoc rustc rustbook error_index_generator
 
@@ -98,43 +98,45 @@ DEPS_serialize := std log
 DEPS_term := std
 DEPS_test := std getopts term native:rust_test_helpers
 
-DEPS_syntax := std term serialize log arena libc rustc_bitflags rustc_unicode
-DEPS_syntax_ext := syntax fmt_macros
+DEPS_syntax := std term serialize log arena libc rustc_bitflags rustc_unicode rustc_errors syntax_pos
+DEPS_syntax_ext := syntax syntax_pos rustc_errors fmt_macros
+DEPS_syntax_pos := serialize
 
 DEPS_rustc_const_math := std syntax log serialize
 DEPS_rustc_const_eval := rustc_const_math rustc syntax log serialize \
-                                            rustc_back graphviz
+                            rustc_back graphviz syntax_pos
 
 DEPS_rustc := syntax fmt_macros flate arena serialize getopts rbml \
               log graphviz rustc_llvm rustc_back rustc_data_structures\
-                         rustc_const_math
+             rustc_const_math syntax_pos rustc_errors
 DEPS_rustc_back := std syntax flate log libc
-DEPS_rustc_borrowck := rustc rustc_mir log graphviz syntax
+DEPS_rustc_borrowck := rustc log graphviz syntax syntax_pos rustc_errors rustc_mir
 DEPS_rustc_data_structures := std log serialize
 DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
                      rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \
                     rustc_trans rustc_privacy rustc_lint rustc_plugin \
                      rustc_metadata syntax_ext rustc_passes rustc_save_analysis rustc_const_eval \
-                     rustc_incremental
-DEPS_rustc_lint := rustc log syntax rustc_const_eval
+                     rustc_incremental syntax_pos rustc_errors
+DEPS_rustc_errors := log libc serialize syntax_pos
+DEPS_rustc_lint := rustc log syntax syntax_pos rustc_const_eval
 DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
-DEPS_rustc_metadata := rustc syntax rbml rustc_const_math
-DEPS_rustc_passes := syntax rustc core rustc_const_eval
-DEPS_rustc_mir := rustc syntax rustc_const_math rustc_const_eval rustc_bitflags
-DEPS_rustc_resolve := arena rustc log syntax
+DEPS_rustc_metadata := rustc syntax syntax_pos rustc_errors rbml rustc_const_math
+DEPS_rustc_passes := syntax syntax_pos rustc core rustc_const_eval rustc_errors
+DEPS_rustc_mir := rustc syntax syntax_pos rustc_const_math rustc_const_eval rustc_bitflags
+DEPS_rustc_resolve := arena rustc log syntax syntax_pos rustc_errors
 DEPS_rustc_platform_intrinsics := std
-DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir
-DEPS_rustc_privacy := rustc log syntax
-DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back rustc_mir \
+DEPS_rustc_plugin := rustc rustc_metadata syntax syntax_pos rustc_errors
+DEPS_rustc_privacy := rustc log syntax syntax_pos
+DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \
                     log syntax serialize rustc_llvm rustc_platform_intrinsics \
-                    rustc_const_math rustc_const_eval rustc_incremental
-DEPS_rustc_incremental := rbml rustc serialize rustc_data_structures
-DEPS_rustc_save_analysis := rustc log syntax serialize
-DEPS_rustc_typeck := rustc syntax rustc_platform_intrinsics rustc_const_math \
-                     rustc_const_eval
+                    rustc_const_math rustc_const_eval rustc_incremental rustc_errors syntax_pos
+DEPS_rustc_incremental := rbml rustc syntax_pos serialize rustc_data_structures
+DEPS_rustc_save_analysis := rustc log syntax syntax_pos serialize
+DEPS_rustc_typeck := rustc syntax syntax_pos rustc_platform_intrinsics rustc_const_math \
+                     rustc_const_eval rustc_errors
 
 DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \
-                test rustc_lint rustc_const_eval
+                test rustc_lint rustc_const_eval syntax_pos
 
 
 TOOL_DEPS_compiletest := test getopts log serialize
index 2bdbef35badf90110721e643523465b2dd03eb5c..2298565221072acf3e36dc9a4ff148dc16a40379 100644 (file)
@@ -27,31 +27,36 @@ endif
 
 define DEF_LLVM_RULES
 
+ifeq ($(1),$$(CFG_BUILD))
+LLVM_DEPS_TARGET_$(1) := $$(LLVM_DEPS)
+else
+LLVM_DEPS_TARGET_$(1) := $$(LLVM_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD))
+endif
+
 # If CFG_LLVM_ROOT is defined then we don't build LLVM ourselves
 ifeq ($(CFG_LLVM_ROOT),)
 
 LLVM_STAMP_$(1) = $$(CFG_LLVM_BUILD_DIR_$(1))/llvm-auto-clean-stamp
+LLVM_DONE_$(1) = $$(CFG_LLVM_BUILD_DIR_$(1))/llvm-finished-building
 
-ifeq ($$(findstring msvc,$(1)),msvc)
+$$(LLVM_CONFIG_$(1)): $$(LLVM_DONE_$(1))
 
-$$(LLVM_CONFIG_$(1)): $$(LLVM_DEPS) $$(LLVM_STAMP_$(1))
+$$(LLVM_DONE_$(1)): $$(LLVM_DEPS_TARGET_$(1)) $$(LLVM_STAMP_$(1))
        @$$(call E, cmake: llvm)
+ifeq ($$(findstring msvc,$(1)),msvc)
        $$(Q)$$(CFG_CMAKE) --build $$(CFG_LLVM_BUILD_DIR_$(1)) \
                --config $$(LLVM_BUILD_CONFIG_MODE)
-       $$(Q)touch $$(LLVM_CONFIG_$(1))
+else
+       $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1))
+endif
+       $$(Q)touch $$@
 
+ifeq ($$(findstring msvc,$(1)),msvc)
 clean-llvm$(1):
-
 else
-
-$$(LLVM_CONFIG_$(1)): $$(LLVM_DEPS) $$(LLVM_STAMP_$(1))
-       @$$(call E, make: llvm)
-       $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) $$(CFG_LLVM_BUILD_ENV_$(1)) ONLY_TOOLS="$$(LLVM_TOOLS)"
-       $$(Q)touch $$(LLVM_CONFIG_$(1))
-
 clean-llvm$(1):
+       @$$(call E, clean: llvm)
        $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) clean
-
 endif
 
 else
index c47020c9f9671c576569b15c4a4ed4ed85ca3fdf..aa70f4a2985f2603abb5460991f013f57d722baf 100644 (file)
 ######################################################################
 
 # The version number
-CFG_RELEASE_NUM=1.10.0
+CFG_RELEASE_NUM=1.11.0
 
 # An optional number to put after the label, e.g. '.2' -> '-beta.2'
 # NB Make sure it starts with a dot to conform to semver pre-release
 # versions (section 9)
-CFG_PRERELEASE_VERSION=.4
+CFG_PRERELEASE_VERSION=.3
 
 # Append a version-dependent hash to each library, so we can install different
 # versions in the same place
@@ -34,7 +34,14 @@ CFG_FILENAME_EXTRA=$(shell printf '%s' $(CFG_RELEASE)$(CFG_EXTRA_FILENAME) | $(C
 # intentionally not "secure" by any definition, this is largely just a deterrent
 # from users enabling unstable features on the stable compiler.
 CFG_BOOTSTRAP_KEY=$(CFG_FILENAME_EXTRA)
+
+# The stage0 compiler needs to use the previous key recorded in src/stage0.txt,
+# except for local-rebuild when it just uses the same current key.
+ifdef CFG_ENABLE_LOCAL_REBUILD
+CFG_BOOTSTRAP_KEY_STAGE0=$(CFG_BOOTSTRAP_KEY)
+else
 CFG_BOOTSTRAP_KEY_STAGE0=$(shell grep 'rustc_key' $(S)src/stage0.txt | sed 's/rustc_key: '//)
+endif
 
 ifeq ($(CFG_RELEASE_CHANNEL),stable)
 # This is the normal semver version string, e.g. "0.12.0", "0.12.0-nightly"
@@ -152,7 +159,7 @@ ifdef CFG_ENABLE_ORBIT
 endif
 
 ifdef SAVE_TEMPS
-  CFG_RUSTC_FLAGS += --save-temps
+  CFG_RUSTC_FLAGS += -save-temps
 endif
 ifdef ASM_COMMENTS
   CFG_RUSTC_FLAGS += -Z asm-comments
@@ -526,6 +533,11 @@ ifneq ($(strip $(CFG_BUILD)),$(strip $(3)))
 CFGFLAG$(1)_T_$(2)_H_$(3) = stage1
 
 RPATH_VAR$(1)_T_$(2)_H_$(3) := $$(TARGET_RPATH_VAR1_T_$(2)_H_$$(CFG_BUILD))
+else
+ifdef CFG_ENABLE_LOCAL_REBUILD
+# Assume the local-rebuild rustc already has stage1 features too.
+CFGFLAG$(1)_T_$(2)_H_$(3) = stage1
+endif
 endif
 endif
 
index 6591812280122067c5ec3aee46686d7f4616c070..d0ab3102d7d70d82e4979463d20664fb46d3731b 100644 (file)
--- a/mk/rt.mk
+++ b/mk/rt.mk
@@ -233,35 +233,98 @@ COMPRT_DEPS := $(wildcard \
               $(S)src/compiler-rt/*/*/*/*)
 endif
 
+# compiler-rt's build system is a godawful mess. Here we figure out
+# the ridiculous platform-specific values and paths necessary to get
+# useful artifacts out of it.
+
 COMPRT_NAME_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt)
 COMPRT_LIB_$(1) := $$(RT_OUTPUT_DIR_$(1))/$$(COMPRT_NAME_$(1))
 COMPRT_BUILD_DIR_$(1) := $$(RT_OUTPUT_DIR_$(1))/compiler-rt
 
-ifeq ($$(findstring msvc,$(1)),msvc)
-$$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD))
-       @$$(call E, cmake: compiler-rt)
-       $$(Q)cd "$$(COMPRT_BUILD_DIR_$(1))"; $$(CFG_CMAKE) "$(S)src/compiler-rt" \
-               -DCMAKE_BUILD_TYPE=$$(LLVM_BUILD_CONFIG_MODE) \
-               -DLLVM_CONFIG_PATH=$$(LLVM_CONFIG_$$(CFG_BUILD)) \
-               -G"$$(CFG_CMAKE_GENERATOR)"
-       $$(Q)$$(CFG_CMAKE) --build "$$(COMPRT_BUILD_DIR_$(1))" \
-               --target lib/builtins/builtins \
-               --config $$(LLVM_BUILD_CONFIG_MODE) \
-               -- //v:m //nologo
-       $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/lib/windows/$$(LLVM_BUILD_CONFIG_MODE)/clang_rt.builtins-$$(HOST_$(1)).lib $$@
+COMPRT_ARCH_$(1) := $$(word 1,$$(subst -, ,$(1)))
+
+# All this is to figure out the path to the compiler-rt bin
+ifeq ($$(findstring windows-msvc,$(1)),windows-msvc)
+COMPRT_DIR_$(1) := windows/Release
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins-$$(patsubst i%86,i386,$$(COMPRT_ARCH_$(1)))
+endif
+
+ifeq ($$(findstring windows-gnu,$(1)),windows-gnu)
+COMPRT_DIR_$(1) := windows
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins-$$(COMPRT_ARCH_$(1))
+endif
+
+ifeq ($$(findstring darwin,$(1)),darwin)
+COMPRT_DIR_$(1) := builtins
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins_$$(patsubst i686,i386,$$(COMPRT_ARCH_$(1)))_osx
+endif
+
+ifeq ($$(findstring ios,$(1)),ios)
+COMPRT_DIR_$(1) := builtins
+COMPRT_ARCH_$(1) := $$(patsubst armv7s,armv7em,$$(COMPRT_ARCH_$(1)))
+COMPRT_LIB_NAME_$(1) := clang_rt.hard_pic_$$(COMPRT_ARCH_$(1))_macho_embedded
+ifeq ($$(COMPRT_ARCH_$(1)),aarch64)
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins_arm64_ios
+endif
+COMPRT_DEFINES_$(1) := -DCOMPILER_RT_ENABLE_IOS=ON
+endif
+
+ifndef COMPRT_DIR_$(1)
+# NB: FreeBSD and NetBSD output to "linux"...
+COMPRT_DIR_$(1) := linux
+COMPRT_ARCH_$(1) := $$(patsubst i586,i386,$$(COMPRT_ARCH_$(1)))
+
+ifeq ($$(findstring android,$(1)),android)
+ifeq ($$(findstring arm,$$(COMPRT_ARCH_$(1))),arm)
+COMPRT_ARCH_$(1) := armhf
+endif
+endif
+
+ifeq ($$(findstring eabihf,$(1)),eabihf)
+ifeq ($$(findstring armv7,$(1)),)
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins-armhf
+endif
+endif
+
+ifndef COMPRT_LIB_NAME_$(1)
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins-$$(COMPRT_ARCH_$(1))
+endif
+endif
+
+
+ifeq ($$(findstring windows-gnu,$(1)),windows-gnu)
+COMPRT_LIB_FILE_$(1) := lib$$(COMPRT_LIB_NAME_$(1)).a
+endif
+
+ifeq ($$(findstring android,$(1)),android)
+ifeq ($$(findstring arm,$(1)),arm)
+COMPRT_LIB_FILE_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$$(COMPRT_LIB_NAME_$(1))-android)
+endif
+endif
+
+ifndef COMPRT_LIB_FILE_$(1)
+COMPRT_LIB_FILE_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$$(COMPRT_LIB_NAME_$(1)))
+endif
+
+COMPRT_OUTPUT_$(1) := $$(COMPRT_BUILD_DIR_$(1))/lib/$$(COMPRT_DIR_$(1))/$$(COMPRT_LIB_FILE_$(1))
+
+ifeq ($$(findstring windows-msvc,$(1)),windows-msvc)
+COMPRT_BUILD_ARGS_$(1) := //v:m //nologo
+COMPRT_BUILD_TARGET_$(1) := lib/builtins/builtins
+COMPRT_BUILD_CC_$(1) :=
 else
-COMPRT_CC_$(1) := $$(CC_$(1))
-COMPRT_AR_$(1) := $$(AR_$(1))
-# We chomp -Werror here because GCC warns about the type signature of
-# builtins not matching its own and the build fails. It's a bit hacky,
-# but what can we do, we're building libclang-rt using GCC ......
-COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -Wno-error -std=c99
-
-# FreeBSD Clang's packaging is problematic; it doesn't copy unwind.h to
-# the standard include directory. This should really be in our changes to
-# compiler-rt, but we override the CFLAGS here so there isn't much choice
-ifeq ($$(findstring freebsd,$(1)),freebsd)
-       COMPRT_CFLAGS_$(1) += -I/usr/include/c++/v1
+COMPRT_BUILD_ARGS_$(1) :=
+ifndef COMPRT_BUILD_TARGET_$(1)
+COMPRT_BUILD_TARGET_$(1) := $$(COMPRT_LIB_NAME_$(1))
+endif
+COMPRT_BUILD_CC_$(1) := -DCMAKE_C_COMPILER=$$(call FIND_COMPILER,$$(CC_$(1))) \
+                       -DCMAKE_CXX_COMPILER=$$(call FIND_COMPILER,$$(CXX_$(1)))
+
+ifeq ($$(findstring ios,$(1)),)
+COMPRT_BUILD_CC_$(1) := $$(COMPRT_BUILD_CC_$(1)) \
+                       -DCMAKE_C_FLAGS="$$(CFG_GCCISH_CFLAGS_$(1)) -Wno-error"
+endif
+
 endif
 
 ifeq ($$(findstring emscripten,$(1)),emscripten)
@@ -273,20 +336,26 @@ $$(COMPRT_LIB_$(1)):
 
 else
 
-$$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS)
-       @$$(call E, make: compiler-rt)
-       $$(Q)$$(MAKE) -C "$(S)src/compiler-rt" \
-               ProjSrcRoot="$(S)src/compiler-rt" \
-               ProjObjRoot="$$(abspath $$(COMPRT_BUILD_DIR_$(1)))" \
-               CC='$$(COMPRT_CC_$(1))' \
-               AR='$$(COMPRT_AR_$(1))' \
-               RANLIB='$$(COMPRT_AR_$(1)) s' \
-               CFLAGS="$$(COMPRT_CFLAGS_$(1))" \
-               TargetTriple=$(1) \
-               triple-builtins
-       $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/triple/builtins/libcompiler_rt.a $$@
-
-endif # if emscripten
+$$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD))
+       @$$(call E, cmake: compiler-rt)
+       $$(Q)rm -rf $$(COMPRT_BUILD_DIR_$(1))
+       $$(Q)mkdir $$(COMPRT_BUILD_DIR_$(1))
+       $$(Q)cd "$$(COMPRT_BUILD_DIR_$(1))"; \
+               $$(CFG_CMAKE) "$(S)src/compiler-rt" \
+               -DCMAKE_BUILD_TYPE=$$(LLVM_BUILD_CONFIG_MODE) \
+               -DLLVM_CONFIG_PATH=$$(LLVM_CONFIG_$$(CFG_BUILD)) \
+               -DCOMPILER_RT_DEFAULT_TARGET_TRIPLE=$(1) \
+               -DCOMPILER_RT_BUILD_SANITIZERS=OFF \
+               -DCOMPILER_RT_BUILD_EMUTLS=OFF \
+               $$(COMPRT_DEFINES_$(1)) \
+               $$(COMPRT_BUILD_CC_$(1)) \
+               -G"$$(CFG_CMAKE_GENERATOR)"
+       $$(Q)$$(CFG_CMAKE) --build "$$(COMPRT_BUILD_DIR_$(1))" \
+               --target $$(COMPRT_BUILD_TARGET_$(1)) \
+               --config $$(LLVM_BUILD_CONFIG_MODE) \
+               -- $$(COMPRT_BUILD_ARGS_$(1)) $$(MFLAGS)
+       $$(Q)cp "$$(COMPRT_OUTPUT_$(1))" $$@
+
 endif
 
 ################################################################################
@@ -310,20 +379,15 @@ ifeq ($$(findstring darwin,$$(OSTYPE_$(1))),darwin)
 $$(BACKTRACE_LIB_$(1)):
        touch $$@
 
-else
-ifeq ($$(findstring ios,$$(OSTYPE_$(1))),ios)
+else ifeq ($$(findstring ios,$$(OSTYPE_$(1))),ios)
 # See comment above
 $$(BACKTRACE_LIB_$(1)):
        touch $$@
-else
-
-ifeq ($$(findstring msvc,$(1)),msvc)
+else ifeq ($$(findstring msvc,$(1)),msvc)
 # See comment above
 $$(BACKTRACE_LIB_$(1)):
        touch $$@
-else
-
-ifeq ($$(findstring emscripten,$(1)),emscripten)
+else ifeq ($$(findstring emscripten,$(1)),emscripten)
 # FIXME: libbacktrace doesn't understand the emscripten triple
 $$(BACKTRACE_LIB_$(1)):
        touch $$@
@@ -376,10 +440,7 @@ $$(BACKTRACE_LIB_$(1)): $$(BACKTRACE_BUILD_DIR_$(1))/Makefile $$(MKFILE_DEPS)
                INCDIR=$(S)src/libbacktrace
        $$(Q)cp $$(BACKTRACE_BUILD_DIR_$(1))/.libs/libbacktrace.a $$@
 
-endif # endif for emscripten
-endif # endif for msvc
-endif # endif for ios
-endif # endif for darwin
+endif
 
 ################################################################################
 # libc/libunwind for musl
index f9ab84e3f8ccf4f2c45624cb6f737c675bc45e26..ed443147d466e3734bf5a209ff23aed0a7e166f8 100644 (file)
@@ -276,6 +276,7 @@ check-stage$(1)-T-$(2)-H-$(3)-exec: \
        check-stage$(1)-T-$(2)-H-$(3)-incremental-exec \
        check-stage$(1)-T-$(2)-H-$(3)-ui-exec \
        check-stage$(1)-T-$(2)-H-$(3)-doc-exec \
+       check-stage$(1)-T-$(2)-H-$(3)-doc-error-index-exec \
        check-stage$(1)-T-$(2)-H-$(3)-pretty-exec
 
 ifndef CFG_DISABLE_CODEGEN_TESTS
index 942f070c82fd8b8e5a928d1ef2aa0c13e98c27b2..57d644d635cf73362e0432babc5aa80887130c78 100644 (file)
@@ -50,7 +50,7 @@ compiler. What actually happens when you invoke rustbuild is:
 1. The entry point script, `src/bootstrap/bootstrap.py` is run. This script is
    responsible for downloading the stage0 compiler/Cargo binaries, and it then
    compiles the build system itself (this folder). Finally, it then invokes the
-   actual `boostrap` binary build system.
+   actual `bootstrap` binary build system.
 2. In Rust, `bootstrap` will slurp up all configuration, perform a number of
    sanity checks (compilers exist for example), and then start building the
    stage0 artifacts.
index 0ab5253ee723940a26d347fa7ce19b3d7a490d85..33de8fd0107673ccbeb97c4699df506e1f2d143c 100644 (file)
@@ -10,6 +10,7 @@
 
 import argparse
 import contextlib
+import datetime
 import hashlib
 import os
 import shutil
@@ -18,6 +19,8 @@ import sys
 import tarfile
 import tempfile
 
+from time import time
+
 
 def get(url, path, verbose=False):
     sha_url = url + ".sha256"
@@ -30,7 +33,7 @@ def get(url, path, verbose=False):
         download(sha_path, sha_url, verbose)
         download(temp_path, url, verbose)
         verify(temp_path, sha_path, verbose)
-        print("moving " + temp_path + " to " + path)
+        print("moving {} to {}".format(temp_path, path))
         shutil.move(temp_path, path)
     finally:
         delete_if_present(sha_path)
@@ -44,7 +47,7 @@ def delete_if_present(path):
 
 
 def download(path, url, verbose):
-    print("downloading " + url + " to " + path)
+    print("downloading {} to {}".format(url, path))
     # see http://serverfault.com/questions/301128/how-to-download
     if sys.platform == 'win32':
         run(["PowerShell.exe", "/nologo", "-Command",
@@ -108,14 +111,18 @@ def run(args, verbose=False):
 
 def stage0_data(rust_root):
     nightlies = os.path.join(rust_root, "src/stage0.txt")
+    data = {}
     with open(nightlies, 'r') as nightlies:
-        data = {}
-        for line in nightlies.read().split("\n"):
+        for line in nightlies:
+            line = line.rstrip()  # Strip newline character, '\n'
             if line.startswith("#") or line == '':
                 continue
             a, b = line.split(": ", 1)
             data[a] = b
-        return data
+    return data
+
+def format_build_time(duration):
+    return str(datetime.timedelta(seconds=int(duration)))
 
 class RustBuild:
     def download_stage0(self):
@@ -132,20 +139,20 @@ class RustBuild:
             if os.path.exists(self.bin_root()):
                 shutil.rmtree(self.bin_root())
             channel = self.stage0_rustc_channel()
-            filename = "rust-std-" + channel + "-" + self.build + ".tar.gz"
+            filename = "rust-std-{}-{}.tar.gz".format(channel, self.build)
             url = "https://static.rust-lang.org/dist/" + self.stage0_rustc_date()
             tarball = os.path.join(rustc_cache, filename)
             if not os.path.exists(tarball):
-                get(url + "/" + filename, tarball, verbose=self.verbose)
+                get("{}/{}".format(url, filename), tarball, verbose=self.verbose)
             unpack(tarball, self.bin_root(),
                    match="rust-std-" + self.build,
                    verbose=self.verbose)
 
-            filename = "rustc-" + channel + "-" + self.build + ".tar.gz"
+            filename = "rustc-{}-{}.tar.gz".format(channel, self.build)
             url = "https://static.rust-lang.org/dist/" + self.stage0_rustc_date()
             tarball = os.path.join(rustc_cache, filename)
             if not os.path.exists(tarball):
-                get(url + "/" + filename, tarball, verbose=self.verbose)
+                get("{}/{}".format(url, filename), tarball, verbose=self.verbose)
             unpack(tarball, self.bin_root(), match="rustc", verbose=self.verbose)
             with open(self.rustc_stamp(), 'w') as f:
                 f.write(self.stage0_rustc_date())
@@ -153,11 +160,11 @@ class RustBuild:
         if self.cargo().startswith(self.bin_root()) and \
            (not os.path.exists(self.cargo()) or self.cargo_out_of_date()):
             channel = self.stage0_cargo_channel()
-            filename = "cargo-" + channel + "-" + self.build + ".tar.gz"
+            filename = "cargo-{}-{}.tar.gz".format(channel, self.build)
             url = "https://static.rust-lang.org/cargo-dist/" + self.stage0_cargo_date()
             tarball = os.path.join(cargo_cache, filename)
             if not os.path.exists(tarball):
-                get(url + "/" + filename, tarball, verbose=self.verbose)
+                get("{}/{}".format(url, filename), tarball, verbose=self.verbose)
             unpack(tarball, self.bin_root(), match="cargo", verbose=self.verbose)
             with open(self.cargo_stamp(), 'w') as f:
                 f.write(self.stage0_cargo_date())
@@ -181,13 +188,13 @@ class RustBuild:
         return os.path.join(self.bin_root(), '.cargo-stamp')
 
     def rustc_out_of_date(self):
-        if not os.path.exists(self.rustc_stamp()):
+        if not os.path.exists(self.rustc_stamp()) or self.clean:
             return True
         with open(self.rustc_stamp(), 'r') as f:
             return self.stage0_rustc_date() != f.read()
 
     def cargo_out_of_date(self):
-        if not os.path.exists(self.cargo_stamp()):
+        if not os.path.exists(self.cargo_stamp()) or self.clean:
             return True
         with open(self.cargo_stamp(), 'r') as f:
             return self.stage0_cargo_date() != f.read()
@@ -234,8 +241,11 @@ class RustBuild:
             return ''
 
     def build_bootstrap(self):
+        build_dir = os.path.join(self.build_dir, "bootstrap")
+        if self.clean and os.path.exists(build_dir):
+            shutil.rmtree(build_dir)
         env = os.environ.copy()
-        env["CARGO_TARGET_DIR"] = os.path.join(self.build_dir, "bootstrap")
+        env["CARGO_TARGET_DIR"] = build_dir
         env["RUSTC"] = self.rustc()
         env["LD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib")
         env["DYLD_LIBRARY_PATH"] = os.path.join(self.bin_root(), "lib")
@@ -246,7 +256,7 @@ class RustBuild:
                  env)
 
     def run(self, args, env):
-        proc = subprocess.Popen(args, env = env)
+        proc = subprocess.Popen(args, env=env)
         ret = proc.wait()
         if ret != 0:
             sys.exit(ret)
@@ -261,20 +271,19 @@ class RustBuild:
         try:
             ostype = subprocess.check_output(['uname', '-s']).strip()
             cputype = subprocess.check_output(['uname', '-m']).strip()
-        except FileNotFoundError:
+        except (subprocess.CalledProcessError, WindowsError):
             if sys.platform == 'win32':
                 return 'x86_64-pc-windows-msvc'
-            else:
-                err = "uname not found"
-                if self.verbose:
-                    raise Exception(err)
-                sys.exit(err)
+            err = "uname not found"
+            if self.verbose:
+                raise Exception(err)
+            sys.exit(err)
 
         # Darwin's `uname -s` lies and always returns i386. We have to use
         # sysctl instead.
         if ostype == 'Darwin' and cputype == 'i686':
             sysctl = subprocess.check_output(['sysctl', 'hw.optional.x86_64'])
-            if sysctl.contains(': 1'):
+            if ': 1' in sysctl:
                 cputype = 'x86_64'
 
         # The goal here is to come up with the same triple as LLVM would,
@@ -335,11 +344,12 @@ class RustBuild:
                 raise ValueError(err)
             sys.exit(err)
 
-        return cputype + '-' + ostype
+        return "{}-{}".format(cputype, ostype)
 
 def main():
     parser = argparse.ArgumentParser(description='Build rust')
     parser.add_argument('--config')
+    parser.add_argument('--clean', action='store_true')
     parser.add_argument('-v', '--verbose', action='store_true')
 
     args = [a for a in sys.argv if a != '-h']
@@ -352,6 +362,7 @@ def main():
     rb.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
     rb.build_dir = os.path.join(os.getcwd(), "build")
     rb.verbose = args.verbose
+    rb.clean = args.clean
 
     try:
         with open(args.config or 'config.toml') as config:
@@ -367,6 +378,8 @@ def main():
     rb._rustc_channel, rb._rustc_date = data['rustc'].split('-', 1)
     rb._cargo_channel, rb._cargo_date = data['cargo'].split('-', 1)
 
+    start_time = time()
+
     # Fetch/build the bootstrap
     rb.build = rb.build_triple()
     rb.download_stage0()
@@ -385,5 +398,9 @@ def main():
     env["BOOTSTRAP_PARENT_ID"] = str(os.getpid())
     rb.run(args, env)
 
+    end_time = time()
+
+    print("Build completed in %s" % format_build_time(end_time - start_time))
+
 if __name__ == '__main__':
     main()
index d0b0f1007c6a0d5c62a10becf4f140168f9aa40f..ff0941a97dce1e3fcbfde7bdf64e79dca66ffc33 100644 (file)
@@ -57,7 +57,9 @@ pub fn find(build: &mut Build) {
         let compiler = cfg.get_compiler();
         let ar = cc2ar(compiler.path(), target);
         build.verbose(&format!("CC_{} = {:?}", target, compiler.path()));
-        build.verbose(&format!("AR_{} = {:?}", target, ar));
+        if let Some(ref ar) = ar {
+            build.verbose(&format!("AR_{} = {:?}", target, ar));
+        }
         build.cc.insert(target.to_string(), (compiler, ar));
     }
 
@@ -88,6 +90,7 @@ fn set_compiler(cfg: &mut gcc::Config,
         // compiler already takes into account the triple in question.
         t if t.contains("android") => {
             if let Some(ndk) = config.and_then(|c| c.ndk.as_ref()) {
+                let target = target.replace("armv7", "arm");
                 let compiler = format!("{}-{}", target, gnu_compiler);
                 cfg.compiler(ndk.join("bin").join(compiler));
             }
index 154d9556fd7ba2331e248ab1e9c20c7ca72757f7..0a096f8e4de417aea9560ad28f3e5c3f6a3503c4 100644 (file)
@@ -23,6 +23,9 @@ use build_helper::output;
 use bootstrap::{dylib_path, dylib_path_var};
 
 use build::{Build, Compiler, Mode};
+use build::util;
+
+const ADB_TEST_DIR: &'static str = "/data/tmp";
 
 /// Runs the `linkchecker` tool as compiled in `stage` by the `host` compiler.
 ///
@@ -88,6 +91,7 @@ pub fn compiletest(build: &Build,
                    target: &str,
                    mode: &str,
                    suite: &str) {
+    println!("Check compiletest {} ({} -> {})", suite, compiler.host, target);
     let mut cmd = build.tool_cmd(compiler, "compiletest");
 
     // compiletest currently has... a lot of arguments, so let's just pass all
@@ -105,21 +109,23 @@ pub fn compiletest(build: &Build,
     cmd.arg("--host").arg(compiler.host);
     cmd.arg("--llvm-filecheck").arg(build.llvm_filecheck(&build.config.build));
 
-    let mut flags = format!("-Crpath");
+    let mut flags = vec!["-Crpath".to_string()];
     if build.config.rust_optimize_tests {
-        flags.push_str(" -O");
+        flags.push("-O".to_string());
     }
     if build.config.rust_debuginfo_tests {
-        flags.push_str(" -g");
+        flags.push("-g".to_string());
     }
 
-    cmd.arg("--host-rustcflags").arg(&flags);
-
-    let linkflag = format!("-Lnative={}", build.test_helpers_out(target).display());
-    cmd.arg("--target-rustcflags").arg(format!("{} {}", flags, linkflag));
+    let mut hostflags = build.rustc_flags(&compiler.host);
+    hostflags.extend(flags.clone());
+    cmd.arg("--host-rustcflags").arg(hostflags.join(" "));
 
-    // FIXME: needs android support
-    cmd.arg("--android-cross-path").arg("");
+    let mut targetflags = build.rustc_flags(&target);
+    targetflags.extend(flags);
+    targetflags.push(format!("-Lnative={}",
+                             build.test_helpers_out(target).display()));
+    cmd.arg("--target-rustcflags").arg(targetflags.join(" "));
 
     // FIXME: CFG_PYTHON should probably be detected more robustly elsewhere
     let python_default = "python";
@@ -180,6 +186,16 @@ pub fn compiletest(build: &Build,
     }
     build.add_bootstrap_key(compiler, &mut cmd);
 
+    cmd.arg("--adb-path").arg("adb");
+    cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
+    if target.contains("android") {
+        // Assume that cc for this target comes from the android sysroot
+        cmd.arg("--android-cross-path")
+           .arg(build.cc(target).parent().unwrap().parent().unwrap());
+    } else {
+        cmd.arg("--android-cross-path").arg("");
+    }
+
     build.run(&mut cmd);
 }
 
@@ -302,7 +318,97 @@ pub fn krate(build: &Build,
     let mut dylib_path = dylib_path();
     dylib_path.insert(0, build.sysroot_libdir(compiler, target));
     cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
-    cargo.args(&build.flags.args);
 
-    build.run(&mut cargo);
+    if target.contains("android") {
+        build.run(cargo.arg("--no-run"));
+        krate_android(build, compiler, target, mode);
+    } else {
+        cargo.args(&build.flags.args);
+        build.run(&mut cargo);
+    }
+}
+
+fn krate_android(build: &Build,
+                 compiler: &Compiler,
+                 target: &str,
+                 mode: Mode) {
+    let mut tests = Vec::new();
+    let out_dir = build.cargo_out(compiler, mode, target);
+    find_tests(&out_dir, target, &mut tests);
+    find_tests(&out_dir.join("deps"), target, &mut tests);
+
+    for test in tests {
+        build.run(Command::new("adb").arg("push").arg(&test).arg(ADB_TEST_DIR));
+
+        let test_file_name = test.file_name().unwrap().to_string_lossy();
+        let log = format!("{}/check-stage{}-T-{}-H-{}-{}.log",
+                          ADB_TEST_DIR,
+                          compiler.stage,
+                          target,
+                          compiler.host,
+                          test_file_name);
+        let program = format!("(cd {dir}; \
+                                LD_LIBRARY_PATH=./{target} ./{test} \
+                                    --logfile {log} \
+                                    {args})",
+                              dir = ADB_TEST_DIR,
+                              target = target,
+                              test = test_file_name,
+                              log = log,
+                              args = build.flags.args.join(" "));
+
+        let output = output(Command::new("adb").arg("shell").arg(&program));
+        println!("{}", output);
+        build.run(Command::new("adb")
+                          .arg("pull")
+                          .arg(&log)
+                          .arg(build.out.join("tmp")));
+        build.run(Command::new("adb").arg("shell").arg("rm").arg(&log));
+        if !output.contains("result: ok") {
+            panic!("some tests failed");
+        }
+    }
+}
+
+fn find_tests(dir: &Path,
+              target: &str,
+              dst: &mut Vec<PathBuf>) {
+    for e in t!(dir.read_dir()).map(|e| t!(e)) {
+        let file_type = t!(e.file_type());
+        if !file_type.is_file() {
+            continue
+        }
+        let filename = e.file_name().into_string().unwrap();
+        if (target.contains("windows") && filename.ends_with(".exe")) ||
+           (!target.contains("windows") && !filename.contains(".")) {
+            dst.push(e.path());
+        }
+    }
+}
+
+pub fn android_copy_libs(build: &Build,
+                         compiler: &Compiler,
+                         target: &str) {
+    println!("Android copy libs to emulator ({})", target);
+    build.run(Command::new("adb").arg("remount"));
+    build.run(Command::new("adb").args(&["shell", "rm", "-r", ADB_TEST_DIR]));
+    build.run(Command::new("adb").args(&["shell", "mkdir", ADB_TEST_DIR]));
+    build.run(Command::new("adb")
+                      .arg("push")
+                      .arg(build.src.join("src/etc/adb_run_wrapper.sh"))
+                      .arg(ADB_TEST_DIR));
+
+    let target_dir = format!("{}/{}", ADB_TEST_DIR, target);
+    build.run(Command::new("adb").args(&["shell", "mkdir", &target_dir[..]]));
+
+    for f in t!(build.sysroot_libdir(compiler, target).read_dir()) {
+        let f = t!(f);
+        let name = f.file_name().into_string().unwrap();
+        if util::is_dylib(&name) {
+            build.run(Command::new("adb")
+                              .arg("push")
+                              .arg(f.path())
+                              .arg(&target_dir));
+        }
+    }
 }
index 1d407c941323545a9745c5e794de5ff5478536de..91334bdb91e2251d3bb635ae3aaf54bda5241c4f 100644 (file)
@@ -21,6 +21,9 @@ use std::path::Path;
 use build::Build;
 
 pub fn clean(build: &Build) {
+    rm_rf(build, "tmp".as_ref());
+    rm_rf(build, &build.out.join("tmp"));
+
     for host in build.config.host.iter() {
 
         let out = build.out.join(host);
index 3c35b9a95169a908478b0fcf9ce0d7a156f4e775..498196e9b6dfc12454274e75cd695c2ad2a04b65 100644 (file)
@@ -65,8 +65,9 @@ pub struct Config {
     pub build: String,
     pub host: Vec<String>,
     pub target: Vec<String>,
-    pub rustc: Option<String>,
-    pub cargo: Option<String>,
+    pub rustc: Option<PathBuf>,
+    pub cargo: Option<PathBuf>,
+    pub local_rebuild: bool,
 
     // libstd features
     pub debug_jemalloc: bool,
@@ -207,8 +208,8 @@ impl Config {
                 config.target.push(target.clone());
             }
         }
-        config.rustc = build.rustc;
-        config.cargo = build.cargo;
+        config.rustc = build.rustc.map(PathBuf::from);
+        config.cargo = build.cargo.map(PathBuf::from);
         set(&mut config.compiler_docs, build.compiler_docs);
         set(&mut config.docs, build.docs);
 
@@ -315,6 +316,7 @@ impl Config {
                 ("RPATH", self.rust_rpath),
                 ("OPTIMIZE_TESTS", self.rust_optimize_tests),
                 ("DEBUGINFO_TESTS", self.rust_debuginfo_tests),
+                ("LOCAL_REBUILD", self.local_rebuild),
             }
 
             match key {
@@ -366,17 +368,21 @@ impl Config {
                     target.ndk = Some(PathBuf::from(value));
                 }
                 "CFG_I686_LINUX_ANDROID_NDK" if value.len() > 0 => {
-                    let target = "i686-linux-androideabi".to_string();
+                    let target = "i686-linux-android".to_string();
                     let target = self.target_config.entry(target)
                                      .or_insert(Target::default());
                     target.ndk = Some(PathBuf::from(value));
                 }
                 "CFG_AARCH64_LINUX_ANDROID_NDK" if value.len() > 0 => {
-                    let target = "aarch64-linux-androideabi".to_string();
+                    let target = "aarch64-linux-android".to_string();
                     let target = self.target_config.entry(target)
                                      .or_insert(Target::default());
                     target.ndk = Some(PathBuf::from(value));
                 }
+                "CFG_LOCAL_RUST_ROOT" if value.len() > 0 => {
+                    self.rustc = Some(PathBuf::from(value).join("bin/rustc"));
+                    self.cargo = Some(PathBuf::from(value).join("bin/cargo"));
+                }
                 _ => {}
             }
         }
index 088e89b658d46699d0d6ff920852b66c44f5043b..6eed7eaf206f4631f584604a5aefa11d70329041 100644 (file)
@@ -135,7 +135,6 @@ pub fn rustc(build: &Build, stage: u32, host: &str) {
 
     // Prepare the overlay which is part of the tarball but won't actually be
     // installed
-    t!(fs::create_dir_all(&overlay));
     let cp = |file: &str| {
         install(&build.src.join(file), &overlay, 0o644);
     };
@@ -199,7 +198,6 @@ pub fn rustc(build: &Build, stage: u32, host: &str) {
 
         // Copy runtime DLLs needed by the compiler
         if libdir != "bin" {
-            t!(fs::create_dir_all(image.join(libdir)));
             for entry in t!(src.join(libdir).read_dir()).map(|e| t!(e)) {
                 let name = entry.file_name();
                 if let Some(s) = name.to_str() {
@@ -221,7 +219,6 @@ pub fn rustc(build: &Build, stage: u32, host: &str) {
         let cp = |file: &str| {
             install(&build.src.join(file), &image.join("share/doc/rust"), 0o644);
         };
-        t!(fs::create_dir_all(&image.join("share/doc/rust")));
         cp("COPYRIGHT");
         cp("LICENSE-APACHE");
         cp("LICENSE-MIT");
@@ -289,6 +286,7 @@ pub fn std(build: &Build, compiler: &Compiler, target: &str) {
 
 fn install(src: &Path, dstdir: &Path, perms: u32) {
     let dst = dstdir.join(src.file_name().unwrap());
+    t!(fs::create_dir_all(dstdir));
     t!(fs::copy(src, &dst));
     chmod(&dst, perms);
 }
index ebc05c5f61c50784a9eccd3a7d2c88b760582f57..195d1bc90c655fb191f832aeaa46c6bf8e3eb45a 100644 (file)
@@ -119,7 +119,7 @@ pub struct Build {
     lldb_python_dir: Option<String>,
 
     // Runtime state filled in later on
-    cc: HashMap<String, (gcc::Tool, PathBuf)>,
+    cc: HashMap<String, (gcc::Tool, Option<PathBuf>)>,
     cxx: HashMap<String, gcc::Tool>,
     compiler_rt_built: RefCell<HashMap<String, PathBuf>>,
 }
@@ -128,6 +128,7 @@ pub struct Build {
 ///
 /// These entries currently correspond to the various output directories of the
 /// build system, with each mod generating output in a different directory.
+#[derive(Clone, Copy)]
 pub enum Mode {
     /// This cargo is going to build the standard library, placing output in the
     /// "stageN-std" directory.
@@ -383,8 +384,7 @@ impl Build {
                                        "ui", "ui");
                 }
                 CheckDebuginfo { compiler } => {
-                    if target.target.contains("msvc") ||
-                       target.target.contains("android") {
+                    if target.target.contains("msvc") {
                         // nothing to do
                     } else if target.target.contains("apple") {
                         check::compiletest(self, &compiler, target.target,
@@ -434,8 +434,14 @@ impl Build {
                                            target.target);
                 }
 
+                AndroidCopyLibs { compiler } => {
+                    check::android_copy_libs(self, &compiler, target.target);
+                }
+
+                // pseudo-steps
                 Dist { .. } |
-                Doc { .. } | // pseudo-steps
+                Doc { .. } |
+                CheckTarget { .. } |
                 Check { .. } => {}
             }
         }
@@ -510,6 +516,14 @@ impl Build {
              .arg("-j").arg(self.jobs().to_string())
              .arg("--target").arg(target);
 
+        let stage;
+        if compiler.stage == 0 && self.config.local_rebuild {
+            // Assume the local-rebuild rustc already has stage1 features.
+            stage = 1;
+        } else {
+            stage = compiler.stage;
+        }
+
         // Customize the compiler we're running. Specify the compiler to cargo
         // as our shim and then pass it some various options used to configure
         // how the actual compiler itself is called.
@@ -518,7 +532,7 @@ impl Build {
         // src/bootstrap/{rustc,rustdoc.rs}
         cargo.env("RUSTC", self.out.join("bootstrap/debug/rustc"))
              .env("RUSTC_REAL", self.compiler_path(compiler))
-             .env("RUSTC_STAGE", compiler.stage.to_string())
+             .env("RUSTC_STAGE", stage.to_string())
              .env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string())
              .env("RUSTC_CODEGEN_UNITS",
                   self.config.rust_codegen_units.to_string())
@@ -541,7 +555,7 @@ impl Build {
         // FIXME: the guard against msvc shouldn't need to be here
         if !target.contains("msvc") {
             cargo.env(format!("CC_{}", target), self.cc(target))
-                 .env(format!("AR_{}", target), self.ar(target))
+                 .env(format!("AR_{}", target), self.ar(target).unwrap()) // only msvc is None
                  .env(format!("CFLAGS_{}", target), self.cflags(target).join(" "));
         }
 
@@ -744,7 +758,7 @@ impl Build {
         // In stage0 we're using a previously released stable compiler, so we
         // use the stage0 bootstrap key. Otherwise we use our own build's
         // bootstrap key.
-        let bootstrap_key = if compiler.is_snapshot(self) {
+        let bootstrap_key = if compiler.is_snapshot(self) && !self.config.local_rebuild {
             &self.bootstrap_key_stage0
         } else {
             &self.bootstrap_key
@@ -817,8 +831,8 @@ impl Build {
     }
 
     /// Returns the path to the `ar` archive utility for the target specified.
-    fn ar(&self, target: &str) -> &Path {
-        &self.cc[target].1
+    fn ar(&self, target: &str) -> Option<&Path> {
+        self.cc[target].1.as_ref().map(|p| &**p)
     }
 
     /// Returns the path to the C++ compiler for the target specified, may panic
index 5691b2da6a448856b76443106c817bfd606ea1d0..f6030cfd090d223e314bd93592a04d5e4bca7d62 100644 (file)
 
 use std::path::Path;
 use std::process::Command;
-use std::fs;
+use std::fs::{self, File};
 
 use build_helper::output;
 use cmake;
 use gcc;
 
 use build::Build;
-use build::util::{exe, staticlib, up_to_date};
+use build::util::{staticlib, up_to_date};
 
 /// Compile LLVM for `target`.
 pub fn llvm(build: &Build, target: &str) {
@@ -43,12 +43,14 @@ pub fn llvm(build: &Build, target: &str) {
     // artifacts are missing) then we keep going, otherwise we bail out.
     let dst = build.llvm_out(target);
     let stamp = build.src.join("src/rustllvm/llvm-auto-clean-trigger");
-    let llvm_config = dst.join("bin").join(exe("llvm-config", target));
+    let done_stamp = dst.join("llvm-finished-building");
     build.clear_if_dirty(&dst, &stamp);
-    if fs::metadata(llvm_config).is_ok() {
+    if fs::metadata(&done_stamp).is_ok() {
         return
     }
 
+    println!("Building LLVM for {}", target);
+
     let _ = fs::remove_dir_all(&dst.join("build"));
     t!(fs::create_dir_all(&dst.join("build")));
     let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};
@@ -111,6 +113,8 @@ pub fn llvm(build: &Build, target: &str) {
     //        tools. Figure out how to filter them down and only build the right
     //        tools and libs on all platforms.
     cfg.build();
+
+    t!(File::create(&done_stamp));
 }
 
 fn check_llvm_version(build: &Build, llvm_config: &Path) {
@@ -135,27 +139,66 @@ pub fn compiler_rt(build: &Build, target: &str) {
     let dst = build.compiler_rt_out(target);
     let arch = target.split('-').next().unwrap();
     let mode = if build.config.rust_optimize {"Release"} else {"Debug"};
+
+    let build_llvm_config = build.llvm_config(&build.config.build);
+    let mut cfg = cmake::Config::new(build.src.join("src/compiler-rt"));
+    cfg.target(target)
+       .host(&build.config.build)
+       .out_dir(&dst)
+       .profile(mode)
+       .define("LLVM_CONFIG_PATH", build_llvm_config)
+       .define("COMPILER_RT_DEFAULT_TARGET_TRIPLE", target)
+       .define("COMPILER_RT_BUILD_SANITIZERS", "OFF")
+       .define("COMPILER_RT_BUILD_EMUTLS", "OFF")
+       // inform about c/c++ compilers, the c++ compiler isn't actually used but
+       // it's needed to get the initial configure to work on all platforms.
+       .define("CMAKE_C_COMPILER", build.cc(target))
+       .define("CMAKE_CXX_COMPILER", build.cc(target));
+
     let (dir, build_target, libname) = if target.contains("linux") ||
                                           target.contains("freebsd") ||
                                           target.contains("netbsd") {
-        let os = if target.contains("android") {"-android"} else {""};
-        let arch = if arch.starts_with("arm") && target.contains("eabihf") {
-            "armhf"
+        let os_extra = if target.contains("android") && target.contains("arm") {
+            "-android"
         } else {
-            arch
+            ""
+        };
+        let builtins_arch = match arch {
+            "i586" => "i386",
+            "arm" | "armv7" if target.contains("android") => "armhf",
+            "arm" if target.contains("eabihf") => "armhf",
+            _ => arch,
+        };
+        let target = format!("clang_rt.builtins-{}", builtins_arch);
+        ("linux".to_string(),
+         target.clone(),
+         format!("{}{}", target, os_extra))
+    } else if target.contains("apple-darwin") {
+        let builtins_arch = match arch {
+            "i686" => "i386",
+            _ => arch,
+        };
+        let target = format!("clang_rt.builtins_{}_osx", builtins_arch);
+        ("builtins".to_string(), target.clone(), target)
+    } else if target.contains("apple-ios") {
+        cfg.define("COMPILER_RT_ENABLE_IOS", "ON");
+        let target = match arch {
+            "armv7s" => "hard_pic_armv7em_macho_embedded".to_string(),
+            "aarch64" => "builtins_arm64_ios".to_string(),
+            _ => format!("hard_pic_{}_macho_embedded", arch),
         };
-        let target = format!("clang_rt.builtins-{}{}", arch, os);
-        ("linux".to_string(), target.clone(), target)
-    } else if target.contains("darwin") {
-        let target = format!("clang_rt.builtins_{}_osx", arch);
         ("builtins".to_string(), target.clone(), target)
     } else if target.contains("windows-gnu") {
         let target = format!("clang_rt.builtins-{}", arch);
         ("windows".to_string(), target.clone(), target)
     } else if target.contains("windows-msvc") {
+        let builtins_arch = match arch {
+            "i586" | "i686" => "i386",
+            _ => arch,
+        };
         (format!("windows/{}", mode),
          "lib/builtins/builtins".to_string(),
-         format!("clang_rt.builtins-{}", arch.replace("i686", "i386")))
+         format!("clang_rt.builtins-{}", builtins_arch))
     } else {
         panic!("can't get os from target: {}", target)
     };
@@ -168,21 +211,7 @@ pub fn compiler_rt(build: &Build, target: &str) {
     }
     let _ = fs::remove_dir_all(&dst);
     t!(fs::create_dir_all(&dst));
-    let build_llvm_config = build.llvm_config(&build.config.build);
-    let mut cfg = cmake::Config::new(build.src.join("src/compiler-rt"));
-    cfg.target(target)
-       .host(&build.config.build)
-       .out_dir(&dst)
-       .profile(mode)
-       .define("LLVM_CONFIG_PATH", build_llvm_config)
-       .define("COMPILER_RT_DEFAULT_TARGET_TRIPLE", target)
-       .define("COMPILER_RT_BUILD_SANITIZERS", "OFF")
-       .define("COMPILER_RT_BUILD_EMUTLS", "OFF")
-       // inform about c/c++ compilers, the c++ compiler isn't actually used but
-       // it's needed to get the initial configure to work on all platforms.
-       .define("CMAKE_C_COMPILER", build.cc(target))
-       .define("CMAKE_CXX_COMPILER", build.cc(target))
-       .build_target(&build_target);
+    cfg.build_target(&build_target);
     cfg.build();
 }
 
index a290527742982421e067f85001d1b7e8199a6a96..5eced00e13973e35109cb32856aa732b16cd9696 100644 (file)
@@ -70,7 +70,9 @@ pub fn check(build: &mut Build) {
     // also build some C++ shims for LLVM so we need a C++ compiler.
     for target in build.config.target.iter() {
         need_cmd(build.cc(target).as_ref());
-        need_cmd(build.ar(target).as_ref());
+        if let Some(ar) = build.ar(target) {
+            need_cmd(ar.as_ref());
+        }
     }
     for host in build.config.host.iter() {
         need_cmd(build.cxx(host).as_ref());
@@ -137,6 +139,10 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
 ");
             }
         }
+
+        if target.contains("arm-linux-android") {
+            need_cmd("adb".as_ref());
+        }
     }
 
     for host in build.flags.host.iter() {
index 742fd8575bb809d6abb264ed1c3b49e31432733e..7cbbd6740a2657a5cbbfda23eea3a572fb30d936 100644 (file)
@@ -102,6 +102,7 @@ macro_rules! targets {
             // Steps for running tests. The 'check' target is just a pseudo
             // target to depend on a bunch of others.
             (check, Check { stage: u32, compiler: Compiler<'a> }),
+            (check_target, CheckTarget { stage: u32, compiler: Compiler<'a> }),
             (check_linkcheck, CheckLinkcheck { stage: u32 }),
             (check_cargotest, CheckCargoTest { stage: u32 }),
             (check_tidy, CheckTidy { stage: u32 }),
@@ -138,6 +139,9 @@ macro_rules! targets {
             (dist_mingw, DistMingw { _dummy: () }),
             (dist_rustc, DistRustc { stage: u32 }),
             (dist_std, DistStd { compiler: Compiler<'a> }),
+
+            // Misc targets
+            (android_copy_libs, AndroidCopyLibs { compiler: Compiler<'a> }),
         }
     }
 }
@@ -382,37 +386,80 @@ impl<'a> Step<'a> {
                      self.doc_error_index(stage)]
             }
             Source::Check { stage, compiler } => {
-                vec![
+                // Check is just a pseudo step which means check all targets,
+                // so just depend on checking all targets.
+                build.config.target.iter().map(|t| {
+                    self.target(t).check_target(stage, compiler)
+                }).collect()
+            }
+            Source::CheckTarget { stage, compiler } => {
+                // CheckTarget here means run all possible test suites for this
+                // target. Most of the time, however, we can't actually run
+                // anything if we're not the build triple as we could be cross
+                // compiling.
+                //
+                // As a result, the base set of targets here is quite stripped
+                // down from the standard set of targets. These suites have
+                // their own internal logic to run in cross-compiled situations
+                // if they'll run at all. For example compiletest knows that
+                // when testing Android targets we ship artifacts to the
+                // emulator.
+                //
+                // When in doubt the rule of thumb for adding to this list is
+                // "should this test suite run on the android bot?"
+                let mut base = vec![
                     self.check_rpass(compiler),
-                    self.check_rpass_full(compiler),
                     self.check_rfail(compiler),
-                    self.check_rfail_full(compiler),
-                    self.check_cfail(compiler),
-                    self.check_cfail_full(compiler),
-                    self.check_pfail(compiler),
-                    self.check_incremental(compiler),
-                    self.check_ui(compiler),
                     self.check_crate_std(compiler),
                     self.check_crate_test(compiler),
-                    self.check_crate_rustc(compiler),
-                    self.check_codegen(compiler),
-                    self.check_codegen_units(compiler),
                     self.check_debuginfo(compiler),
-                    self.check_rustdoc(compiler),
-                    self.check_pretty(compiler),
-                    self.check_pretty_rpass(compiler),
-                    self.check_pretty_rpass_full(compiler),
-                    self.check_pretty_rfail(compiler),
-                    self.check_pretty_rfail_full(compiler),
-                    self.check_pretty_rpass_valgrind(compiler),
-                    self.check_rpass_valgrind(compiler),
-                    self.check_error_index(compiler),
-                    self.check_docs(compiler),
-                    self.check_rmake(compiler),
-                    self.check_linkcheck(stage),
-                    self.check_tidy(stage),
                     self.dist(stage),
-                ]
+                ];
+
+                // If we're testing the build triple, then we know we can
+                // actually run binaries and such, so we run all possible tests
+                // that we know about.
+                if self.target == build.config.build {
+                    base.extend(vec![
+                        // docs-related
+                        self.check_docs(compiler),
+                        self.check_error_index(compiler),
+                        self.check_rustdoc(compiler),
+
+                        // UI-related
+                        self.check_cfail(compiler),
+                        self.check_pfail(compiler),
+                        self.check_ui(compiler),
+
+                        // codegen-related
+                        self.check_incremental(compiler),
+                        self.check_codegen(compiler),
+                        self.check_codegen_units(compiler),
+
+                        // misc compiletest-test suites
+                        self.check_rpass_full(compiler),
+                        self.check_rfail_full(compiler),
+                        self.check_cfail_full(compiler),
+                        self.check_pretty_rpass_full(compiler),
+                        self.check_pretty_rfail_full(compiler),
+                        self.check_rpass_valgrind(compiler),
+                        self.check_rmake(compiler),
+
+                        // crates
+                        self.check_crate_rustc(compiler),
+
+                        // pretty
+                        self.check_pretty(compiler),
+                        self.check_pretty_rpass(compiler),
+                        self.check_pretty_rfail(compiler),
+                        self.check_pretty_rpass_valgrind(compiler),
+
+                        // misc
+                        self.check_linkcheck(stage),
+                        self.check_tidy(stage),
+                    ]);
+                }
+                return base
             }
             Source::CheckLinkcheck { stage } => {
                 vec![self.tool_linkchecker(stage), self.doc(stage)]
@@ -437,16 +484,20 @@ impl<'a> Step<'a> {
             Source::CheckCFail { compiler } |
             Source::CheckRPassValgrind { compiler } |
             Source::CheckRPass { compiler } => {
-                vec![
+                let mut base = vec![
                     self.libtest(compiler),
-                    self.tool_compiletest(compiler.stage),
+                    self.target(compiler.host).tool_compiletest(compiler.stage),
                     self.test_helpers(()),
-                ]
+                ];
+                if self.target.contains("android") {
+                    base.push(self.android_copy_libs(compiler));
+                }
+                base
             }
             Source::CheckDebuginfo { compiler } => {
                 vec![
                     self.libtest(compiler),
-                    self.tool_compiletest(compiler.stage),
+                    self.target(compiler.host).tool_compiletest(compiler.stage),
                     self.test_helpers(()),
                     self.debugger_scripts(compiler.stage),
                 ]
@@ -459,13 +510,14 @@ impl<'a> Step<'a> {
             Source::CheckPrettyRPassValgrind { compiler } |
             Source::CheckRMake { compiler } => {
                 vec![self.librustc(compiler),
-                     self.tool_compiletest(compiler.stage)]
+                     self.target(compiler.host).tool_compiletest(compiler.stage)]
             }
             Source::CheckDocs { compiler } => {
                 vec![self.libstd(compiler)]
             }
             Source::CheckErrorIndex { compiler } => {
-                vec![self.libstd(compiler), self.tool_error_index(compiler.stage)]
+                vec![self.libstd(compiler),
+                     self.target(compiler.host).tool_error_index(compiler.stage)]
             }
             Source::CheckCrateStd { compiler } => {
                 vec![self.libtest(compiler)]
@@ -529,6 +581,10 @@ impl<'a> Step<'a> {
                 }
                 return base
             }
+
+            Source::AndroidCopyLibs { compiler } => {
+                vec![self.libtest(compiler)]
+            }
         }
     }
 }
index 1f3ea8f19bb020ae84d99c0c0ed95f29d9207742..c657785d78b6e31f094536b736f7275289c0039a 100644 (file)
@@ -25,6 +25,11 @@ all:
 clean:
        $(Q)$(BOOTSTRAP) --clean
 
+rustc-stage1:
+       $(Q)$(BOOTSTRAP) --step libtest --stage 1
+rustc-stage2:
+       $(Q)$(BOOTSTRAP) --step libtest --stage 2
+
 docs: doc
 doc:
        $(Q)$(BOOTSTRAP) --step doc
index 8e1da69cf02e752e2a4c5622549092408b37bd70..838cc4f07a9a175397c253dbec2ab730413926b8 100644 (file)
@@ -39,9 +39,11 @@ pub fn gnu_target(target: &str) -> String {
     }
 }
 
-pub fn cc2ar(cc: &Path, target: &str) -> PathBuf {
-    if target.contains("musl") || target.contains("msvc") {
-        PathBuf::from("ar")
+pub fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
+    if target.contains("msvc") {
+        None
+    } else if target.contains("musl") {
+        Some(PathBuf::from("ar"))
     } else {
         let parent = cc.parent().unwrap();
         let file = cc.file_name().unwrap().to_str().unwrap();
@@ -49,10 +51,10 @@ pub fn cc2ar(cc: &Path, target: &str) -> PathBuf {
             if let Some(idx) = file.rfind(suffix) {
                 let mut file = file[..idx].to_owned();
                 file.push_str("ar");
-                return parent.join(&file);
+                return Some(parent.join(&file));
             }
         }
-        parent.join(file)
+        Some(parent.join(file))
     }
 }
 
index 6f0b8674631b7652d0696425ab963084e489a2ee..c60c246efa7443d9113921078e56435c5a24f8b8 100644 (file)
@@ -66,8 +66,10 @@ if (NOT COMPILER_RT_STANDALONE_BUILD)
   # Windows where we need to use clang-cl instead.
   if(NOT MSVC)
     set(COMPILER_RT_TEST_COMPILER ${LLVM_RUNTIME_OUTPUT_INTDIR}/clang)
+    set(COMPILER_RT_TEST_CXX_COMPILER ${LLVM_RUNTIME_OUTPUT_INTDIR}/clang++)
   else()
     set(COMPILER_RT_TEST_COMPILER ${LLVM_RUNTIME_OUTPUT_INTDIR}/clang.exe)
+    set(COMPILER_RT_TEST_CXX_COMPILER ${LLVM_RUNTIME_OUTPUT_INTDIR}/clang++.exe)
   endif()
 else()
   # Take output dir and install path from the user.
@@ -81,6 +83,7 @@ else()
   option(COMPILER_RT_ENABLE_WERROR "Fail and stop if warning is triggered" OFF)
   # Use a host compiler to compile/link tests.
   set(COMPILER_RT_TEST_COMPILER ${CMAKE_C_COMPILER} CACHE PATH "Compiler to use for testing")
+  set(COMPILER_RT_TEST_CXX_COMPILER ${CMAKE_CXX_COMPILER} CACHE PATH "C++ Compiler to use for testing")
 
   if (NOT LLVM_CONFIG_PATH)
     find_program(LLVM_CONFIG_PATH "llvm-config"
@@ -189,6 +192,8 @@ else()
 endif()
 
 option(COMPILER_RT_DEBUG "Build runtimes with full debug info" OFF)
+option(COMPILER_RT_EXTERNALIZE_DEBUGINFO
+  "Generate dSYM files and strip executables and libraries (Darwin Only)" OFF)
 # COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in.
 pythonize_bool(COMPILER_RT_DEBUG)
 
@@ -226,6 +231,7 @@ append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COM
 append_list_if(COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG -fno-stack-protector SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_FNO_SANITIZE_SAFE_STACK_FLAG -fno-sanitize=safe-stack SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANITIZER_COMMON_CFLAGS)
+append_list_if(COMPILER_RT_HAS_FVISIBILITY_INLINES_HIDDEN_FLAG -fvisibility-inlines-hidden SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG -fno-function-sections SANITIZER_COMMON_CFLAGS)
 append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS)
 
index 284cf1de70d13954f263c86e9f2b1a7ef6cd29dc..1ab590e34a8899715279067d31b09e2a375bda55 100644 (file)
@@ -19,7 +19,7 @@ function(add_compiler_rt_object_libraries name)
       set(libname "${name}.${os}")
       set(libnames ${libnames} ${libname})
       set(extra_cflags_${libname} ${DARWIN_${os}_CFLAGS})
-      list_union(LIB_ARCHS_${libname} DARWIN_${os}_ARCHS LIB_ARCHS)
+      list_intersect(LIB_ARCHS_${libname} DARWIN_${os}_ARCHS LIB_ARCHS)
     endforeach()
   else()
     foreach(arch ${LIB_ARCHS})
@@ -87,7 +87,7 @@ function(add_compiler_rt_runtime name type)
         set(libname "${name}_${os}_dynamic")
         set(extra_linkflags_${libname} ${DARWIN_${os}_LINKFLAGS} ${LIB_LINKFLAGS})
       endif()
-      list_union(LIB_ARCHS_${libname} DARWIN_${os}_ARCHS LIB_ARCHS)
+      list_intersect(LIB_ARCHS_${libname} DARWIN_${os}_ARCHS LIB_ARCHS)
       if(LIB_ARCHS_${libname})
         list(APPEND libnames ${libname})
         set(extra_cflags_${libname} ${DARWIN_${os}_CFLAGS} ${LIB_CFLAGS})
@@ -155,6 +155,10 @@ function(add_compiler_rt_runtime name type)
       set_target_properties(${libname} PROPERTIES
       OSX_ARCHITECTURES "${LIB_ARCHS_${libname}}")
     endif()
+
+    if(type STREQUAL "SHARED")
+      rt_externalize_debuginfo(${libname})
+    endif()
   endforeach()
   if(LIB_PARENT_TARGET)
     add_dependencies(${LIB_PARENT_TARGET} ${libnames})
@@ -284,12 +288,14 @@ macro(add_custom_libcxx name prefix)
   ExternalProject_Add(${name}
     PREFIX ${prefix}
     SOURCE_DIR ${COMPILER_RT_LIBCXX_PATH}
-    CMAKE_ARGS -DCMAKE_C_COMPILER=${COMPILER_RT_TEST_COMPILER}
-               -DCMAKE_CXX_COMPILER=${COMPILER_RT_TEST_COMPILER}
+    CMAKE_ARGS -DCMAKE_MAKE_PROGRAM:STRING=${CMAKE_MAKE_PROGRAM}
+               -DCMAKE_C_COMPILER=${COMPILER_RT_TEST_COMPILER}
+               -DCMAKE_CXX_COMPILER=${COMPILER_RT_TEST_CXX_COMPILER}
                -DCMAKE_C_FLAGS=${LIBCXX_CFLAGS}
                -DCMAKE_CXX_FLAGS=${LIBCXX_CFLAGS}
                -DCMAKE_BUILD_TYPE=Release
                -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
+               -DLLVM_PATH=${LLVM_MAIN_SRC_DIR}
     LOG_BUILD 1
     LOG_CONFIGURE 1
     LOG_INSTALL 1
@@ -309,3 +315,24 @@ macro(add_custom_libcxx name prefix)
     DEPENDS ${LIBCXX_DEPS}
     )
 endmacro()
+
+function(rt_externalize_debuginfo name)
+  if(NOT COMPILER_RT_EXTERNALIZE_DEBUGINFO)
+    return()
+  endif()
+
+  if(APPLE)
+    if(CMAKE_CXX_FLAGS MATCHES "-flto"
+      OR CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE} MATCHES "-flto")
+
+      set(lto_object ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${name}-lto.o)
+      set_property(TARGET ${name} APPEND_STRING PROPERTY
+        LINK_FLAGS " -Wl,-object_path_lto -Wl,${lto_object}")
+    endif()
+    add_custom_command(TARGET ${name} POST_BUILD
+      COMMAND xcrun dsymutil $<TARGET_FILE:${name}>
+      COMMAND xcrun strip -Sl $<TARGET_FILE:${name}>)
+  else()
+    message(FATAL_ERROR "COMPILER_RT_EXTERNALIZE_DEBUGINFO isn't implemented for non-darwin platforms!")
+  endif()
+endfunction()
index 850d109c26d9a7353732c72fcca96111a5c2601d..48f40bf4f7530a66c0c837d4840e075430e1df56 100644 (file)
@@ -24,19 +24,6 @@ function(translate_msvc_cflags out_flags msvc_flags)
   set(${out_flags} "${clang_flags}" PARENT_SCOPE)
 endfunction()
 
-if (APPLE)
-  # On Darwin if /usr/include doesn't exist, the user probably has Xcode but not
-  # the command line tools. If this is the case, we need to find the OS X
-  # sysroot to pass to clang.
-  if(NOT EXISTS /usr/include)
-    execute_process(COMMAND xcodebuild -version -sdk macosx Path
-       OUTPUT_VARIABLE OSX_SYSROOT
-       ERROR_QUIET
-       OUTPUT_STRIP_TRAILING_WHITESPACE)
-    set(OSX_SYSROOT_FLAG "-isysroot${OSX_SYSROOT}")
-  endif()
-endif()
-
 # Compile a source into an object file with COMPILER_RT_TEST_COMPILER using
 # a provided compile flags and dependenices.
 # clang_compile(<object> <source>
index c52ba09b8f2ec1f481fd1c2aa447d0583e22a15d..d20e372a810d8b040c70764c18ebea0fb6ba7359 100644 (file)
@@ -46,6 +46,11 @@ endfunction()
 # This function takes an OS and a list of architectures and identifies the
 # subset of the architectures list that the installed toolchain can target.
 function(darwin_test_archs os valid_archs)
+  if(${valid_archs})
+    message(STATUS "Using cached valid architectures for ${os}.")
+    return()
+  endif()
+
   set(archs ${ARGN})
   message(STATUS "Finding valid architectures for ${os}...")
   set(SIMPLE_CPP ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/src.cpp)
@@ -73,15 +78,20 @@ function(darwin_test_archs os valid_archs)
                 OUTPUT_VARIABLE TEST_OUTPUT)
     if(${CAN_TARGET_${os}_${arch}})
       list(APPEND working_archs ${arch})
+    else()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Testing compiler for supporting ${os}-${arch}:\n"
+        "${TEST_OUTPUT}\n")
     endif()
   endforeach()
-  set(${valid_archs} ${working_archs} PARENT_SCOPE)
+  set(${valid_archs} ${working_archs}
+    CACHE STRING "List of valid architectures for platform ${os}.")
 endfunction()
 
 # This function checks the host cpusubtype to see if it is post-haswell. Haswell
 # and later machines can run x86_64h binaries. Haswell is cpusubtype 8.
 function(darwin_filter_host_archs input output)
-  list_union(tmp_var DARWIN_osx_ARCHS ${input})
+  list_intersect(tmp_var DARWIN_osx_ARCHS ${input})
   execute_process(
     COMMAND sysctl hw.cpusubtype
     OUTPUT_VARIABLE SUBTYPE)
@@ -159,11 +169,43 @@ macro(darwin_add_builtin_library name suffix)
     "PARENT_TARGET;OS;ARCH"
     "SOURCES;CFLAGS;DEFS"
     ${ARGN})
+
+  # --- Workaround ---
+  # The REF_OS variable was introduced to workaround linking problems when
+  # compiler-rt is build for the iOS simulator.
+  #
+  # Without this workaround, trying to link compiler-rt into an executable for
+  # the iOS simulator would produce an error like
+  #
+  #   ld: warning: URGENT: building for iOS simulator, but linking in object
+  #   file built for OSX. Note: This will be an error in the future.
+  #
+  # The underlying reason is that the iOS simulator specific configuration is
+  # stored in variables named like DARWIN_iossim_SYSROOT and not
+  # DARWIN_macho_embedded_SYSROOT. Thus, with the current setup, compiler-rt
+  # would be compiled against the OS X SDK and not the iPhone Simulator SDK.
+  #
+  # As a workaround we manually override macho_embedded with iossim when
+  # accessing the DARWIN_*_SYSROOT and DARWIN_*_BUILTIN_MIN_VER_FLAG variables.
+  #
+  # This workaround probably break builds of compiler-rt for the watchOS and
+  # tvOS simulators (if they weren't broken already).
+  #
+  # See also rust-lang/rust#34617.
+  if(${LIB_OS} STREQUAL "macho_embedded")
+    set(REF_OS iossim)
+  else()
+    set(REF_OS ${LIB_OS})
+  endif()
+
   set(libname "${name}.${suffix}_${LIB_ARCH}_${LIB_OS}")
   add_library(${libname} STATIC ${LIB_SOURCES})
+  if(DARWIN_${LIB_OS}_SYSROOT)
+    set(sysroot_flag -isysroot ${DARWIN_${REF_OS}_SYSROOT})
+  endif()
   set_target_compile_flags(${libname}
-    -isysroot ${DARWIN_${LIB_OS}_SYSROOT}
-    ${DARWIN_${LIB_OS}_BUILTIN_MIN_VER_FLAG}
+    ${sysroot_flag}
+    ${DARWIN_${REF_OS}_BUILTIN_MIN_VER_FLAG}
     ${LIB_CFLAGS})
   set_property(TARGET ${libname} APPEND PROPERTY
       COMPILE_DEFINITIONS ${LIB_DEFS})
@@ -186,18 +228,22 @@ function(darwin_lipo_libs name)
     "PARENT_TARGET;OUTPUT_DIR;INSTALL_DIR"
     "LIPO_FLAGS;DEPENDS"
     ${ARGN})
-  add_custom_command(OUTPUT ${LIB_OUTPUT_DIR}/lib${name}.a
-    COMMAND ${CMAKE_COMMAND} -E make_directory ${LIB_OUTPUT_DIR}
-    COMMAND lipo -output
-            ${LIB_OUTPUT_DIR}/lib${name}.a
-            -create ${LIB_LIPO_FLAGS}
-    DEPENDS ${LIB_DEPENDS}
-    )
-  add_custom_target(${name}
-    DEPENDS ${LIB_OUTPUT_DIR}/lib${name}.a)
-  add_dependencies(${LIB_PARENT_TARGET} ${name})
-  install(FILES ${LIB_OUTPUT_DIR}/lib${name}.a
-    DESTINATION ${LIB_INSTALL_DIR})
+  if(LIB_DEPENDS AND LIB_LIPO_FLAGS)
+    add_custom_command(OUTPUT ${LIB_OUTPUT_DIR}/lib${name}.a
+      COMMAND ${CMAKE_COMMAND} -E make_directory ${LIB_OUTPUT_DIR}
+      COMMAND lipo -output
+              ${LIB_OUTPUT_DIR}/lib${name}.a
+              -create ${LIB_LIPO_FLAGS}
+      DEPENDS ${LIB_DEPENDS}
+      )
+    add_custom_target(${name}
+      DEPENDS ${LIB_OUTPUT_DIR}/lib${name}.a)
+    add_dependencies(${LIB_PARENT_TARGET} ${name})
+    install(FILES ${LIB_OUTPUT_DIR}/lib${name}.a
+      DESTINATION ${LIB_INSTALL_DIR})
+  else()
+    message(WARNING "Not generating lipo target for ${name} because no input libraries exist.")
+  endif()
 endfunction()
 
 # Filter out generic versions of routines that are re-implemented in
@@ -220,7 +266,7 @@ function(darwin_filter_builtin_sources output_var exclude_or_include excluded_li
     list(FIND ${excluded_list} ${_name_we} _found)
     if(_found ${filter_action} ${filter_value})
       list(REMOVE_ITEM intermediate ${_file})
-    elseif(${_file} MATCHES ".*/.*\\.S")
+    elseif(${_file} MATCHES ".*/.*\\.S" OR ${_file} MATCHES ".*/.*\\.c")
       get_filename_component(_name ${_file} NAME)
       string(REPLACE ".S" ".c" _cname "${_name}")
       list(REMOVE_ITEM intermediate ${_cname})
@@ -230,11 +276,18 @@ function(darwin_filter_builtin_sources output_var exclude_or_include excluded_li
 endfunction()
 
 function(darwin_add_eprintf_library)
+  cmake_parse_arguments(LIB
+    ""
+    ""
+    "CFLAGS"
+    ${ARGN})
+
   add_library(clang_rt.eprintf STATIC eprintf.c)
   set_target_compile_flags(clang_rt.eprintf
     -isysroot ${DARWIN_osx_SYSROOT}
     ${DARWIN_osx_BUILTIN_MIN_VER_FLAG}
-    -arch i386)
+    -arch i386
+    ${LIB_CFLAGS})
   set_target_properties(clang_rt.eprintf PROPERTIES
       OUTPUT_NAME clang_rt.eprintf${COMPILER_RT_OS_SUFFIX})
   set_target_properties(clang_rt.eprintf PROPERTIES
@@ -251,24 +304,17 @@ endfunction()
 macro(darwin_add_builtin_libraries)
   set(DARWIN_EXCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Darwin-excludes)
 
-  if(CMAKE_CONFIGURATION_TYPES)
-    foreach(type ${CMAKE_CONFIGURATION_TYPES})
-      set(CMAKE_C_FLAGS_${type} -O3)
-      set(CMAKE_CXX_FLAGS_${type} -O3)
-    endforeach()
-  else()
-    set(CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE} -O3)
-  endif()
-
-  set(CMAKE_C_FLAGS "-fPIC -fvisibility=hidden -DVISIBILITY_HIDDEN -Wall -fomit-frame-pointer")
-  set(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS})
-  set(CMAKE_ASM_FLAGS ${CMAKE_C_FLAGS})
+  set(CFLAGS "-fPIC -O3 -fvisibility=hidden -DVISIBILITY_HIDDEN -Wall -fomit-frame-pointer")
+  set(CMAKE_C_FLAGS "")
+  set(CMAKE_CXX_FLAGS "")
+  set(CMAKE_ASM_FLAGS "")
 
   set(PROFILE_SOURCES ../profile/InstrProfiling 
                       ../profile/InstrProfilingBuffer
-                      ../profile/InstrProfilingPlatformDarwin)
+                      ../profile/InstrProfilingPlatformDarwin
+                      ../profile/InstrProfilingWriter)
   foreach (os ${ARGN})
-    list_union(DARWIN_BUILTIN_ARCHS DARWIN_${os}_ARCHS BUILTIN_SUPPORTED_ARCH)
+    list_intersect(DARWIN_BUILTIN_ARCHS DARWIN_${os}_ARCHS BUILTIN_SUPPORTED_ARCH)
     foreach (arch ${DARWIN_BUILTIN_ARCHS})
       darwin_find_excluded_builtins_list(${arch}_${os}_EXCLUDED_BUILTINS
                               OS ${os}
@@ -283,7 +329,7 @@ macro(darwin_add_builtin_libraries)
                               OS ${os}
                               ARCH ${arch}
                               SOURCES ${filtered_sources}
-                              CFLAGS -arch ${arch}
+                              CFLAGS ${CFLAGS} -arch ${arch}
                               PARENT_TARGET builtins)
     endforeach()
 
@@ -306,7 +352,7 @@ macro(darwin_add_builtin_libraries)
                                 OS ${os}
                                 ARCH ${arch}
                                 SOURCES ${filtered_sources} ${PROFILE_SOURCES}
-                                CFLAGS -arch ${arch} -mkernel
+                                CFLAGS ${CFLAGS} -arch ${arch} -mkernel
                                 DEFS KERNEL_USE
                                 PARENT_TARGET builtins)
       endforeach()
@@ -323,7 +369,7 @@ macro(darwin_add_builtin_libraries)
     endif()
   endforeach()
 
-  darwin_add_eprintf_library()
+  darwin_add_eprintf_library(CFLAGS ${CFLAGS})
 
   # We put the x86 sim slices into the archives for their base OS
   foreach (os ${ARGN})
@@ -339,96 +385,99 @@ macro(darwin_add_builtin_libraries)
   darwin_add_embedded_builtin_libraries()
 endmacro()
 
-function(darwin_add_embedded_builtin_libraries)
-  set(MACHO_SYM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/macho_embedded)
-  if(CMAKE_CONFIGURATION_TYPES)
-    foreach(type ${CMAKE_CONFIGURATION_TYPES})
-      set(CMAKE_C_FLAGS_${type} -Oz)
-      set(CMAKE_CXX_FLAGS_${type} -Oz)
-    endforeach()
-  else()
-    set(CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE} -Oz)
-  endif()
+macro(darwin_add_embedded_builtin_libraries)
+  # this is a hacky opt-out. If you can't target both intel and arm
+  # architectures we bail here.
+  set(DARWIN_SOFT_FLOAT_ARCHS armv6m armv7m armv7em armv7)
+  set(DARWIN_HARD_FLOAT_ARCHS armv7em armv7)
+  if(COMPILER_RT_SUPPORTED_ARCH MATCHES ".*armv.*")
+    list(FIND COMPILER_RT_SUPPORTED_ARCH i386 i386_idx)
+    if(i386_idx GREATER -1)
+      list(APPEND DARWIN_HARD_FLOAT_ARCHS i386)
+    endif()
 
-  set(CMAKE_C_FLAGS "-Wall -fomit-frame-pointer -ffreestanding")
-  set(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS})
-  set(CMAKE_ASM_FLAGS ${CMAKE_C_FLAGS})
+    list(FIND COMPILER_RT_SUPPORTED_ARCH x86_64 x86_64_idx)
+    if(x86_64_idx GREATER -1)
+      list(APPEND DARWIN_HARD_FLOAT_ARCHS x86_64)
+    endif()
 
-  set(SOFT_FLOAT_FLAG -mfloat-abi=soft)
-  set(HARD_FLOAT_FLAG -mfloat-abi=hard)
+    set(MACHO_SYM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/macho_embedded)
 
-  set(PIC_FLAG_ -fPIC)
-  set(STATIC_FLAG -static)
+    set(CFLAGS "-Oz -Wall -fomit-frame-pointer -ffreestanding")
+    set(CMAKE_C_FLAGS "")
+    set(CMAKE_CXX_FLAGS "")
+    set(CMAKE_ASM_FLAGS "")
 
-  set(DARWIN_macho_embedded_ARCHS armv6m armv7m armv7em armv7 i386 x86_64)
+    set(SOFT_FLOAT_FLAG -mfloat-abi=soft)
+    set(HARD_FLOAT_FLAG -mfloat-abi=hard)
 
-  set(DARWIN_macho_embedded_LIBRARY_OUTPUT_DIR
-    ${COMPILER_RT_OUTPUT_DIR}/lib/macho_embedded)
-  set(DARWIN_macho_embedded_LIBRARY_INSTALL_DIR
-    ${COMPILER_RT_INSTALL_PATH}/lib/macho_embedded)
-    
-  set(CFLAGS_armv7 "-target thumbv7-apple-darwin-eabi")
-  set(CFLAGS_armv7em "-target thumbv7-apple-darwin-eabi")
-  set(CFLAGS_armv7m "-target thumbv7-apple-darwin-eabi")
-  set(CFLAGS_i386 "-march=pentium")
+    set(ENABLE_PIC Off)
+    set(PIC_FLAG -fPIC)
+    set(STATIC_FLAG -static)
 
-  set(DARWIN_SOFT_FLOAT_ARCHS armv6m armv7m armv7em armv7)
-  set(DARWIN_HARD_FLOAT_ARCHS armv7em armv7 i386 x86_64)
-
-  darwin_read_list_from_file(common_FUNCTIONS ${MACHO_SYM_DIR}/common.txt)
-  darwin_read_list_from_file(thumb2_FUNCTIONS ${MACHO_SYM_DIR}/thumb2.txt)
-  darwin_read_list_from_file(thumb2_64_FUNCTIONS ${MACHO_SYM_DIR}/thumb2-64.txt)
-  darwin_read_list_from_file(arm_FUNCTIONS ${MACHO_SYM_DIR}/arm.txt)
-  darwin_read_list_from_file(i386_FUNCTIONS ${MACHO_SYM_DIR}/i386.txt)
-
-
-  set(armv6m_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS})
-  set(armv7m_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS} ${thumb2_FUNCTIONS})
-  set(armv7em_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS} ${thumb2_FUNCTIONS})
-  set(armv7_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS} ${thumb2_FUNCTIONS} ${thumb2_64_FUNCTIONS})
-  set(i386_FUNCTIONS ${common_FUNCTIONS} ${i386_FUNCTIONS})
-  set(x86_64_FUNCTIONS ${common_FUNCTIONS})
-
-  foreach(arch ${DARWIN_macho_embedded_ARCHS})
-    darwin_filter_builtin_sources(${arch}_filtered_sources
-      INCLUDE ${arch}_FUNCTIONS
-      ${${arch}_SOURCES})
-    if(NOT ${arch}_filtered_sources)
-      message("${arch}_SOURCES: ${${arch}_SOURCES}")
-      message("${arch}_FUNCTIONS: ${${arch}_FUNCTIONS}")
-      message(FATAL_ERROR "Empty filtered sources!")
-    endif()
-  endforeach()
+    set(DARWIN_macho_embedded_ARCHS armv6m armv7m armv7em armv7 i386 x86_64)
 
-  foreach(float_type SOFT HARD)
-    foreach(type PIC STATIC)
-      string(TOLOWER "${float_type}_${type}" lib_suffix)
-      foreach(arch ${DARWIN_${float_type}_FLOAT_ARCHS})
-        set(DARWIN_macho_embedded_SYSROOT ${DARWIN_osx_SYSROOT})
-        set(DARWIN_macho_embedded_BUILTIN_MIN_VER_FLAG ${DARWIN_osx_BUILTIN_MIN_VER_FLAG})
-        set(float_flag)
-        if(${arch} MATCHES "^arm")
-          set(DARWIN_macho_embedded_SYSROOT ${DARWIN_ios_SYSROOT})
-          # x86 targets are hard float by default, but the complain about the
-          # float ABI flag, so don't pass it unless we're targeting arm.
-          set(float_flag ${${float_type}_FLOAT_FLAG})
-        endif()
-        darwin_add_builtin_library(clang_rt ${lib_suffix}
-                              OS macho_embedded
-                              ARCH ${arch}
-                              SOURCES ${${arch}_filtered_sources}
-                              CFLAGS -arch ${arch} ${${type}_FLAG} ${float_flag} ${CFLAGS_${arch}}
-                              PARENT_TARGET builtins)
-      endforeach()
-      foreach(lib ${macho_embedded_${lib_suffix}_libs})
-        set_target_properties(${lib} PROPERTIES LINKER_LANGUAGE C)
+    set(DARWIN_macho_embedded_LIBRARY_OUTPUT_DIR
+      ${COMPILER_RT_OUTPUT_DIR}/lib/macho_embedded)
+    set(DARWIN_macho_embedded_LIBRARY_INSTALL_DIR
+      ${COMPILER_RT_INSTALL_PATH}/lib/macho_embedded)
+      
+    set(CFLAGS_armv7 "-target thumbv7-apple-darwin-eabi")
+    set(CFLAGS_i386 "-march=pentium")
+
+    darwin_read_list_from_file(common_FUNCTIONS ${MACHO_SYM_DIR}/common.txt)
+    darwin_read_list_from_file(thumb2_FUNCTIONS ${MACHO_SYM_DIR}/thumb2.txt)
+    darwin_read_list_from_file(thumb2_64_FUNCTIONS ${MACHO_SYM_DIR}/thumb2-64.txt)
+    darwin_read_list_from_file(arm_FUNCTIONS ${MACHO_SYM_DIR}/arm.txt)
+    darwin_read_list_from_file(i386_FUNCTIONS ${MACHO_SYM_DIR}/i386.txt)
+
+
+    set(armv6m_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS})
+    set(armv7m_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS} ${thumb2_FUNCTIONS})
+    set(armv7em_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS} ${thumb2_FUNCTIONS})
+    set(armv7_FUNCTIONS ${common_FUNCTIONS} ${arm_FUNCTIONS} ${thumb2_FUNCTIONS} ${thumb2_64_FUNCTIONS})
+    set(i386_FUNCTIONS ${common_FUNCTIONS} ${i386_FUNCTIONS})
+    set(x86_64_FUNCTIONS ${common_FUNCTIONS})
+
+    foreach(arch ${DARWIN_macho_embedded_ARCHS})
+      darwin_filter_builtin_sources(${arch}_filtered_sources
+        INCLUDE ${arch}_FUNCTIONS
+        ${${arch}_SOURCES})
+      if(NOT ${arch}_filtered_sources)
+        message("${arch}_SOURCES: ${${arch}_SOURCES}")
+        message("${arch}_FUNCTIONS: ${${arch}_FUNCTIONS}")
+        message(FATAL_ERROR "Empty filtered sources!")
+      endif()
+    endforeach()
+
+    foreach(float_type SOFT HARD)
+      foreach(type PIC STATIC)
+        string(TOLOWER "${float_type}_${type}" lib_suffix)
+        foreach(arch ${DARWIN_${float_type}_FLOAT_ARCHS})
+          set(DARWIN_macho_embedded_SYSROOT ${DARWIN_osx_SYSROOT})
+          set(float_flag)
+          if(${arch} MATCHES "^arm")
+            # x86 targets are hard float by default, but the complain about the
+            # float ABI flag, so don't pass it unless we're targeting arm.
+            set(float_flag ${${float_type}_FLOAT_FLAG})
+          endif()
+          darwin_add_builtin_library(clang_rt ${lib_suffix}
+                                OS macho_embedded
+                                ARCH ${arch}
+                                SOURCES ${${arch}_filtered_sources}
+                                CFLAGS ${CFLAGS} -arch ${arch} ${${type}_FLAG} ${float_flag} ${CFLAGS_${arch}}
+                                PARENT_TARGET builtins)
+        endforeach()
+        foreach(lib ${macho_embedded_${lib_suffix}_libs})
+          set_target_properties(${lib} PROPERTIES LINKER_LANGUAGE C)
+        endforeach()
+        darwin_lipo_libs(clang_rt.${lib_suffix}
+                      PARENT_TARGET builtins
+                      LIPO_FLAGS ${macho_embedded_${lib_suffix}_lipo_flags}
+                      DEPENDS ${macho_embedded_${lib_suffix}_libs}
+                      OUTPUT_DIR ${DARWIN_macho_embedded_LIBRARY_OUTPUT_DIR}
+                      INSTALL_DIR ${DARWIN_macho_embedded_LIBRARY_INSTALL_DIR})
       endforeach()
-      darwin_lipo_libs(clang_rt.${lib_suffix}
-                    PARENT_TARGET builtins
-                    LIPO_FLAGS ${macho_embedded_${lib_suffix}_lipo_flags}
-                    DEPENDS ${macho_embedded_${lib_suffix}_libs}
-                    OUTPUT_DIR ${DARWIN_macho_embedded_LIBRARY_OUTPUT_DIR}
-                    INSTALL_DIR ${DARWIN_macho_embedded_LIBRARY_INSTALL_DIR})
     endforeach()
-  endforeach()
-endfunction()
+  endif()
+endmacro()
index cf690f4a33c523f04aa37ba753d6569cbdf1314b..ad9e70c0587adbb1e4e714839a90f0ab8bbfe3bb 100644 (file)
@@ -58,7 +58,7 @@ macro(append_have_file_definition filename varname list)
   list(APPEND ${list} "${varname}=${${varname}}")
 endmacro()
 
-macro(list_union output input1 input2)
+macro(list_intersect output input1 input2)
   set(${output})
   foreach(it ${${input1}})
     list(FIND ${input2} ${it} index)
index 9ede3670801875e89fa9700fe1575fae813dda99..0063cad5d9948dfdd141f9588845ab6c9fee0ada 100644 (file)
@@ -27,7 +27,14 @@ check_cxx_compiler_flag("-Werror -fno-function-sections" COMPILER_RT_HAS_FNO_FUN
 check_cxx_compiler_flag(-std=c++11           COMPILER_RT_HAS_STD_CXX11_FLAG)
 check_cxx_compiler_flag(-ftls-model=initial-exec COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC)
 check_cxx_compiler_flag(-fno-lto             COMPILER_RT_HAS_FNO_LTO_FLAG)
-check_cxx_compiler_flag(-msse3               COMPILER_RT_HAS_MSSE3_FLAG)
+check_cxx_compiler_flag("-Werror -msse3" COMPILER_RT_HAS_MSSE3_FLAG)
+check_cxx_compiler_flag(-std=c99             COMPILER_RT_HAS_STD_C99_FLAG)
+check_cxx_compiler_flag(--sysroot=.          COMPILER_RT_HAS_SYSROOT_FLAG)
+
+if(NOT WIN32 AND NOT CYGWIN)
+  # MinGW warns if -fvisibility-inlines-hidden is used.
+  check_cxx_compiler_flag("-fvisibility-inlines-hidden" COMPILER_RT_HAS_FVISIBILITY_INLINES_HIDDEN_FLAG)
+endif()
 
 check_cxx_compiler_flag(/GR COMPILER_RT_HAS_GR_FLAG)
 check_cxx_compiler_flag(/GS COMPILER_RT_HAS_GS_FLAG)
@@ -61,7 +68,7 @@ check_cxx_compiler_flag(/wd4800 COMPILER_RT_HAS_WD4800_FLAG)
 check_symbol_exists(__func__ "" COMPILER_RT_HAS_FUNC_SYMBOL)
 
 # Libraries.
-check_library_exists(c printf "" COMPILER_RT_HAS_LIBC)
+check_library_exists(c fopen "" COMPILER_RT_HAS_LIBC)
 check_library_exists(dl dlopen "" COMPILER_RT_HAS_LIBDL)
 check_library_exists(rt shm_open "" COMPILER_RT_HAS_LIBRT)
 check_library_exists(m pow "" COMPILER_RT_HAS_LIBM)
@@ -143,8 +150,11 @@ macro(detect_target_arch)
   check_symbol_exists(__i386__ "" __I386)
   check_symbol_exists(__mips__ "" __MIPS)
   check_symbol_exists(__mips64__ "" __MIPS64)
+  check_symbol_exists(__wasm32__ "" __WEBASSEMBLY32)
+  check_symbol_exists(__wasm64__ "" __WEBASSEMBLY64)
   if(__ARM)
-    add_default_target_arch(arm)
+    # Android shouldn't use Thumb2 instructions but can use VFP instructions.
+    add_default_target_arch(armhf)
   elseif(__AARCH64)
     add_default_target_arch(aarch64)
   elseif(__X86_64)
@@ -157,6 +167,10 @@ macro(detect_target_arch)
     add_default_target_arch(mips64)
   elseif(__MIPS)
     add_default_target_arch(mips)
+  elseif(__WEBASSEMBLY32)
+    add_default_target_arch(wasm32)
+  elseif(__WEBASSEMBLY64)
+    add_default_target_arch(wasm64)
   endif()
 endmacro()
 
@@ -191,6 +205,7 @@ elseif(NOT APPLE) # Supported archs for Apple platforms are generated later
   elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "powerpc")
     TEST_BIG_ENDIAN(HOST_IS_BIG_ENDIAN)
     if(HOST_IS_BIG_ENDIAN)
+      test_target_arch(powerpc "" "-m32")
       test_target_arch(powerpc64 "" "-m64")
     else()
       test_target_arch(powerpc64le "" "-m64")
@@ -207,15 +222,21 @@ elseif(NOT APPLE) # Supported archs for Apple platforms are generated later
     test_target_arch(mips "" "-mips32r2" "--target=mips-linux-gnu")
     test_target_arch(mips64 "" "-mips64r2" "--target=mips64-linux-gnu" "-mabi=n64")
   elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "arm")
-    if("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" MATCHES "eabihf")
-      test_target_arch(armhf "" "-march=armv7-a" "-mfloat-abi=hard")
+    if("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" MATCHES "armv7")
+      test_target_arch(armv7 "" "${CMAKE_C_FLAGS}")
+    elseif("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" MATCHES "eabihf")
+      test_target_arch(armhf "" "${CMAKE_C_FLAGS}")
     else()
-      test_target_arch(arm "" "-march=armv7-a" "-mfloat-abi=soft")
+      test_target_arch(arm "" "${CMAKE_C_FLAGS}")
     endif()
   elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "aarch32")
     test_target_arch(aarch32 "" "-march=armv8-a")
   elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "aarch64")
     test_target_arch(aarch64 "" "-march=armv8-a")
+  elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "wasm32")
+    test_target_arch(wasm32 "" "--target=wasm32-unknown-unknown")
+  elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "wasm64")
+    test_target_arch(wasm64 "" "--target=wasm64-unknown-unknown")
   endif()
   set(COMPILER_RT_OS_SUFFIX "")
 endif()
@@ -250,12 +271,15 @@ function(get_target_flags_for_arch arch out_var)
 endfunction()
 
 set(ARM64 aarch64)
-set(ARM32 arm armhf)
+set(ARM32 arm armhf armv7)
 set(X86 i386 i686)
 set(X86_64 x86_64)
 set(MIPS32 mips mipsel)
 set(MIPS64 mips64 mips64el)
+set(PPC powerpc)
 set(PPC64 powerpc64 powerpc64le)
+set(WASM32 wasm32)
+set(WASM64 wasm64)
 
 if(APPLE)
   set(ARM64 arm64)
@@ -264,7 +288,7 @@ if(APPLE)
 endif()
 
 set(ALL_BUILTIN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
-    ${MIPS32} ${MIPS64})
+    ${MIPS32} ${MIPS64} ${WASM32} ${WASM64} ${PPC} ${PPC64})
 set(ALL_SANITIZER_COMMON_SUPPORTED_ARCH ${X86} ${X86_64} ${PPC64}
     ${ARM32} ${ARM64} ${MIPS32} ${MIPS64})
 set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
@@ -274,19 +298,65 @@ set(ALL_LSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
 set(ALL_MSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
 set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC64}
     ${MIPS32} ${MIPS64})
-set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64})
+set(ALL_TSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64})
 set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
     ${MIPS32} ${MIPS64} ${PPC64})
-set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64})
+set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64})
+set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64})
 
 if(APPLE)
   include(CompilerRTDarwinUtils)
 
+  # On Darwin if /usr/include doesn't exist, the user probably has Xcode but not
+  # the command line tools. If this is the case, we need to find the OS X
+  # sysroot to pass to clang.
+  if(NOT EXISTS /usr/include)
+    execute_process(COMMAND xcodebuild -version -sdk macosx Path
+       OUTPUT_VARIABLE OSX_SYSROOT
+       ERROR_QUIET
+       OUTPUT_STRIP_TRAILING_WHITESPACE)
+    set(OSX_SYSROOT_FLAG "-isysroot${OSX_SYSROOT}")
+  endif()
+
   option(COMPILER_RT_ENABLE_IOS "Enable building for iOS - Experimental" Off)
+  option(COMPILER_RT_ENABLE_WATCHOS "Enable building for watchOS - Experimental" Off)
+  option(COMPILER_RT_ENABLE_TVOS "Enable building for tvOS - Experimental" Off)
 
   find_darwin_sdk_dir(DARWIN_osx_SYSROOT macosx)
   find_darwin_sdk_dir(DARWIN_iossim_SYSROOT iphonesimulator)
   find_darwin_sdk_dir(DARWIN_ios_SYSROOT iphoneos)
+  find_darwin_sdk_dir(DARWIN_watchossim_SYSROOT watchsimulator)
+  find_darwin_sdk_dir(DARWIN_watchos_SYSROOT watchos)
+  find_darwin_sdk_dir(DARWIN_tvossim_SYSROOT appletvsimulator)
+  find_darwin_sdk_dir(DARWIN_tvos_SYSROOT appletvos)
+
+  if(COMPILER_RT_ENABLE_IOS)
+    list(APPEND DARWIN_EMBEDDED_PLATFORMS ios)
+    set(DARWIN_ios_MIN_VER_FLAG -miphoneos-version-min)
+    set(DARWIN_ios_SANITIZER_MIN_VER_FLAG
+      ${DARWIN_ios_MIN_VER_FLAG}=7.0)
+    set(DARWIN_ios_BUILTIN_MIN_VER 6.0)
+    set(DARWIN_ios_BUILTIN_MIN_VER_FLAG
+      ${DARWIN_ios_MIN_VER_FLAG}=${DARWIN_ios_BUILTIN_MIN_VER})
+  endif()
+  if(COMPILER_RT_ENABLE_WATCHOS)
+    list(APPEND DARWIN_EMBEDDED_PLATFORMS watchos)
+    set(DARWIN_watchos_MIN_VER_FLAG -mwatchos-version-min)
+    set(DARWIN_watchos_SANITIZER_MIN_VER_FLAG
+      ${DARWIN_watchos_MIN_VER_FLAG}=2.0)
+    set(DARWIN_watchos_BUILTIN_MIN_VER 2.0)
+    set(DARWIN_watchos_BUILTIN_MIN_VER_FLAG
+      ${DARWIN_watchos_MIN_VER_FLAG}=${DARWIN_watchos_BUILTIN_MIN_VER})
+  endif()
+  if(COMPILER_RT_ENABLE_TVOS)
+    list(APPEND DARWIN_EMBEDDED_PLATFORMS tvos)
+    set(DARWIN_tvos_MIN_VER_FLAG -mtvos-version-min)
+    set(DARWIN_tvos_SANITIZER_MIN_VER_FLAG
+      ${DARWIN_tvos_MIN_VER_FLAG}=9.0)
+    set(DARWIN_tvos_BUILTIN_MIN_VER 9.0)
+    set(DARWIN_tvos_BUILTIN_MIN_VER_FLAG
+      ${DARWIN_tvos_MIN_VER_FLAG}=${DARWIN_tvos_BUILTIN_MIN_VER})
+  endif()
 
   # Note: In order to target x86_64h on OS X the minimum deployment target must
   # be 10.8 or higher.
@@ -311,13 +381,18 @@ if(APPLE)
 
   # We're setting the flag manually for each target OS
   set(CMAKE_OSX_DEPLOYMENT_TARGET "")
-  
+
   set(DARWIN_COMMON_CFLAGS -stdlib=libc++)
   set(DARWIN_COMMON_LINKFLAGS
     -stdlib=libc++
     -lc++
     -lc++abi)
-  
+
+  check_linker_flag("-fapplication-extension" COMPILER_RT_HAS_APP_EXTENSION)
+  if(COMPILER_RT_HAS_APP_EXTENSION)
+    list(APPEND DARWIN_COMMON_LINKFLAGS "-fapplication-extension")
+  endif()
+
   set(DARWIN_osx_CFLAGS
     ${DARWIN_COMMON_CFLAGS}
     -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION})
@@ -336,7 +411,7 @@ if(APPLE)
   # Figure out which arches to use for each OS
   darwin_get_toolchain_supported_archs(toolchain_arches)
   message(STATUS "Toolchain supported arches: ${toolchain_arches}")
-  
+
   if(NOT MACOSX_VERSION_MIN_FLAG)
     darwin_test_archs(osx
       DARWIN_osx_ARCHS
@@ -363,98 +438,102 @@ if(APPLE)
       list(APPEND BUILTIN_SUPPORTED_OS 10.4)
     endif()
 
-    if(DARWIN_iossim_SYSROOT)
-      set(DARWIN_iossim_CFLAGS
-        ${DARWIN_COMMON_CFLAGS}
-        -mios-simulator-version-min=7.0
-        -isysroot ${DARWIN_iossim_SYSROOT})
-      set(DARWIN_iossim_LINKFLAGS
-        ${DARWIN_COMMON_LINKFLAGS}
-        -mios-simulator-version-min=7.0
-        -isysroot ${DARWIN_iossim_SYSROOT})
-      set(DARWIN_iossim_BUILTIN_MIN_VER 6.0)
-      set(DARWIN_iossim_BUILTIN_MIN_VER_FLAG
-        -mios-simulator-version-min=${DARWIN_iossim_BUILTIN_MIN_VER})
-
-      set(DARWIN_iossim_SKIP_CC_KEXT On)
-      darwin_test_archs(iossim
-        DARWIN_iossim_ARCHS
-        ${toolchain_arches})
-      message(STATUS "iOS Simulator supported arches: ${DARWIN_iossim_ARCHS}")
-      if(DARWIN_iossim_ARCHS)
-        list(APPEND SANITIZER_COMMON_SUPPORTED_OS iossim)
+    foreach(platform ${DARWIN_EMBEDDED_PLATFORMS})
+      if(DARWIN_${platform}sim_SYSROOT)
+        set(DARWIN_${platform}sim_CFLAGS
+          ${DARWIN_COMMON_CFLAGS}
+          ${DARWIN_${platform}_SANITIZER_MIN_VER_FLAG}
+          -isysroot ${DARWIN_iossim_SYSROOT})
+        set(DARWIN_${platform}sim_LINKFLAGS
+          ${DARWIN_COMMON_LINKFLAGS}
+          ${DARWIN_${platform}_SANITIZER_MIN_VER_FLAG}
+          -isysroot ${DARWIN_${platform}sim_SYSROOT})
+        set(DARWIN_${platform}sim_BUILTIN_MIN_VER
+          ${DARWIN_${platform}_BUILTIN_MIN_VER})
+        set(DARWIN_${platform}sim_BUILTIN_MIN_VER_FLAG
+          ${DARWIN_${platform}_BUILTIN_MIN_VER_FLAG})
+
+        set(DARWIN_${platform}sim_SKIP_CC_KEXT On)
+        darwin_test_archs(${platform}sim
+          DARWIN_${platform}sim_ARCHS
+          ${toolchain_arches})
+        message(STATUS "${platform} Simulator supported arches: ${DARWIN_${platform}sim_ARCHS}")
+        if(DARWIN_iossim_ARCHS)
+          list(APPEND SANITIZER_COMMON_SUPPORTED_OS ${platform}sim)
+          list(APPEND BUILTIN_SUPPORTED_OS ${platform}sim)
+          list(APPEND PROFILE_SUPPORTED_OS ${platform}sim)
+        endif()
+        foreach(arch ${DARWIN_${platform}sim_ARCHS})
+          list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
+          set(CAN_TARGET_${arch} 1)
+        endforeach()
       endif()
-      foreach(arch ${DARWIN_iossim_ARCHS})
-        list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
-        set(CAN_TARGET_${arch} 1)
-      endforeach()
-    endif()
 
-    if(DARWIN_ios_SYSROOT AND COMPILER_RT_ENABLE_IOS)
-      set(DARWIN_ios_CFLAGS
-        ${DARWIN_COMMON_CFLAGS}
-        -miphoneos-version-min=7.0
-        -isysroot ${DARWIN_ios_SYSROOT})
-      set(DARWIN_ios_LINKFLAGS
-        ${DARWIN_COMMON_LINKFLAGS}
-        -miphoneos-version-min=7.0
-        -isysroot ${DARWIN_ios_SYSROOT})
-      set(DARWIN_ios_BUILTIN_MIN_VER 6.0)
-      set(DARWIN_ios_BUILTIN_MIN_VER_FLAG
-        -miphoneos-version-min=${DARWIN_ios_BUILTIN_MIN_VER})
-
-      darwin_test_archs(ios
-        DARWIN_ios_ARCHS
-        ${toolchain_arches})
-      message(STATUS "iOS supported arches: ${DARWIN_ios_ARCHS}")
-      if(DARWIN_ios_ARCHS)
-        list(APPEND SANITIZER_COMMON_SUPPORTED_OS ios)
-        list(APPEND BUILTIN_SUPPORTED_OS ios)
-        list(APPEND PROFILE_SUPPORTED_OS ios)
-        list(APPEND BUILTIN_SUPPORTED_OS iossim)
+      if(DARWIN_${platform}_SYSROOT)
+        set(DARWIN_${platform}_CFLAGS
+          ${DARWIN_COMMON_CFLAGS}
+          ${DARWIN_${platform}_SANITIZER_MIN_VER_FLAG}
+          -isysroot ${DARWIN_${platform}_SYSROOT})
+        set(DARWIN_${platform}_LINKFLAGS
+          ${DARWIN_COMMON_LINKFLAGS}
+          ${DARWIN_${platform}_SANITIZER_MIN_VER_FLAG}
+          -isysroot ${DARWIN_${platform}_SYSROOT})
+
+        darwin_test_archs(${platform}
+          DARWIN_${platform}_ARCHS
+          ${toolchain_arches})
+        message(STATUS "${platform} supported arches: ${DARWIN_${platform}_ARCHS}")
+        if(DARWIN_${platform}_ARCHS)
+          list(APPEND SANITIZER_COMMON_SUPPORTED_OS ${platform})
+          list(APPEND BUILTIN_SUPPORTED_OS ${platform})
+          list(APPEND PROFILE_SUPPORTED_OS ${platform})
+        endif()
+        foreach(arch ${DARWIN_${platform}_ARCHS})
+          list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
+          set(CAN_TARGET_${arch} 1)
+        endforeach()
       endif()
-      foreach(arch ${DARWIN_ios_ARCHS})
-        list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch})
-        set(CAN_TARGET_${arch} 1)
-      endforeach()
-    endif()
+    endforeach()
   endif()
 
-  # for list_union
+  # for list_intersect
   include(CompilerRTUtils)
 
-  list_union(BUILTIN_SUPPORTED_ARCH ALL_BUILTIN_SUPPORTED_ARCH toolchain_arches)
+  list_intersect(BUILTIN_SUPPORTED_ARCH ALL_BUILTIN_SUPPORTED_ARCH toolchain_arches)
 
-  list_union(SANITIZER_COMMON_SUPPORTED_ARCH
+  list_intersect(SANITIZER_COMMON_SUPPORTED_ARCH
     ALL_SANITIZER_COMMON_SUPPORTED_ARCH
     COMPILER_RT_SUPPORTED_ARCH
     )
   set(LSAN_COMMON_SUPPORTED_ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH})
   set(UBSAN_COMMON_SUPPORTED_ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH})
-  list_union(ASAN_SUPPORTED_ARCH
+  list_intersect(ASAN_SUPPORTED_ARCH
     ALL_ASAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
-  list_union(DFSAN_SUPPORTED_ARCH
+  list_intersect(DFSAN_SUPPORTED_ARCH
     ALL_DFSAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
-  list_union(LSAN_SUPPORTED_ARCH
+  list_intersect(LSAN_SUPPORTED_ARCH
     ALL_LSAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
-  list_union(MSAN_SUPPORTED_ARCH
+  list_intersect(MSAN_SUPPORTED_ARCH
     ALL_MSAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
-  list_union(PROFILE_SUPPORTED_ARCH
+  list_intersect(PROFILE_SUPPORTED_ARCH
     ALL_PROFILE_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
-  list_union(TSAN_SUPPORTED_ARCH
+  list_intersect(TSAN_SUPPORTED_ARCH
     ALL_TSAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
-  list_union(UBSAN_SUPPORTED_ARCH
+  list_intersect(UBSAN_SUPPORTED_ARCH
     ALL_UBSAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
-  list_union(SAFESTACK_SUPPORTED_ARCH
+  list_intersect(SAFESTACK_SUPPORTED_ARCH
     ALL_SAFESTACK_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
+  list_intersect(CFI_SUPPORTED_ARCH
+    ALL_CFI_SUPPORTED_ARCH
+    SANITIZER_COMMON_SUPPORTED_ARCH)
 else()
   # Architectures supported by compiler-rt libraries.
   filter_available_targets(BUILTIN_SUPPORTED_ARCH
@@ -476,6 +555,7 @@ else()
   filter_available_targets(UBSAN_SUPPORTED_ARCH ${ALL_UBSAN_SUPPORTED_ARCH})
   filter_available_targets(SAFESTACK_SUPPORTED_ARCH
     ${ALL_SAFESTACK_SUPPORTED_ARCH})
+  filter_available_targets(CFI_SUPPORTED_ARCH ${ALL_CFI_SUPPORTED_ARCH})
 endif()
 
 message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}")
@@ -538,26 +618,19 @@ else()
 endif()
 
 if (PROFILE_SUPPORTED_ARCH AND
-    OS_NAME MATCHES "Darwin|Linux|FreeBSD")
+    OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows")
   set(COMPILER_RT_HAS_PROFILE TRUE)
 else()
   set(COMPILER_RT_HAS_PROFILE FALSE)
 endif()
 
 if (COMPILER_RT_HAS_SANITIZER_COMMON AND TSAN_SUPPORTED_ARCH AND
-    OS_NAME MATCHES "Linux|FreeBSD")
+    OS_NAME MATCHES "Darwin|Linux|FreeBSD")
   set(COMPILER_RT_HAS_TSAN TRUE)
 else()
   set(COMPILER_RT_HAS_TSAN FALSE)
 endif()
 
-if(APPLE)
-  option(COMPILER_RT_ENABLE_TSAN_OSX "Enable building TSan for OS X - Experimental" Off)
-  if(COMPILER_RT_ENABLE_TSAN_OSX)
-    set(COMPILER_RT_HAS_TSAN TRUE)
-  endif()
-endif()
-
 if (COMPILER_RT_HAS_SANITIZER_COMMON AND UBSAN_SUPPORTED_ARCH AND
     OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows")
   set(COMPILER_RT_HAS_UBSAN TRUE)
@@ -565,17 +638,16 @@ else()
   set(COMPILER_RT_HAS_UBSAN FALSE)
 endif()
 
-# -msse3 flag is not valid for Mips therefore clang gives a warning
-# message with -msse3. But check_c_compiler_flags() checks only for
-# compiler error messages. Therefore COMPILER_RT_HAS_MSSE3_FLAG turns out to be
-# true on Mips, so we make it false here.
-if("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "mips")
-  set(COMPILER_RT_HAS_MSSE3_FLAG FALSE)
-endif()
-
 if (COMPILER_RT_HAS_SANITIZER_COMMON AND SAFESTACK_SUPPORTED_ARCH AND
     OS_NAME MATCHES "Darwin|Linux|FreeBSD")
   set(COMPILER_RT_HAS_SAFESTACK TRUE)
 else()
   set(COMPILER_RT_HAS_SAFESTACK FALSE)
 endif()
+
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND CFI_SUPPORTED_ARCH AND
+    OS_NAME MATCHES "Linux")
+  set(COMPILER_RT_HAS_CFI TRUE)
+else()
+  set(COMPILER_RT_HAS_CFI FALSE)
+endif()
index b736ed9e52354ac08fdbaaf5ef286dc806762dbe..b2a4bb7b89eeb6cdbdd7d854d13e7fd4e56ebe35 100644 (file)
@@ -125,9 +125,11 @@ extern "C" {
   // to know what is being passed to libc functions, e.g. memcmp.
   // FIXME: implement more hooks.
   void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1,
-                                    const void *s2, size_t n);
+                                    const void *s2, size_t n, int result);
   void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1,
-                                    const char *s2, size_t n);
+                                    const char *s2, size_t n, int result);
+  void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1,
+                                    const char *s2, int result);
 #ifdef __cplusplus
 }  // extern "C"
 #endif
index b93111b859bc9e31288af4e410d426a519fc5664..2dcc09fc84993560b27315c084c61c424f7b30be 100644 (file)
@@ -41,6 +41,13 @@ extern "C" {
   // Some of the entries in *data will be zero.
   uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data);
 
+  // Set *data to the growing buffer with covered PCs and return the size
+  // of the buffer. The entries are never zero.
+  // When only unique pcs are collected, the size is equal to
+  // __sanitizer_get_total_unique_coverage.
+  // WARNING: EXPERIMENTAL API.
+  uintptr_t __sanitizer_get_coverage_pc_buffer(uintptr_t **data);
+
   // The coverage instrumentation may optionally provide imprecise counters.
   // Rather than exposing the counter values to the user we instead map
   // the counters to a bitset.
index 9215b080b7b021c8bdadf06268a721be8c8f2133..4bc6f7a2d576e3861d91aaaefd552db5812b040a 100644 (file)
@@ -19,8 +19,6 @@ if(COMPILER_RT_BUILD_SANITIZERS)
     add_subdirectory(ubsan)
   endif()
 
-  add_subdirectory(cfi)
-
   if(COMPILER_RT_HAS_ASAN)
     add_subdirectory(asan)
   endif()
@@ -45,4 +43,8 @@ if(COMPILER_RT_BUILD_SANITIZERS)
   if(COMPILER_RT_HAS_SAFESTACK)
     add_subdirectory(safestack)
   endif()
+
+  if(COMPILER_RT_HAS_CFI)
+    add_subdirectory(cfi)
+  endif()
 endif()
diff --git a/src/compiler-rt/lib/asan/.clang-format b/src/compiler-rt/lib/asan/.clang-format
new file mode 100644 (file)
index 0000000..f6cb8ad
--- /dev/null
@@ -0,0 +1 @@
+BasedOnStyle: Google
index 8cc9bb17b59de418b304acfb55eb8ce8821a9740..bb6ff42c5cde4892f3f2a2c093f4a8484f89bd37 100644 (file)
@@ -23,4 +23,4 @@ from the root of your CMake build tree:
 make check-asan
 
 For more instructions see:
-http://code.google.com/p/address-sanitizer/wiki/HowToBuild
+https://github.com/google/sanitizers/wiki/AddressSanitizerHowToBuild
index 1d3582c16809600429f1b6d80b6ae6434aadf608..9df3b977ea1ba9b431826242775f77013208d1e0 100644 (file)
@@ -61,11 +61,6 @@ static struct AsanDeactivatedFlags {
       parser.ParseString(env);
     }
 
-    // Override from getprop asan.options.
-    char buf[100];
-    GetExtraActivationFlags(buf, sizeof(buf));
-    parser.ParseString(buf);
-
     SetVerbosity(cf.verbosity);
 
     if (Verbosity()) ReportUnrecognizedFlags();
index 18e597ffa004aec7d6e5b1884d62ed07a8ff17a8..363ee67e77c6d8bdac5ba7bbcf6d2186a653ecaa 100644 (file)
@@ -116,14 +116,6 @@ void InitializeFlags() {
   ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
 #endif
 
-  // Let activation flags override current settings. On Android they come
-  // from a system property. On other platforms this is no-op.
-  if (!flags()->start_deactivated) {
-    char buf[100];
-    GetExtraActivationFlags(buf, sizeof(buf));
-    asan_parser.ParseString(buf);
-  }
-
   SetVerbosity(common_flags()->verbosity);
 
   // TODO(eugenis): dump all flags at verbosity>=2?
index ec6400e864a6937d4ca3a61f9f769a6a6c0b1305..5e69242fb8e98d1be591ea75434004713befb782 100644 (file)
@@ -44,9 +44,6 @@ ASAN_FLAG(
     "to find more errors.")
 ASAN_FLAG(bool, replace_intrin, true,
           "If set, uses custom wrappers for memset/memcpy/memmove intinsics.")
-ASAN_FLAG(bool, mac_ignore_invalid_free, false,
-          "Ignore invalid free() calls to work around some bugs. Used on OS X "
-          "only.")
 ASAN_FLAG(bool, detect_stack_use_after_return, false,
           "Enables stack-use-after-return checking at run-time.")
 ASAN_FLAG(int, min_uar_stack_size_log, 16, // We can't do smaller anyway.
@@ -75,6 +72,7 @@ ASAN_FLAG(bool, check_malloc_usable_size, true,
           "295.*.")
 ASAN_FLAG(bool, unmap_shadow_on_exit, false,
           "If set, explicitly unmaps the (huge) shadow at exit.")
+ASAN_FLAG(bool, protect_shadow_gap, true, "If set, mprotect the shadow gap")
 ASAN_FLAG(bool, print_stats, false,
           "Print various statistics after printing an error message or if "
           "atexit=1.")
@@ -98,8 +96,8 @@ ASAN_FLAG(bool, poison_array_cookie, true,
           "Poison (or not) the array cookie after operator new[].")
 
 // Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
-// https://code.google.com/p/address-sanitizer/issues/detail?id=131
-// https://code.google.com/p/address-sanitizer/issues/detail?id=309
+// https://github.com/google/sanitizers/issues/131
+// https://github.com/google/sanitizers/issues/309
 // TODO(glider,timurrrr): Fix known issues and enable this back.
 ASAN_FLAG(bool, alloc_dealloc_mismatch,
           (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0),
@@ -125,8 +123,8 @@ ASAN_FLAG(
     "The bigger the value the harder we try.")
 ASAN_FLAG(
     bool, detect_container_overflow, true,
-    "If true, honor the container overflow  annotations. "
-    "See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow")
+    "If true, honor the container overflow annotations. See "
+    "https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow")
 ASAN_FLAG(int, detect_odr_violation, 2,
           "If >=2, detect violation of One-Definition-Rule (ODR); "
           "If ==1, detect ODR-violation only if the two variables "
@@ -134,3 +132,6 @@ ASAN_FLAG(int, detect_odr_violation, 2,
 ASAN_FLAG(bool, dump_instruction_bytes, false,
           "If true, dump 16 bytes starting at the instruction that caused SEGV")
 ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
+ASAN_FLAG(bool, halt_on_error, true,
+          "Crash the program after printing the first error report "
+          "(WARNING: USE AT YOUR OWN RISK!)")
index 86879e424aeac6c1cfacdc64168187d5ff6e750b..d9a0c71a002da799f7a597f1bbf0ab4746a4a8fd 100644 (file)
@@ -75,7 +75,7 @@ struct AsanInterceptorContext {
       }                                                                 \
       if (!suppressed) {                                                \
         GET_CURRENT_PC_BP_SP;                                           \
-        __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);     \
+        ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false);\
       }                                                                 \
     }                                                                   \
   } while (0)
@@ -178,7 +178,7 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
   } while (false)
 #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
 // Strict init-order checking is dlopen-hostile:
-// https://code.google.com/p/address-sanitizer/issues/detail?id=178
+// https://github.com/google/sanitizers/issues/178
 #define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag)                           \
   if (flags()->strict_init_order) {                                            \
     StopInitOrderChecking();                                                   \
index 18cd2b6decdd542f3e29828a7c88f10beb3dd9b5..9efddcbd42b23f8d69b61a7978f52be15f5560f2 100644 (file)
@@ -167,6 +167,19 @@ extern "C" {
   SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN(uptr p, uptr size);
   SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN(uptr p, uptr size);
 
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_load8_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_load16_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_store1_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_store2_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_store4_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_store8_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_store16_noabort(uptr p);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN_noabort(uptr p, uptr size);
+  SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN_noabort(uptr p, uptr size);
+
   SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load1(uptr p, u32 exp);
   SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp);
   SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load4(uptr p, u32 exp);
index 9e9175879f55fa1888928bf31045c58e54300576..0ef0d0eb526311f97eecfa00e547ca040aa61ada 100644 (file)
@@ -75,8 +75,6 @@ void AsanCheckIncompatibleRT();
 
 void AsanOnDeadlySignal(int, void *siginfo, void *context);
 
-void DisableReexec();
-void MaybeReexec();
 void ReadContextStack(void *context, uptr *stack, uptr *ssize);
 void StopInitOrderChecking();
 
index 804aef73735398d68af70cb31df39c4c4903711e..e26b400562df7728d972014f7b26624872940833 100644 (file)
@@ -70,14 +70,6 @@ namespace __asan {
 
 void InitializePlatformInterceptors() {}
 
-void DisableReexec() {
-  // No need to re-exec on Linux.
-}
-
-void MaybeReexec() {
-  // No need to re-exec on Linux.
-}
-
 void *AsanDoesNotSupportStaticLinkage() {
   // This will fail to link with -static.
   return &_DYNAMIC;  // defined in link.h
index 8e23c71be5aa444a591998f80cca526dd061f3d3..f00d98f8e5e6ceb7cee67364c2c8b0ab3f35da41 100644 (file)
 #include "sanitizer_common/sanitizer_libc.h"
 #include "sanitizer_common/sanitizer_mac.h"
 
-#if !SANITIZER_IOS
-#include <crt_externs.h>  // for _NSGetArgv and _NSGetEnviron
-#else
-extern "C" {
-  extern char ***_NSGetArgv(void);
-}
-#endif
-
-#include <dlfcn.h>  // for dladdr()
 #include <fcntl.h>
 #include <libkern/OSAtomic.h>
 #include <mach-o/dyld.h>
@@ -52,182 +43,12 @@ void InitializePlatformInterceptors() {}
 bool PlatformHasDifferentMemcpyAndMemmove() {
   // On OS X 10.7 memcpy() and memmove() are both resolved
   // into memmove$VARIANT$sse42.
-  // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34.
+  // See also https://github.com/google/sanitizers/issues/34.
   // TODO(glider): need to check dynamically that memcpy() and memmove() are
   // actually the same function.
   return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
 }
 
-extern "C"
-void __asan_init();
-
-static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
-LowLevelAllocator allocator_for_env;
-
-// Change the value of the env var |name|, leaking the original value.
-// If |name_value| is NULL, the variable is deleted from the environment,
-// otherwise the corresponding "NAME=value" string is replaced with
-// |name_value|.
-void LeakyResetEnv(const char *name, const char *name_value) {
-  char **env = GetEnviron();
-  uptr name_len = internal_strlen(name);
-  while (*env != 0) {
-    uptr len = internal_strlen(*env);
-    if (len > name_len) {
-      const char *p = *env;
-      if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
-        // Match.
-        if (name_value) {
-          // Replace the old value with the new one.
-          *env = const_cast<char*>(name_value);
-        } else {
-          // Shift the subsequent pointers back.
-          char **del = env;
-          do {
-            del[0] = del[1];
-          } while (*del++);
-        }
-      }
-    }
-    env++;
-  }
-}
-
-static bool reexec_disabled = false;
-
-void DisableReexec() {
-  reexec_disabled = true;
-}
-
-extern "C" double dyldVersionNumber;
-static const double kMinDyldVersionWithAutoInterposition = 360.0;
-
-bool DyldNeedsEnvVariable() {
-  // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
-  // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via
-  // GetMacosVersion() doesn't work for the simulator. Let's instead check
-  // `dyldVersionNumber`, which is exported by dyld, against a known version
-  // number from the first OS release where this appeared.
-  return dyldVersionNumber < kMinDyldVersionWithAutoInterposition;
-}
-
-void MaybeReexec() {
-  if (reexec_disabled) return;
-
-  // Make sure the dynamic ASan runtime library is preloaded so that the
-  // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
-  // ourselves.
-  Dl_info info;
-  CHECK(dladdr((void*)((uptr)__asan_init), &info));
-  char *dyld_insert_libraries =
-      const_cast<char*>(GetEnv(kDyldInsertLibraries));
-  uptr old_env_len = dyld_insert_libraries ?
-      internal_strlen(dyld_insert_libraries) : 0;
-  uptr fname_len = internal_strlen(info.dli_fname);
-  const char *dylib_name = StripModuleName(info.dli_fname);
-  uptr dylib_name_len = internal_strlen(dylib_name);
-
-  bool lib_is_in_env =
-      dyld_insert_libraries && REAL(strstr)(dyld_insert_libraries, dylib_name);
-  if (DyldNeedsEnvVariable() && !lib_is_in_env) {
-    // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
-    // library.
-    char program_name[1024];
-    uint32_t buf_size = sizeof(program_name);
-    _NSGetExecutablePath(program_name, &buf_size);
-    char *new_env = const_cast<char*>(info.dli_fname);
-    if (dyld_insert_libraries) {
-      // Append the runtime dylib name to the existing value of
-      // DYLD_INSERT_LIBRARIES.
-      new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
-      internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
-      new_env[old_env_len] = ':';
-      // Copy fname_len and add a trailing zero.
-      internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
-                       fname_len + 1);
-      // Ok to use setenv() since the wrappers don't depend on the value of
-      // asan_inited.
-      setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
-    } else {
-      // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
-      setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
-    }
-    VReport(1, "exec()-ing the program with\n");
-    VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env);
-    VReport(1, "to enable ASan wrappers.\n");
-    execv(program_name, *_NSGetArgv());
-
-    // We get here only if execv() failed.
-    Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, "
-           "which is required for ASan to work. ASan tried to set the "
-           "environment variable and re-execute itself, but execv() failed, "
-           "possibly because of sandbox restrictions. Make sure to launch the "
-           "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env);
-    CHECK("execv failed" && 0);
-  }
-
-  if (!lib_is_in_env)
-    return;
-
-  // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
-  // the dylib from the environment variable, because interceptors are installed
-  // and we don't want our children to inherit the variable.
-
-  uptr env_name_len = internal_strlen(kDyldInsertLibraries);
-  // Allocate memory to hold the previous env var name, its value, the '='
-  // sign and the '\0' char.
-  char *new_env = (char*)allocator_for_env.Allocate(
-      old_env_len + 2 + env_name_len);
-  CHECK(new_env);
-  internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
-  internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
-  new_env[env_name_len] = '=';
-  char *new_env_pos = new_env + env_name_len + 1;
-
-  // Iterate over colon-separated pieces of |dyld_insert_libraries|.
-  char *piece_start = dyld_insert_libraries;
-  char *piece_end = NULL;
-  char *old_env_end = dyld_insert_libraries + old_env_len;
-  do {
-    if (piece_start[0] == ':') piece_start++;
-    piece_end = REAL(strchr)(piece_start, ':');
-    if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
-    if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
-    uptr piece_len = piece_end - piece_start;
-
-    char *filename_start =
-        (char *)internal_memrchr(piece_start, '/', piece_len);
-    uptr filename_len = piece_len;
-    if (filename_start) {
-      filename_start += 1;
-      filename_len = piece_len - (filename_start - piece_start);
-    } else {
-      filename_start = piece_start;
-    }
-
-    // If the current piece isn't the runtime library name,
-    // append it to new_env.
-    if ((dylib_name_len != filename_len) ||
-        (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
-      if (new_env_pos != new_env + env_name_len + 1) {
-        new_env_pos[0] = ':';
-        new_env_pos++;
-      }
-      internal_strncpy(new_env_pos, piece_start, piece_len);
-      new_env_pos += piece_len;
-    }
-    // Move on to the next piece.
-    piece_start = piece_end;
-  } while (piece_start < old_env_end);
-
-  // Can't use setenv() here, because it requires the allocator to be
-  // initialized.
-  // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
-  // a separate function called after InitializeAllocator().
-  if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
-  LeakyResetEnv(kDyldInsertLibraries, new_env);
-}
-
 // No-op. Mac does not support static linkage anyway.
 void *AsanDoesNotSupportStaticLinkage() {
   return 0;
index 46a6a9db4a8122f48c9040812dc5efeed685b1f0..d5089f9f7b362131d2c25ca98e9f664f54f7abb3 100644 (file)
 // ---------------------- Replacement functions ---------------- {{{1
 using namespace __asan;  // NOLINT
 
+static const uptr kCallocPoolSize = 1024;
+static uptr calloc_memory_for_dlsym[kCallocPoolSize];
+
+static bool IsInCallocPool(const void *ptr) {
+  sptr off = (sptr)ptr - (sptr)calloc_memory_for_dlsym;
+  return 0 <= off && off < (sptr)kCallocPoolSize;
+}
+
 INTERCEPTOR(void, free, void *ptr) {
   GET_STACK_TRACE_FREE;
+  if (UNLIKELY(IsInCallocPool(ptr)))
+    return;
   asan_free(ptr, &stack, FROM_MALLOC);
 }
 
 INTERCEPTOR(void, cfree, void *ptr) {
   GET_STACK_TRACE_FREE;
+  if (UNLIKELY(IsInCallocPool(ptr)))
+    return;
   asan_free(ptr, &stack, FROM_MALLOC);
 }
 
@@ -44,8 +56,6 @@ INTERCEPTOR(void*, malloc, uptr size) {
 INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
   if (UNLIKELY(!asan_inited)) {
     // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
-    const uptr kCallocPoolSize = 1024;
-    static uptr calloc_memory_for_dlsym[kCallocPoolSize];
     static uptr allocated;
     uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
     void *mem = (void*)&calloc_memory_for_dlsym[allocated];
@@ -59,6 +69,13 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
 
 INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
   GET_STACK_TRACE_MALLOC;
+  if (UNLIKELY(IsInCallocPool(ptr))) {
+    uptr offset = (uptr)ptr - (uptr)calloc_memory_for_dlsym;
+    uptr copy_size = Min(size, kCallocPoolSize - offset);
+    void *new_ptr = asan_malloc(size, &stack);
+    internal_memcpy(new_ptr, ptr, copy_size);
+    return new_ptr;
+  }
   return asan_realloc(ptr, size, &stack);
 }
 
index 312c1c34fc6aa039999be23a6a750500ed6a25f8..744728d40df5395550be33baa705a767d69fe996 100644 (file)
@@ -54,10 +54,6 @@ using namespace __asan;
 #define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
   GET_STACK_TRACE_FREE; \
   ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
-#define COMMON_MALLOC_IGNORE_INVALID_FREE flags()->mac_ignore_invalid_free
-#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \
-  GET_STACK_TRACE_FREE; \
-  WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
 #define COMMON_MALLOC_NAMESPACE __asan
 
 #include "sanitizer_common/sanitizer_malloc_mac.inc"
index f76ca53c360734a9e2ff04cb5b26c2977c7ccad3..8fe347c8bad06802bc50a83927c7d473f32bb319 100644 (file)
@@ -17,7 +17,7 @@
 #include "asan_internal.h"
 
 // The full explanation of the memory mapping could be found here:
-// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
+// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm
 //
 // Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000:
 // || `[0x10007fff8000, 0x7fffffffffff]` || HighMem    ||
@@ -118,11 +118,7 @@ static const u64 kIosShadowOffset32 = 1ULL << 30;  // 0x40000000
 static const u64 kIosShadowOffset64 = 0x130000000;
 static const u64 kIosSimShadowOffset32 = 1ULL << 30;
 static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64;
-#if SANITIZER_AARCH64_VMA == 39
 static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
-#elif SANITIZER_AARCH64_VMA == 42
-static const u64 kAArch64_ShadowOffset64 = 1ULL << 39;
-#endif
 static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
 static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37;
 static const u64 kPPC64_ShadowOffset64 = 1ULL << 41;
index cd7f1632e4263eb65074bb4273422962582e8ebf..b5ba13ef40551c8b1e5bced98d7f8e9707d86673 100644 (file)
@@ -30,7 +30,7 @@
 using namespace __asan;  // NOLINT
 
 // This code has issues on OSX.
-// See https://code.google.com/p/address-sanitizer/issues/detail?id=131.
+// See https://github.com/google/sanitizers/issues/131.
 
 // Fake std::nothrow_t to avoid including <new>.
 namespace std {
index 0082e845fcf8f0206f7c3aa77667891340d746f7..f77ab8780bb7098d5efdc4b4ce46f73241c58142 100644 (file)
@@ -102,7 +102,7 @@ using namespace __asan;  // NOLINT
 // that user program (un)poisons the memory it owns. It poisons memory
 // conservatively, and unpoisons progressively to make sure asan shadow
 // mapping invariant is preserved (see detailed mapping description here:
-// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm).
+// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm).
 //
 // * if user asks to poison region [left, right), the program poisons
 // at least [left, AlignDown(right)).
@@ -354,7 +354,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p,
   // Make a quick sanity check that we are indeed in this state.
   //
   // FIXME: Two of these three checks are disabled until we fix
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=258.
+  // https://github.com/google/sanitizers/issues/258.
   // if (d1 != d2)
   //  CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1);
   if (a + granularity <= d1)
index cce56984ecdb1a8349fcbaf2dcdbd7de71ab2957..9e01bcd091bf62192390274a8d7475c33a16e56e 100644 (file)
@@ -77,6 +77,8 @@ void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
     ReportStackOverflow(sig);
   else if (signo == SIGFPE)
     ReportDeadlySignal("FPE", sig);
+  else if (signo == SIGILL)
+    ReportDeadlySignal("ILL", sig);
   else
     ReportDeadlySignal("SEGV", sig);
 }
index 957ac14eaf5234ef3ae8362fb4b9d8a9ff90b4b4..bb7e36e84652575512df2bf1241ebec14896d601 100644 (file)
@@ -30,7 +30,9 @@ namespace __asan {
 static void (*error_report_callback)(const char*);
 static char *error_message_buffer = nullptr;
 static uptr error_message_buffer_pos = 0;
-static uptr error_message_buffer_size = 0;
+static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED);
+static const unsigned kAsanBuggyPcPoolSize = 25;
+static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
 
 struct ReportData {
   uptr pc;
@@ -46,16 +48,20 @@ static bool report_happened = false;
 static ReportData report_data = {};
 
 void AppendToErrorMessageBuffer(const char *buffer) {
-  if (error_message_buffer) {
-    uptr length = internal_strlen(buffer);
-    CHECK_GE(error_message_buffer_size, error_message_buffer_pos);
-    uptr remaining = error_message_buffer_size - error_message_buffer_pos;
-    internal_strncpy(error_message_buffer + error_message_buffer_pos,
-                     buffer, remaining);
-    error_message_buffer[error_message_buffer_size - 1] = '\0';
-    // FIXME: reallocate the buffer instead of truncating the message.
-    error_message_buffer_pos += Min(remaining, length);
+  BlockingMutexLock l(&error_message_buf_mutex);
+  if (!error_message_buffer) {
+    error_message_buffer =
+      (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__);
+    error_message_buffer_pos = 0;
   }
+  uptr length = internal_strlen(buffer);
+  RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos);
+  uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos;
+  internal_strncpy(error_message_buffer + error_message_buffer_pos,
+                   buffer, remaining);
+  error_message_buffer[kErrorMessageBufferSize - 1] = '\0';
+  // FIXME: reallocate the buffer instead of truncating the message.
+  error_message_buffer_pos += Min(remaining, length);
 }
 
 // ---------------------- Decorator ------------------------------ {{{1
@@ -622,26 +628,90 @@ void DescribeThread(AsanThreadContext *context) {
 // immediately after printing error report.
 class ScopedInErrorReport {
  public:
-  explicit ScopedInErrorReport(ReportData *report = nullptr) {
-    static atomic_uint32_t num_calls;
-    static u32 reporting_thread_tid;
-    if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
+  explicit ScopedInErrorReport(ReportData *report = nullptr,
+                               bool fatal = false) {
+    halt_on_error_ = fatal || flags()->halt_on_error;
+
+    if (lock_.TryLock()) {
+      StartReporting(report);
+      return;
+    }
+
+    // ASan found two bugs in different threads simultaneously.
+
+    u32 current_tid = GetCurrentTidOrInvalid();
+    if (reporting_thread_tid_ == current_tid ||
+        reporting_thread_tid_ == kInvalidTid) {
+      // This is either asynch signal or nested error during error reporting.
+      // Fail simple to avoid deadlocks in Report().
+
+      // Can't use Report() here because of potential deadlocks
+      // in nested signal handlers.
+      const char msg[] = "AddressSanitizer: nested bug in the same thread, "
+                         "aborting.\n";
+      WriteToFile(kStderrFd, msg, sizeof(msg));
+
+      internal__exit(common_flags()->exitcode);
+    }
+
+    if (halt_on_error_) {
       // Do not print more than one report, otherwise they will mix up.
       // Error reporting functions shouldn't return at this situation, as
-      // they are defined as no-return.
+      // they are effectively no-returns.
+
       Report("AddressSanitizer: while reporting a bug found another one. "
-                 "Ignoring.\n");
-      u32 current_tid = GetCurrentTidOrInvalid();
-      if (current_tid != reporting_thread_tid) {
-        // ASan found two bugs in different threads simultaneously. Sleep
-        // long enough to make sure that the thread which started to print
-        // an error report will finish doing it.
-        SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
-      }
+             "Ignoring.\n");
+
+      // Sleep long enough to make sure that the thread which started
+      // to print an error report will finish doing it.
+      SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
+
       // If we're still not dead for some reason, use raw _exit() instead of
       // Die() to bypass any additional checks.
       internal__exit(common_flags()->exitcode);
+    } else {
+      // The other thread will eventually finish reporting
+      // so it's safe to wait
+      lock_.Lock();
+    }
+
+    StartReporting(report);
+  }
+
+  ~ScopedInErrorReport() {
+    // Make sure the current thread is announced.
+    DescribeThread(GetCurrentThread());
+    // We may want to grab this lock again when printing stats.
+    asanThreadRegistry().Unlock();
+    // Print memory stats.
+    if (flags()->print_stats)
+      __asan_print_accumulated_stats();
+
+    // Copy the message buffer so that we could start logging without holding a
+    // lock that gets aquired during printing.
+    InternalScopedBuffer<char> buffer_copy(kErrorMessageBufferSize);
+    {
+      BlockingMutexLock l(&error_message_buf_mutex);
+      internal_memcpy(buffer_copy.data(),
+                      error_message_buffer, kErrorMessageBufferSize);
+    }
+
+    LogFullErrorReport(buffer_copy.data());
+
+    if (error_report_callback) {
+      error_report_callback(buffer_copy.data());
+    }
+    CommonSanitizerReportMutex.Unlock();
+    reporting_thread_tid_ = kInvalidTid;
+    lock_.Unlock();
+    if (halt_on_error_) {
+      Report("ABORTING\n");
+      Die();
     }
+  }
+
+ private:
+  void StartReporting(ReportData *report) {
     if (report) report_data = *report;
     report_happened = true;
     ASAN_ON_ERROR();
@@ -651,27 +721,19 @@ class ScopedInErrorReport {
     // recursive reports.
     asanThreadRegistry().Lock();
     CommonSanitizerReportMutex.Lock();
-    reporting_thread_tid = GetCurrentTidOrInvalid();
+    reporting_thread_tid_ = GetCurrentTidOrInvalid();
     Printf("===================================================="
            "=============\n");
   }
-  // Destructor is NORETURN, as functions that report errors are.
-  NORETURN ~ScopedInErrorReport() {
-    // Make sure the current thread is announced.
-    DescribeThread(GetCurrentThread());
-    // We may want to grab this lock again when printing stats.
-    asanThreadRegistry().Unlock();
-    // Print memory stats.
-    if (flags()->print_stats)
-      __asan_print_accumulated_stats();
-    if (error_report_callback) {
-      error_report_callback(error_message_buffer);
-    }
-    Report("ABORTING\n");
-    Die();
-  }
+
+  static StaticSpinMutex lock_;
+  static u32 reporting_thread_tid_;
+  bool halt_on_error_;
 };
 
+StaticSpinMutex ScopedInErrorReport::lock_;
+u32 ScopedInErrorReport::reporting_thread_tid_;
+
 void ReportStackOverflow(const SignalContext &sig) {
   ScopedInErrorReport in_report;
   Decorator d;
@@ -688,7 +750,7 @@ void ReportStackOverflow(const SignalContext &sig) {
 }
 
 void ReportDeadlySignal(const char *description, const SignalContext &sig) {
-  ScopedInErrorReport in_report;
+  ScopedInErrorReport in_report(/*report*/nullptr, /*fatal*/true);
   Decorator d;
   Printf("%s", d.Warning());
   Report(
@@ -745,7 +807,7 @@ void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
   stack.Print();
   DescribeHeapAddress(addr, 1);
   ReportErrorSummary("new-delete-type-mismatch", &stack);
-  Report("HINT: if you don't care about these warnings you may set "
+  Report("HINT: if you don't care about these errors you may set "
          "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
 }
 
@@ -785,7 +847,7 @@ void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
   stack.Print();
   DescribeHeapAddress(addr, 1);
   ReportErrorSummary("alloc-dealloc-mismatch", &stack);
-  Report("HINT: if you don't care about these warnings you may set "
+  Report("HINT: if you don't care about these errors you may set "
          "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
 }
 
@@ -887,7 +949,7 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
     Printf("  [2]:\n");
     StackDepotGet(stack_id2).Print();
   }
-  Report("HINT: if you don't care about these warnings you may set "
+  Report("HINT: if you don't care about these errors you may set "
          "ASAN_OPTIONS=detect_odr_violation=0\n");
   InternalScopedString error_msg(256);
   error_msg.append("odr-violation: global '%s' at %s",
@@ -926,17 +988,6 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
 }
 // ----------------------- Mac-specific reports ----------------- {{{1
 
-void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
-                            BufferedStackTrace *stack) {
-  // Just print a warning here.
-  Printf("free_common(%p) -- attempting to free unallocated memory.\n"
-             "AddressSanitizer is ignoring this error on Mac OS now.\n",
-             addr);
-  PrintZoneForPointer(addr, zone_ptr, zone_name);
-  stack->Print();
-  DescribeHeapAddress(addr, 1);
-}
-
 void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
                                BufferedStackTrace *stack) {
   ScopedInErrorReport in_report;
@@ -948,24 +999,23 @@ void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
   DescribeHeapAddress(addr, 1);
 }
 
-void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
-                               BufferedStackTrace *stack) {
-  ScopedInErrorReport in_report;
-  Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
-             "This is an unrecoverable problem, exiting now.\n",
-             addr);
-  PrintZoneForPointer(addr, zone_ptr, zone_name);
-  stack->Print();
-  DescribeHeapAddress(addr, 1);
+// -------------- SuppressErrorReport -------------- {{{1
+// Avoid error reports duplicating for ASan recover mode.
+static bool SuppressErrorReport(uptr pc) {
+  if (!common_flags()->suppress_equal_pcs) return false;
+  for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) {
+    uptr cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]);
+    if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp,
+                                                   pc, memory_order_relaxed))
+      return false;
+    if (cmp == pc) return true;
+  }
+  Die();
 }
 
-} // namespace __asan
-
-// --------------------------- Interface --------------------- {{{1
-using namespace __asan;  // NOLINT
-
-void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
-                         uptr access_size, u32 exp) {
+void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
+                        uptr access_size, u32 exp, bool fatal) {
+  if (!fatal && SuppressErrorReport(pc)) return;
   ENABLE_FRAME_POINTER;
 
   // Optimization experiments.
@@ -1034,7 +1084,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
 
   ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
                         bug_descr };
-  ScopedInErrorReport in_report(&report);
+  ScopedInErrorReport in_report(&report, fatal);
 
   Decorator d;
   Printf("%s", d.Warning());
@@ -1060,14 +1110,21 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
   PrintShadowMemoryForAddress(addr);
 }
 
+}  // namespace __asan
+
+// --------------------------- Interface --------------------- {{{1
+using namespace __asan;  // NOLINT
+
+void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
+                         uptr access_size, u32 exp) {
+  ENABLE_FRAME_POINTER;
+  bool fatal = flags()->halt_on_error;
+  ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal);
+}
+
 void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
+  BlockingMutexLock l(&error_message_buf_mutex);
   error_report_callback = callback;
-  if (callback) {
-    error_message_buffer_size = 1 << 16;
-    error_message_buffer =
-        (char*)MmapOrDie(error_message_buffer_size, __func__);
-    error_message_buffer_pos = 0;
-  }
 }
 
 void __asan_describe_address(uptr addr) {
index e971eb1d6e1c20544469f2f0392d2b000ab3ca4a..559b8adfd51dabcffd13077c1d4d1ee642901f10 100644 (file)
@@ -49,45 +49,39 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size);
 void DescribeThread(AsanThreadContext *context);
 
 // Different kinds of error reports.
-void NORETURN ReportStackOverflow(const SignalContext &sig);
-void NORETURN ReportDeadlySignal(const char* description,
-                                 const SignalContext &sig);
-void NORETURN ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
-                                          BufferedStackTrace *free_stack);
-void NORETURN ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
-void NORETURN ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
-void NORETURN ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
-                                      AllocType alloc_type,
-                                      AllocType dealloc_type);
-void NORETURN
-    ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack);
-void NORETURN
-    ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
-                                            BufferedStackTrace *stack);
-void NORETURN
-    ReportStringFunctionMemoryRangesOverlap(const char *function,
-                                            const char *offset1, uptr length1,
-                                            const char *offset2, uptr length2,
-                                            BufferedStackTrace *stack);
-void NORETURN ReportStringFunctionSizeOverflow(uptr offset, uptr size,
-                                               BufferedStackTrace *stack);
-void NORETURN
-    ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
-                                                 uptr old_mid, uptr new_mid,
-                                                 BufferedStackTrace *stack);
+void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
+                        uptr access_size, u32 exp, bool fatal);
+void ReportStackOverflow(const SignalContext &sig);
+void ReportDeadlySignal(const char *description, const SignalContext &sig);
+void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
+                                 BufferedStackTrace *free_stack);
+void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
+void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
+void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
+                             AllocType alloc_type,
+                             AllocType dealloc_type);
+void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack);
+void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
+                                             BufferedStackTrace *stack);
+void ReportStringFunctionMemoryRangesOverlap(const char *function,
+                                             const char *offset1, uptr length1,
+                                             const char *offset2, uptr length2,
+                                             BufferedStackTrace *stack);
+void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
+                                      BufferedStackTrace *stack);
+void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
+                                                  uptr old_mid, uptr new_mid,
+                                                  BufferedStackTrace *stack);
 
-void NORETURN
-ReportODRViolation(const __asan_global *g1, u32 stack_id1,
-                   const __asan_global *g2, u32 stack_id2);
+void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
+                        const __asan_global *g2, u32 stack_id2);
 
 // Mac-specific errors and warnings.
-void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
-                            BufferedStackTrace *stack);
-void NORETURN ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
-                                        const char *zone_name,
-                                        BufferedStackTrace *stack);
-void NORETURN ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
-                                        const char *zone_name,
-                                        BufferedStackTrace *stack);
+void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
+                               const char *zone_name,
+                               BufferedStackTrace *stack);
+void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
+                               const char *zone_name,
+                               BufferedStackTrace *stack);
 
 }  // namespace __asan
index c744ac25195c5b44166a9eed1ab57dacf4226e00..7b8b5dd9be1b4ff4c3170dd7fe7f954c58a17564 100644 (file)
@@ -113,13 +113,18 @@ static void OnLowLevelAllocate(uptr ptr, uptr size) {
 extern "C" NOINLINE INTERFACE_ATTRIBUTE                             \
 void __asan_report_ ## type ## size(uptr addr) {                    \
   GET_CALLER_PC_BP_SP;                                              \
-  __asan_report_error(pc, bp, sp, addr, is_write, size, 0);         \
+  ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true);    \
 }                                                                   \
 extern "C" NOINLINE INTERFACE_ATTRIBUTE                             \
 void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) {       \
   GET_CALLER_PC_BP_SP;                                              \
-  __asan_report_error(pc, bp, sp, addr, is_write, size, exp);       \
-}
+  ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true);  \
+}                                                                   \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE                             \
+void __asan_report_ ## type ## size ## _noabort(uptr addr) {        \
+  GET_CALLER_PC_BP_SP;                                              \
+  ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false);   \
+}                                                                   \
 
 ASAN_REPORT_ERROR(load, false, 1)
 ASAN_REPORT_ERROR(load, false, 2)
@@ -132,22 +137,27 @@ ASAN_REPORT_ERROR(store, true, 4)
 ASAN_REPORT_ERROR(store, true, 8)
 ASAN_REPORT_ERROR(store, true, 16)
 
-#define ASAN_REPORT_ERROR_N(type, is_write)                    \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE                        \
-void __asan_report_ ## type ## _n(uptr addr, uptr size) {      \
-  GET_CALLER_PC_BP_SP;                                         \
-  __asan_report_error(pc, bp, sp, addr, is_write, size, 0);    \
-}                                                              \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE                        \
+#define ASAN_REPORT_ERROR_N(type, is_write)                                 \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE                                     \
+void __asan_report_ ## type ## _n(uptr addr, uptr size) {                   \
+  GET_CALLER_PC_BP_SP;                                                      \
+  ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true);            \
+}                                                                           \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE                                     \
 void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) {      \
   GET_CALLER_PC_BP_SP;                                                      \
-  __asan_report_error(pc, bp, sp, addr, is_write, size, exp);               \
-}
+  ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true);          \
+}                                                                           \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE                                     \
+void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) {           \
+  GET_CALLER_PC_BP_SP;                                                      \
+  ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false);           \
+}                                                                           \
 
 ASAN_REPORT_ERROR_N(load, false)
 ASAN_REPORT_ERROR_N(store, true)
 
-#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg)        \
+#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \
     uptr sp = MEM_TO_SHADOW(addr);                                             \
     uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp)          \
                                         : *reinterpret_cast<u16 *>(sp);        \
@@ -159,7 +169,8 @@ ASAN_REPORT_ERROR_N(store, true)
           *__asan_test_only_reported_buggy_pointer = addr;                     \
         } else {                                                               \
           GET_CALLER_PC_BP_SP;                                                 \
-          __asan_report_error(pc, bp, sp, addr, is_write, size, exp_arg);      \
+          ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg,        \
+                              fatal);                                          \
         }                                                                      \
       }                                                                        \
     }
@@ -167,12 +178,16 @@ ASAN_REPORT_ERROR_N(store, true)
 #define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size)                      \
   extern "C" NOINLINE INTERFACE_ATTRIBUTE                                      \
   void __asan_##type##size(uptr addr) {                                        \
-    ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0)                  \
+    ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, true)            \
   }                                                                            \
   extern "C" NOINLINE INTERFACE_ATTRIBUTE                                      \
   void __asan_exp_##type##size(uptr addr, u32 exp) {                           \
-    ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp)                \
-  }
+    ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true)          \
+  }                                                                            \
+  extern "C" NOINLINE INTERFACE_ATTRIBUTE                                      \
+  void __asan_##type##size ## _noabort(uptr addr) {                            \
+    ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false)           \
+  }                                                                            \
 
 ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1)
 ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2)
@@ -190,7 +205,7 @@ NOINLINE INTERFACE_ATTRIBUTE
 void __asan_loadN(uptr addr, uptr size) {
   if (__asan_region_is_poisoned(addr, size)) {
     GET_CALLER_PC_BP_SP;
-    __asan_report_error(pc, bp, sp, addr, false, size, 0);
+    ReportGenericError(pc, bp, sp, addr, false, size, 0, true);
   }
 }
 
@@ -199,7 +214,16 @@ NOINLINE INTERFACE_ATTRIBUTE
 void __asan_exp_loadN(uptr addr, uptr size, u32 exp) {
   if (__asan_region_is_poisoned(addr, size)) {
     GET_CALLER_PC_BP_SP;
-    __asan_report_error(pc, bp, sp, addr, false, size, exp);
+    ReportGenericError(pc, bp, sp, addr, false, size, exp, true);
+  }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_loadN_noabort(uptr addr, uptr size) {
+  if (__asan_region_is_poisoned(addr, size)) {
+    GET_CALLER_PC_BP_SP;
+    ReportGenericError(pc, bp, sp, addr, false, size, 0, false);
   }
 }
 
@@ -208,7 +232,7 @@ NOINLINE INTERFACE_ATTRIBUTE
 void __asan_storeN(uptr addr, uptr size) {
   if (__asan_region_is_poisoned(addr, size)) {
     GET_CALLER_PC_BP_SP;
-    __asan_report_error(pc, bp, sp, addr, true, size, 0);
+    ReportGenericError(pc, bp, sp, addr, true, size, 0, true);
   }
 }
 
@@ -217,7 +241,16 @@ NOINLINE INTERFACE_ATTRIBUTE
 void __asan_exp_storeN(uptr addr, uptr size, u32 exp) {
   if (__asan_region_is_poisoned(addr, size)) {
     GET_CALLER_PC_BP_SP;
-    __asan_report_error(pc, bp, sp, addr, true, size, exp);
+    ReportGenericError(pc, bp, sp, addr, true, size, exp, true);
+  }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_storeN_noabort(uptr addr, uptr size) {
+  if (__asan_region_is_poisoned(addr, size)) {
+    GET_CALLER_PC_BP_SP;
+    ReportGenericError(pc, bp, sp, addr, true, size, 0, false);
   }
 }
 
@@ -293,6 +326,8 @@ static void InitializeHighMemEnd() {
 }
 
 static void ProtectGap(uptr addr, uptr size) {
+  if (!flags()->protect_shadow_gap)
+    return;
   void *res = MmapNoAccess(addr, size, "shadow gap");
   if (addr == (uptr)res)
     return;
@@ -378,8 +413,6 @@ static void AsanInitInternal() {
   // initialization steps look at flags().
   InitializeFlags();
 
-  CheckVMASize();
-
   AsanCheckIncompatibleRT();
   AsanCheckDynamicRTPrereqs();
 
@@ -572,7 +605,7 @@ void NOINLINE __asan_handle_no_return() {
            "stack top: %p; bottom %p; size: %p (%zd)\n"
            "False positive error reports may follow\n"
            "For details see "
-           "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n",
+           "https://github.com/google/sanitizers/issues/189\n",
            top, bottom, top - bottom, top - bottom);
     return;
   }
index 28ec0768078d8ffc06c3d68dda1b9fcb4fef95f9..92bd893d10ef4d13130c408b90d2bd1fb2b46f53 100644 (file)
@@ -175,14 +175,6 @@ void PlatformTSDDtor(void *tsd) {
 // }}}
 
 // ---------------------- Various stuff ---------------- {{{
-void DisableReexec() {
-  // No need to re-exec on Windows.
-}
-
-void MaybeReexec() {
-  // No need to re-exec on Windows.
-}
-
 void *AsanDoesNotSupportStaticLinkage() {
 #if defined(_DEBUG)
 #error Please build the runtime with a non-debug CRT: /MD or /MT
index b8d0798960c133b8f3aeb66cc0ce9a29f2f2e716..672cabf43ec1d57c7498cb1a757f36f672f25c51 100644 (file)
@@ -12,8 +12,7 @@
 // This file defines a family of thunks that should be statically linked into
 // the DLLs that have ASan instrumentation in order to delegate the calls to the
 // shared runtime that lives in the main binary.
-// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the
-// details.
+// See https://github.com/google/sanitizers/issues/209 for the details.
 //===----------------------------------------------------------------------===//
 
 // Only compile this code when buidling asan_dll_thunk.lib
@@ -257,6 +256,9 @@ INTERFACE_FUNCTION(__asan_memcpy);
 INTERFACE_FUNCTION(__asan_memset);
 INTERFACE_FUNCTION(__asan_memmove);
 
+INTERFACE_FUNCTION(__asan_alloca_poison);
+INTERFACE_FUNCTION(__asan_allocas_unpoison);
+
 INTERFACE_FUNCTION(__asan_register_globals)
 INTERFACE_FUNCTION(__asan_unregister_globals)
 
@@ -300,6 +302,7 @@ INTERFACE_FUNCTION(__asan_stack_free_10)
 
 // FIXME: we might want to have a sanitizer_win_dll_thunk?
 INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
+INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
 INTERFACE_FUNCTION(__sanitizer_cov)
 INTERFACE_FUNCTION(__sanitizer_cov_dump)
 INTERFACE_FUNCTION(__sanitizer_cov_indir_call16)
@@ -312,11 +315,13 @@ INTERFACE_FUNCTION(__sanitizer_cov_trace_switch)
 INTERFACE_FUNCTION(__sanitizer_cov_with_check)
 INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
 INTERFACE_FUNCTION(__sanitizer_get_coverage_guards)
+INTERFACE_FUNCTION(__sanitizer_get_coverage_pc_buffer)
 INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes)
 INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size)
 INTERFACE_FUNCTION(__sanitizer_get_free_bytes)
 INTERFACE_FUNCTION(__sanitizer_get_heap_size)
 INTERFACE_FUNCTION(__sanitizer_get_ownership)
+INTERFACE_FUNCTION(__sanitizer_get_total_unique_caller_callee_pairs)
 INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
 INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
 INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)
index d5a14088dac4540bcdfb0a219a275b593ca704bb..73e5207bb334b4351ab94a2de184d4c053d4eaab 100644 (file)
@@ -59,6 +59,7 @@ int __asan_option_detect_stack_use_after_return =
 // using atexit() that calls a small subset of C terminators
 // where LLVM global_dtors is placed.  Fingers crossed, no other C terminators
 // are there.
+extern "C" int __cdecl atexit(void (__cdecl *f)(void));
 extern "C" void __cdecl _initterm(void *a, void *b);
 
 namespace {
index 338b8d501b00676e52d34f783620129b9a667f14..6cb7b94c21978924e750d6c36f5f56b84b7cefbe 100755 (executable)
@@ -222,8 +222,13 @@ elif [[ -f "$HERE/$ASAN_RT" ]]; then
     ASAN_RT_PATH="$HERE"
 elif [[ $(basename "$HERE") == "bin" ]]; then
     # We could be in the toolchain's base directory.
-    # Consider ../lib, ../lib/asan, ../lib/linux and ../lib/clang/$VERSION/lib/linux.
-    P=$(ls "$HERE"/../lib/"$ASAN_RT" "$HERE"/../lib/asan/"$ASAN_RT" "$HERE"/../lib/linux/"$ASAN_RT" "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1)
+    # Consider ../lib, ../lib/asan, ../lib/linux,
+    # ../lib/clang/$VERSION/lib/linux, and ../lib64/clang/$VERSION/lib/linux.
+    P=$(ls "$HERE"/../lib/"$ASAN_RT" \
+           "$HERE"/../lib/asan/"$ASAN_RT" \
+           "$HERE"/../lib/linux/"$ASAN_RT" \
+           "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" \
+           "$HERE"/../lib64/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1)
     if [[ -n "$P" ]]; then
         ASAN_RT_PATH="$(dirname "$P")"
     fi
index e6d43cd3f4ad7d0c0d4f946b3a7feb52fae9d5e0..8e6fb61f7bf7ed8f18bcabaed5c5de0ea1ee5c51 100755 (executable)
@@ -271,7 +271,7 @@ def BreakpadSymbolizerFactory(binary):
 def SystemSymbolizerFactory(system, addr, binary):
   if system == 'Darwin':
     return DarwinSymbolizer(addr, binary)
-  elif system == 'Linux':
+  elif system == 'Linux' or system == 'FreeBSD':
     return Addr2LineSymbolizer(binary)
 
 
index cabdfd711ea2f367bfeee0da8cf40cfcbc21499e..dfa6d7596d742c1456b1371566d76cd44e8118b2 100644 (file)
@@ -216,12 +216,12 @@ TEST(AddressSanitizerMac, NSObjectOOB) {
 
 // Make sure that correct pointer is passed to free() when deallocating a
 // NSURL object.
-// See http://code.google.com/p/address-sanitizer/issues/detail?id=70.
+// See https://github.com/google/sanitizers/issues/70.
 TEST(AddressSanitizerMac, NSURLDeallocation) {
   TestNSURLDeallocation();
 }
 
-// See http://code.google.com/p/address-sanitizer/issues/detail?id=109.
+// See https://github.com/google/sanitizers/issues/109.
 TEST(AddressSanitizerMac, Mstats) {
   malloc_statistics_t stats1, stats2;
   malloc_zone_statistics(/*all zones*/NULL, &stats1);
index 6a428fbbc2b9dd2ff3ef0defda95b76bbceefb58..5f5354f92caf996ed55e064422b4fa95b6e07bda 100644 (file)
@@ -34,7 +34,7 @@
 // Make sure __asan_init is called before any test case is run.
 struct AsanInitCaller {
   AsanInitCaller() {
-    __asan::DisableReexec();
+    DisableReexec();
     __asan_init();
   }
 };
index ed796d6259072707a86cd9963e6049ae3566e598..71fb27a0ca1199f5e7a7ac0da02a408ad73b02c5 100644 (file)
@@ -610,7 +610,7 @@ NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) {
 }
 
 // Does not work on Power and ARM:
-// https://code.google.com/p/address-sanitizer/issues/detail?id=185
+// https://github.com/google/sanitizers/issues/185
 TEST(AddressSanitizer, BuiltinLongJmpTest) {
   static jmp_buf buf;
   if (!__builtin_setjmp((void**)buf)) {
@@ -1156,9 +1156,9 @@ TEST(AddressSanitizer, AttributeNoSanitizeAddressTest) {
 // The new/delete/etc mismatch checks don't work on Android,
 //   as calls to new/delete go through malloc/free.
 // OS X support is tracked here:
-//   https://code.google.com/p/address-sanitizer/issues/detail?id=131
+//   https://github.com/google/sanitizers/issues/131
 // Windows support is tracked here:
-//   https://code.google.com/p/address-sanitizer/issues/detail?id=309
+//   https://github.com/google/sanitizers/issues/309
 #if !defined(__ANDROID__) && \
     !defined(__APPLE__) && \
     !defined(_WIN32)
@@ -1255,7 +1255,7 @@ TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) {
   }
 }
 
-// http://code.google.com/p/address-sanitizer/issues/detail?id=66
+// https://github.com/google/sanitizers/issues/66
 TEST(AddressSanitizer, BufferOverflowAfterManyFrees) {
   for (int i = 0; i < 1000000; i++) {
     delete [] (Ident(new char [8644]));
index 5958cb8a065f7c98ea47336c5e27728e7c6bccd2..cdaf801d914ba729ba196962dca4553519d143d0 100644 (file)
@@ -19,7 +19,8 @@ extern "C" const char* __asan_default_options() {
 #if SANITIZER_MAC
   // On Darwin, we default to `abort_on_error=1`, which would make tests run
   // much slower. Let's override this and run lit tests with 'abort_on_error=0'.
-  return "symbolize=false:abort_on_error=0";
+  // Also, make sure we do not overwhelm the syslog while testing.
+  return "symbolize=false:abort_on_error=0:log_to_syslog=0";
 #else
   return "symbolize=false";
 #endif
index 9f9f4304d7347493109d3ddc9019e18b98caa687..f87a19c8063cfdf4c81ae1c29f489703becc39ae 100644 (file)
@@ -2,6 +2,9 @@
 # generic implementations of the core runtime library along with optimized
 # architecture-specific code in various subdirectories.
 
+# TODO: Need to add a mechanism for logging errors when builtin source files are
+# added to a sub-directory and not this CMakeLists file.
+
 set(GENERIC_SOURCES
   absvdi2.c
   absvsi2.c
@@ -150,7 +153,7 @@ if(APPLE)
     atomic_thread_fence.c)
 endif()
 
-if(NOT WIN32)
+if(NOT WIN32 OR MINGW)
   set(GENERIC_SOURCES
       ${GENERIC_SOURCES}
       emutls.c)
@@ -164,6 +167,8 @@ endif ()
 
 if (NOT MSVC)
   set(x86_64_SOURCES
+      x86_64/chkstk.S
+      x86_64/chkstk2.S
       x86_64/floatdidf.c
       x86_64/floatdisf.c
       x86_64/floatdixf.c
@@ -183,6 +188,8 @@ if (NOT MSVC)
   set(i386_SOURCES
       i386/ashldi3.S
       i386/ashrdi3.S
+      i386/chkstk.S
+      i386/chkstk2.S
       i386/divdi3.S
       i386/floatdidf.S
       i386/floatdisf.S
@@ -219,7 +226,8 @@ else () # MSVC
   set(i686_SOURCES ${i386_SOURCES})
 endif () # if (NOT MSVC)
 
-set(arm_SOURCES
+# These are sources that should be appropriate for any ARM platform.
+set(arm_GENERIC_SOURCES
   arm/aeabi_cdcmp.S
   arm/aeabi_cdcmpeq_check_nan.c
   arm/aeabi_cfcmp.S
@@ -239,36 +247,16 @@ set(arm_SOURCES
   arm/aeabi_uldivmod.S
   arm/bswapdi2.S
   arm/bswapsi2.S
+  arm/clzdi2.S
+  arm/clzsi2.S
   arm/comparesf2.S
   arm/divmodsi4.S
   arm/divsi3.S
   arm/modsi3.S
-  arm/negdf2vfp.S
-  arm/negsf2vfp.S
   arm/switch16.S
   arm/switch32.S
   arm/switch8.S
   arm/switchu8.S
-  arm/sync_fetch_and_add_4.S
-  arm/sync_fetch_and_add_8.S
-  arm/sync_fetch_and_and_4.S
-  arm/sync_fetch_and_and_8.S
-  arm/sync_fetch_and_max_4.S
-  arm/sync_fetch_and_max_8.S
-  arm/sync_fetch_and_min_4.S
-  arm/sync_fetch_and_min_8.S
-  arm/sync_fetch_and_nand_4.S
-  arm/sync_fetch_and_nand_8.S
-  arm/sync_fetch_and_or_4.S
-  arm/sync_fetch_and_or_8.S
-  arm/sync_fetch_and_sub_4.S
-  arm/sync_fetch_and_sub_8.S
-  arm/sync_fetch_and_umax_4.S
-  arm/sync_fetch_and_umax_8.S
-  arm/sync_fetch_and_umin_4.S
-  arm/sync_fetch_and_umin_8.S
-  arm/sync_fetch_and_xor_4.S
-  arm/sync_fetch_and_xor_8.S
   arm/sync_synchronize.S
   arm/udivmodsi4.S
   arm/udivsi3.S
@@ -294,7 +282,32 @@ set(aarch64_SOURCES
   trunctfsf2.c
   ${GENERIC_SOURCES})
 
-set(armhf_SOURCES
+# These are sources for the ARM platform that require Thumb2 instructions
+# support.
+set(arm_THUMB_SOURCES
+  arm/sync_fetch_and_add_4.S
+  arm/sync_fetch_and_add_8.S
+  arm/sync_fetch_and_and_4.S
+  arm/sync_fetch_and_and_8.S
+  arm/sync_fetch_and_max_4.S
+  arm/sync_fetch_and_max_8.S
+  arm/sync_fetch_and_min_4.S
+  arm/sync_fetch_and_min_8.S
+  arm/sync_fetch_and_nand_4.S
+  arm/sync_fetch_and_nand_8.S
+  arm/sync_fetch_and_or_4.S
+  arm/sync_fetch_and_or_8.S
+  arm/sync_fetch_and_sub_4.S
+  arm/sync_fetch_and_sub_8.S
+  arm/sync_fetch_and_umax_4.S
+  arm/sync_fetch_and_umax_8.S
+  arm/sync_fetch_and_umin_4.S
+  arm/sync_fetch_and_umin_8.S
+  arm/sync_fetch_and_xor_4.S
+  arm/sync_fetch_and_xor_8.S)
+
+# These are sources for the ARM platform that require VFP instructions support.
+set(arm_VFP_SOURCES
   arm/adddf3vfp.S
   arm/addsf3vfp.S
   arm/divdf3vfp.S
@@ -320,6 +333,8 @@ set(armhf_SOURCES
   arm/ltsf2vfp.S
   arm/muldf3vfp.S
   arm/mulsf3vfp.S
+  arm/negdf2vfp.S
+  arm/negsf2vfp.S
   arm/nedf2vfp.S
   arm/nesf2vfp.S
   arm/restore_vfp_d8_d15_regs.S
@@ -328,22 +343,31 @@ set(armhf_SOURCES
   arm/subsf3vfp.S
   arm/truncdfsf2vfp.S
   arm/unorddf2vfp.S
-  arm/unordsf2vfp.S
-  ${arm_SOURCES})
-set(armv7_SOURCES ${arm_SOURCES})
-set(armv7s_SOURCES ${arm_SOURCES})
+  arm/unordsf2vfp.S)
+
+set(arm_SOURCES ${arm_GENERIC_SOURCES})
+set(armhf_SOURCES ${arm_GENERIC_SOURCES} ${arm_VFP_SOURCES})
+set(armv7_SOURCES ${arm_GENERIC_SOURCES} ${arm_VFP_SOURCES} ${arm_THUMB_SOURCES})
+set(armv7s_SOURCES ${arm_GENERIC_SOURCES} ${arm_VFP_SOURCES} ${arm_THUMB_SOURCES})
 set(arm64_SOURCES ${aarch64_SOURCES})
 
 # macho_embedded archs
 set(armv6m_SOURCES ${GENERIC_SOURCES})
-set(armv7m_SOURCES ${arm_SOURCES})
-set(armv7em_SOURCES ${arm_SOURCES})
+set(armv7m_SOURCES ${arm_GENERIC_SOURCES} ${arm_THUMB_SOURCES})
+set(armv7em_SOURCES ${arm_GENERIC_SOURCES} ${arm_THUMB_SOURCES})
 
 set(mips_SOURCES ${GENERIC_SOURCES})
 set(mipsel_SOURCES ${mips_SOURCES})
 set(mips64_SOURCES ${mips_SOURCES})
 set(mips64el_SOURCES ${mips_SOURCES})
 
+set(powerpc_SOURCES ${GENERIC_SOURCES})
+set(powerpc64_SOURCES ${GENERIC_SOURCES})
+set(powerpc64le_SOURCES ${GENERIC_SOURCES})
+
+set(wasm32_SOURCES ${GENERIC_SOURCES})
+set(wasm64_SOURCES ${GENERIC_SOURCES})
+
 add_custom_target(builtins)
 
 if (APPLE)
@@ -351,6 +375,8 @@ if (APPLE)
   add_subdirectory(macho_embedded)
   darwin_add_builtin_libraries(${BUILTIN_SUPPORTED_OS})
 else ()
+  append_string_if(COMPILER_RT_HAS_STD_C99_FLAG -std=c99 maybe_stdc99)
+
   foreach (arch ${BUILTIN_SUPPORTED_ARCH})
     if (CAN_TARGET_${arch})
       # Filter out generic versions of routines that are re-implemented in
@@ -366,16 +392,14 @@ else ()
 
       # Rust: don't insert a reference to MSVCRT.lib/etc
       if (MSVC)
-        set(_cflags -Zl)
-      else ()
-        set(_cflags -std=c99)
+        set(maybe_zl -Zl)
       endif ()
 
       add_compiler_rt_runtime(clang_rt.builtins
                               STATIC
                               ARCHS ${arch}
                               SOURCES ${${arch}_SOURCES}
-                              CFLAGS ${_cflags}
+                              CFLAGS ${maybe_stdc99} ${maybe_zl}
                               PARENT_TARGET builtins)
     endif ()
   endforeach ()
index 21cd778eb358c1255b7d758f1856566cff0833ed..6aa542f7fe4adbb41e6201e8b301fcb0a8e667c4 100644 (file)
@@ -1,3 +1,6 @@
+absvti2
+addtf3
+addvti3
 aeabi_cdcmp
 aeabi_cdcmpeq_check_nan
 aeabi_cfcmp
@@ -15,57 +18,13 @@ aeabi_memmove
 aeabi_memset
 aeabi_uidivmod
 aeabi_uldivmod
-absvti2
-addtf3
-addvti3
 ashlti3
 ashrti3
 clzti2
 cmpti2
 ctzti2
-divti3
 divtf3
-ffsti2
-fixdfti
-fixsfti
-fixunsdfti
-fixunssfti
-fixunsxfti
-fixxfti
-floattidf
-floattisf
-floattixf
-floatuntidf
-floatuntisf
-floatuntixf
-lshrti3
-modti3
-muloti4
-multi3
-multf3
-mulvti3
-negti2
-negvti2
-parityti2
-popcountti2
-powitf2
-subvti3
-subtf3
-trampoline_setup
-ucmpti2
-udivmodti4
-udivti3
-umodti3
-absvti2
-addtf3
-addvti3
-ashlti3
-ashrti3
-clzti2
-cmpti2
-ctzti2
 divti3
-divtf3
 ffsti2
 fixdfti
 fixsfti
@@ -81,17 +40,16 @@ floatuntisf
 floatuntixf
 lshrti3
 modti3
-muloti4
-multi3
 multf3
+multi3
 mulvti3
 negti2
 negvti2
 parityti2
 popcountti2
 powitf2
-subvti3
 subtf3
+subvti3
 trampoline_setup
 ucmpti2
 udivmodti4
index 21cd778eb358c1255b7d758f1856566cff0833ed..28167aa4c5db7551a6d85fa0b1d410a0ac0ff467 100644 (file)
@@ -1,3 +1,6 @@
+absvti2
+addtf3
+addvti3
 aeabi_cdcmp
 aeabi_cdcmpeq_check_nan
 aeabi_cfcmp
@@ -15,57 +18,13 @@ aeabi_memmove
 aeabi_memset
 aeabi_uidivmod
 aeabi_uldivmod
-absvti2
-addtf3
-addvti3
 ashlti3
 ashrti3
 clzti2
 cmpti2
 ctzti2
-divti3
 divtf3
-ffsti2
-fixdfti
-fixsfti
-fixunsdfti
-fixunssfti
-fixunsxfti
-fixxfti
-floattidf
-floattisf
-floattixf
-floatuntidf
-floatuntisf
-floatuntixf
-lshrti3
-modti3
-muloti4
-multi3
-multf3
-mulvti3
-negti2
-negvti2
-parityti2
-popcountti2
-powitf2
-subvti3
-subtf3
-trampoline_setup
-ucmpti2
-udivmodti4
-udivti3
-umodti3
-absvti2
-addtf3
-addvti3
-ashlti3
-ashrti3
-clzti2
-cmpti2
-ctzti2
 divti3
-divtf3
 ffsti2
 fixdfti
 fixsfti
@@ -81,17 +40,16 @@ floatuntisf
 floatuntixf
 lshrti3
 modti3
-muloti4
+multf
 multi3
-multf3
 mulvti3
 negti2
 negvti2
 parityti2
 popcountti2
 powitf2
-subvti3
 subtf3
+subvti3
 trampoline_setup
 ucmpti2
 udivmodti4
index 2825ae92cd5a94306639194c69d3925c66292e16..f4c00a03e05f14da418f3d81eb672ca444592e21 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__adddf3vfp)
        vmov    r0, r1, d6              // move result back to r0/r1 pair
        bx      lr
 END_COMPILERRT_FUNCTION(__adddf3vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index bff5a7e0fbe81e70931bdb9f69925305a4ba83e5..af40c1cc92af77c8771054e0afc5fca8dadcdcdb 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__addsf3vfp)
        vmov    r0, s14         // move result back to r0
        bx      lr
 END_COMPILERRT_FUNCTION(__addsf3vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 036a6f542f79311c1fa41997f42bd9ea7f8eb004..8008f5fca2627d8e47d06a7f16f35cd7b8d9f212 100644 (file)
@@ -94,3 +94,5 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdrcmple)
         b __aeabi_cdcmple
 END_COMPILERRT_FUNCTION(__aeabi_cdrcmple)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 43594e5c393608b0fa7630c8d92dbeca60b2b19f..274baf7aecf2ed992e5b07596212b8426bffaf12 100644 (file)
@@ -89,3 +89,5 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfrcmple)
         b __aeabi_cfcmple
 END_COMPILERRT_FUNCTION(__aeabi_cfrcmple)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 310c35b74932d915efa6c13a5dfce3b7b667dedc..43e439268d9ac70d9035502605d7b3939a4f51b7 100644 (file)
@@ -38,3 +38,6 @@ DEFINE_AEABI_DCMP(lt)
 DEFINE_AEABI_DCMP(le)
 DEFINE_AEABI_DCMP(ge)
 DEFINE_AEABI_DCMP(gt)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 55f49a2b5af68ab02cc502ac9ad6165203e68ebd..0a1d92a60b6828c0a5116742c901aaf25825d55a 100644 (file)
@@ -38,3 +38,6 @@ DEFINE_AEABI_FCMP(lt)
 DEFINE_AEABI_FCMP(le)
 DEFINE_AEABI_FCMP(ge)
 DEFINE_AEABI_FCMP(gt)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 384add38279ec55813496548987d6bb4e7a88d07..2fcad862f73a2eef793ad3c450eb8f1610e10513 100644 (file)
@@ -26,3 +26,6 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_idivmod)
         add     sp, sp, #4
         pop     { pc }
 END_COMPILERRT_FUNCTION(__aeabi_idivmod)
+
+NO_EXEC_STACK_DIRECTIVE
+
index ad06f1de2af45d95df2ae4fa240743b988608bbd..9f161f3007f6487111c6faf29730e9b0b316274d 100644 (file)
@@ -29,3 +29,6 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod)
         add     sp, sp, #16
         pop     {r11, pc}
 END_COMPILERRT_FUNCTION(__aeabi_ldivmod)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 051ce435bab9309b16bc29a851b941b05179a5a0..8f49d06891f3173b3724853fa30a939bbf0cc329 100644 (file)
@@ -18,3 +18,6 @@ END_COMPILERRT_FUNCTION(__aeabi_memcmp)
 
 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcmp4, __aeabi_memcmp)
 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcmp8, __aeabi_memcmp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index cf02332490a1224c6047a6ac44b718b24e13d2b9..bc8486364267f5d7df3665393c773821506d6e39 100644 (file)
@@ -18,3 +18,6 @@ END_COMPILERRT_FUNCTION(__aeabi_memcpy)
 
 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcpy4, __aeabi_memcpy)
 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcpy8, __aeabi_memcpy)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 4dda06f75d04b559d04228f8a18d78a75eafd8b9..1bf08c0d5b75f058099f406ea4fbf3c006927b52 100644 (file)
@@ -18,3 +18,6 @@ END_COMPILERRT_FUNCTION(__aeabi_memmove)
 
 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memmove4, __aeabi_memmove)
 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memmove8, __aeabi_memmove)
+
+NO_EXEC_STACK_DIRECTIVE
+
index c8b49c7809a6224246f3a3f273535b53d27995db..c1d655a559dad17a58231a031a88de8eb36fa8c0 100644 (file)
@@ -32,3 +32,5 @@ END_COMPILERRT_FUNCTION(__aeabi_memclr)
 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memclr4, __aeabi_memclr)
 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memclr8, __aeabi_memclr)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 8ea474d91c6b5eb76858138b5d39e69ffbc956d1..e1e12d97aa00135cf06d7dc18f8edf845f5117d5 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_uidivmod)
         add     sp, sp, #4
         pop     { pc }
 END_COMPILERRT_FUNCTION(__aeabi_uidivmod)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 4e1f8e2a673633b22b1403db1ec436947ceb1bcb..e8aaef282e905d4bfa29e193c02f4b1a76c7075b 100644 (file)
@@ -29,3 +29,6 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_uldivmod)
         add    sp, sp, #16
         pop    {r11, pc}
 END_COMPILERRT_FUNCTION(__aeabi_uldivmod)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 86f3bba8c2902d5b9b07a66ef9164205f7125382..fb226cea249eb705ebb1524fe3396c72e7415b4d 100644 (file)
@@ -45,3 +45,6 @@ DEFINE_COMPILERRT_FUNCTION(__bswapdi2)
     mov r1, r2  // r1 = r2 = rev(r0)
     JMP(lr)
 END_COMPILERRT_FUNCTION(__bswapdi2)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 59ba8158fd57845160ae7b65d460c74a855ff598..553c3c2e39c8f9f7bb74012051e788b12a862e3e 100644 (file)
@@ -37,3 +37,6 @@ DEFINE_COMPILERRT_FUNCTION(__bswapsi2)
 #endif
     JMP(lr)
 END_COMPILERRT_FUNCTION(__bswapsi2)
+
+NO_EXEC_STACK_DIRECTIVE
+
index a55abac0469b350755854fba1b30b22a1e035a3a..6068c176fd15727257b575cd83a53b27dcacd565 100644 (file)
@@ -95,3 +95,6 @@ DEFINE_COMPILERRT_FUNCTION(__clzdi2)
        JMP(lr)
 #endif // __ARM_FEATURE_CLZ
 END_COMPILERRT_FUNCTION(__clzdi2)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 1cd379bfb0a94b7c9dd4d9eab1e9fc9b8c533ee0..c2ba3a8cfcda903d1d77332b9c20b48f273a70b4 100644 (file)
@@ -74,3 +74,6 @@ DEFINE_COMPILERRT_FUNCTION(__clzsi2)
        JMP(lr)
 #endif // __ARM_FEATURE_CLZ
 END_COMPILERRT_FUNCTION(__clzsi2)
+
+NO_EXEC_STACK_DIRECTIVE
+
index cf71d36e0517c6a38c75b669e41f9f9995a9297d..52597b673f96eca694bbffa007d1ee1157157310 100644 (file)
@@ -146,3 +146,6 @@ DEFINE_COMPILERRT_FUNCTION(__unordsf2)
 END_COMPILERRT_FUNCTION(__unordsf2)
 
 DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_fcmpun, __unordsf2)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 6eebef167a2cf241b65ff1d3712fef17e15d3b49..928f53809f121a8de2bf9c5a1198f925f8fb06fb 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__divdf3vfp)
        vmov    r0, r1, d5              // move result back to r0/r1 pair
        bx      lr
 END_COMPILERRT_FUNCTION(__divdf3vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 646b9ab78fb6edfb4a28f3c832c299bc7f836bd9..999c310ec8a3a5a13b42d913bd9dbb81d84e79f1 100644 (file)
@@ -72,3 +72,6 @@ LOCAL_LABEL(divzero):
     CLEAR_FRAME_AND_RETURN
 #endif
 END_COMPILERRT_FUNCTION(__divmodsi4)
+
+NO_EXEC_STACK_DIRECTIVE
+
index fdbaebc88371c7600353b013d32d7c7b69e7bfb9..a2e297f701577cbb708eeea1d78d7e75029767e3 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__divsf3vfp)
        vmov    r0, s13         // move result back to r0
        bx      lr
 END_COMPILERRT_FUNCTION(__divsf3vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index adf8f94fc7b8d86eb628e620af85fd800206cf6b..7e23ba4fc2373373b66b324086235233174d9fb8 100644 (file)
@@ -63,3 +63,6 @@ ESTABLISH_FRAME
     CLEAR_FRAME_AND_RETURN
 #endif
 END_COMPILERRT_FUNCTION(__divsi3)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 7f2fbc3072d4563b33b3d03524c72ac50004e444..95e6bb36334b90e73ae8edbddc7e1f76d8cc168b 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__eqdf2vfp)
        movne   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__eqdf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index a318b336ae9e74b4a84a51d86dd7345f316d3d68..fbac139c193a166f28bb1d1b08b4785686f164ce 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__eqsf2vfp)
        movne   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__eqsf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index b998e589459e86e9f88082309a126157720a1fb1..563bf92afc36def2da625a3531cb17caf165c620 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__extendsfdf2vfp)
        vmov    r0, r1, d7   // return result in r0/r1 pair
        bx      lr
 END_COMPILERRT_FUNCTION(__extendsfdf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index e3bd8e05e01f5787c57c2a1a35a5a73e8da2c8be..8263ff942f8c5db977679a8a12aa4cfd2db4ed9e 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__fixdfsivfp)
        vmov    r0, s15       // move s15 to result register
        bx      lr
 END_COMPILERRT_FUNCTION(__fixdfsivfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 3d0d0f56d235ec950abca1b5cc313b61d850aaa4..c7c3b8117876c93b8238c6bbd63f3b9b278aff23 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__fixsfsivfp)
        vmov    r0, s15        // move s15 to result register
        bx      lr
 END_COMPILERRT_FUNCTION(__fixsfsivfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 35dda5b9b03497b8e404a2a7ad03dfe6eab22c64..9cc1e628699edafe20b7eb45694693a4671cd63b 100644 (file)
@@ -25,3 +25,6 @@ DEFINE_COMPILERRT_FUNCTION(__fixunsdfsivfp)
        vmov    r0, s15       // move s15 to result register
        bx      lr
 END_COMPILERRT_FUNCTION(__fixunsdfsivfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 5c3a7d926fccd7ac5002297d3d9e03bfae4981eb..79d7082291128a7c61a8912351cf805b4618a22f 100644 (file)
@@ -25,3 +25,6 @@ DEFINE_COMPILERRT_FUNCTION(__fixunssfsivfp)
        vmov    r0, s15        // move s15 to result register
        bx      lr
 END_COMPILERRT_FUNCTION(__fixunssfsivfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index d69184914ccd2b4e2e9e79bf58ffd4d3c323e9c9..7623f26c6e6d25ee60dee2178199a7d702f803da 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatsidfvfp)
        vmov    r0, r1, d7     // move d7 to result register pair r0/r1
        bx      lr
 END_COMPILERRT_FUNCTION(__floatsidfvfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 4a0cb39d0eb04bb3b59b0fe8377225b8bd622730..c73dfac13eb2bb279c8dc69d6688856f0f99dd43 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatsisfvfp)
        vmov    r0, s15        // move s15 to result register
        bx      lr
 END_COMPILERRT_FUNCTION(__floatsisfvfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index d92969ea3453a50123824a5c61d06d4abb95d64d..2a59fdb830b220001db49cdc9e6ba5fb1f784826 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatunssidfvfp)
        vmov    r0, r1, d7     // move d7 to result register pair r0/r1
        bx      lr
 END_COMPILERRT_FUNCTION(__floatunssidfvfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index f6aeba56ae158b98b66a2bbb8babf1ccc4ffeda1..c096263c1bca65144025b5dd57f9c31e5179180c 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatunssisfvfp)
        vmov    r0, s15        // move s15 to result register
        bx      lr
 END_COMPILERRT_FUNCTION(__floatunssisfvfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 9e235270175cde4ba191f5914cf21d8a8ac5624b..72f13ef4e7188b5e0c109a92ed9ce52c351bec22 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__gedf2vfp)
        movlt   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__gedf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 0ff6084778823aea68ae1a6d67d478efd1bec8ea..c9ee52c9c44973073ff5665673233b4ad242ab8c 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__gesf2vfp)
        movlt   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__gesf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 3dc5d5b59225d080505cf39244779f40ee8912eb..c7f277552fa6e38fc68402a9a930729f9f794aa8 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__gtdf2vfp)
        movle   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__gtdf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index ddd843acf59229e8db6987c531e3c33c66ad6417..7d49e4564a8b2b543db2775029bde99568e36f45 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__gtsf2vfp)
        movle   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__gtsf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index b06ff6db5a332c413279380320bb693dda5466ce..ca5b553f1153c1f20351ee95e9dbac2dfaab81fc 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__ledf2vfp)
        movhi   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__ledf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 9b33c0c53697d61010f3be71850f68662c06d8b9..f25422ece8f54bb94fa648088240d9a55198b464 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__lesf2vfp)
        movhi   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__lesf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 9f794b026a4ae7452bc0f3863682b76572da8074..6e2c0997c01b1d425936490145570174c1d5511a 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__ltdf2vfp)
        movpl   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__ltdf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index ba190d9d8dc2f22155885815dbfa2e13a1f7f928..95febb60672ad3251687bb2df7b6e7864e5827b5 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__ltsf2vfp)
        movpl   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__ltsf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 295a227d862e669c6c06bae3f8ca0860f9e622ae..1d302edc67bda1172e4da44be3a69325f142805f 100644 (file)
@@ -61,3 +61,6 @@ LOCAL_LABEL(divzero):
     CLEAR_FRAME_AND_RETURN
 #endif
 END_COMPILERRT_FUNCTION(__modsi3)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 636cc711ac1a17c6b09af0b7772b3d07161b5f0a..f638de1ad28adb33f543f4dce3b8bc5f3ccce276 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__muldf3vfp)
        vmov    r0, r1, d6         // move result back to r0/r1 pair
        bx      lr
 END_COMPILERRT_FUNCTION(__muldf3vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 7f4008266bff43e966e7ace1fede22ddf4b15665..bef58d3a0c89ad74410fa9ee0797b2e618cd64ed 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__mulsf3vfp)
        vmov    r0, s13         // move result back to r0
        bx      lr
 END_COMPILERRT_FUNCTION(__mulsf3vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 7ab2f5501ce013fe682d8b65ed7b572e48557a95..78cf529d665b4394a6d3872614bc17214132211e 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__nedf2vfp)
        moveq   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__nedf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 56d73c676176c9b9adcb7538e95d2cd360cd0fbd..01c8ba6a120f063125770acce1786070ddf78154 100644 (file)
@@ -21,3 +21,6 @@ DEFINE_COMPILERRT_FUNCTION(__negdf2vfp)
        eor     r1, r1, #-2147483648    // flip sign bit on double in r0/r1 pair
        bx      lr
 END_COMPILERRT_FUNCTION(__negdf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index a6e32e1ff89cfa897b6ab9b965c9edc4661f1e60..797abb32ead31e05b6e51f87d283a195aef84bbb 100644 (file)
@@ -21,3 +21,6 @@ DEFINE_COMPILERRT_FUNCTION(__negsf2vfp)
        eor     r0, r0, #-2147483648    // flip sign bit on float in r0
        bx      lr
 END_COMPILERRT_FUNCTION(__negsf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 9fe8ecdefb37e4d108de78cf3e664ced4cf47b18..554d3e467512305f3018bb0df99a0caf73a5ea02 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__nesf2vfp)
        moveq   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__nesf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 0f6ea51361663c87fbebb340b3b08c098ec17174..0692cf3e1b77ef58af976d9810bf3f8227ef9227 100644 (file)
@@ -31,3 +31,5 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__restore_vfp_d8_d15_regs)
        bx      lr                      // return to prolog
 END_COMPILERRT_FUNCTION(__restore_vfp_d8_d15_regs)
 
+NO_EXEC_STACK_DIRECTIVE
+
index f1d90e75808c9a5aceb4fd7c1c008935399ae142..544dd5467a4dc6d8f46242ef2be2026886d6278b 100644 (file)
@@ -31,3 +31,5 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__save_vfp_d8_d15_regs)
        bx      lr                      // return to prolog
 END_COMPILERRT_FUNCTION(__save_vfp_d8_d15_regs)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 5f3c0f70dbc4d83fda90f4fd41b0eaf99ea2cb60..1fc7d18c3d3c87e267d08f716cfa8fc53a716bc5 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__subdf3vfp)
        vmov    r0, r1, d6         // move result back to r0/r1 pair
        bx      lr
 END_COMPILERRT_FUNCTION(__subdf3vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index d6e06df51920f35115a68deae844c311a23c84a0..11fe386cd0d1c2b90050095c3888efd43952f6a4 100644 (file)
@@ -25,3 +25,6 @@ DEFINE_COMPILERRT_FUNCTION(__subsf3vfp)
        vmov    r0, s14         // move result back to r0
        bx      lr
 END_COMPILERRT_FUNCTION(__subsf3vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 3c3a6b10612431147416aeeace868b2ad4af508b..df9e38e176ce97e20d1d9a052315d2ec9cb870ed 100644 (file)
@@ -42,3 +42,5 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch16)
        bx      ip                      // jump to computed label
 END_COMPILERRT_FUNCTION(__switch16)
 
+NO_EXEC_STACK_DIRECTIVE
+
index b38cd2b764a4ff5fff3ca7c342b053bb26dcc5f2..d97b5361436d541c0aad1e2fd644621ecfd134a3 100644 (file)
@@ -42,3 +42,5 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch32)
        bx      ip                       // jump to computed label
 END_COMPILERRT_FUNCTION(__switch32)
 
+NO_EXEC_STACK_DIRECTIVE
+
index d7c20423def2c8a65c94f433ca010759e531ec22..4d9e0eaff8453c634f6e272e8e0fa05debe66dcb 100644 (file)
@@ -40,3 +40,5 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switch8)
        bx      ip                      // jump to computed label
 END_COMPILERRT_FUNCTION(__switch8)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 1844f11c604e71021fc8ad855d5775e408c39df9..4ffe35f0549b05d6fb6ccbb89822bf907cdae15c 100644 (file)
@@ -40,3 +40,5 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION(__switchu8)
        bx      ip                      // jump to computed label
 END_COMPILERRT_FUNCTION(__switchu8)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 54c33e2d26b7f80d8ee212eac624cad8f809b987..7877d6c46c111470bb711e879926a5fdde090528 100644 (file)
@@ -19,3 +19,5 @@
 
 SYNC_OP_4(add_4)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 5724bb148ba77578e69d89485da9f4b2c97ebb2b..1df07a342a1bcda4e869cf61c9abb6ec7ab8c404 100644 (file)
@@ -22,3 +22,5 @@
 SYNC_OP_8(add_8)
 #endif
 
+NO_EXEC_STACK_DIRECTIVE
+
index e2b77a1a87d42ffc4744fe217d8bb2a3cfee0384..720ff02279cdf7f636a9504acbe37f95e199660d 100644 (file)
@@ -17,3 +17,6 @@
 #define and_4(rD, rN, rM)  and rD, rN, rM
 
 SYNC_OP_4(and_4)
+
+NO_EXEC_STACK_DIRECTIVE
+
index a74163a8600b8ca259c321a2f4fb4b3a952a3c49..4f7b5ca7ab29274ac96b2606032278fedfd80375 100644 (file)
@@ -21,3 +21,6 @@
 
 SYNC_OP_8(and_8)
 #endif
+
+NO_EXEC_STACK_DIRECTIVE
+
index 01e4f444c2f7e880eb22a59b5241e4dc7362a1de..43da9c7d4067cf4e41e671c71f3b63c725c015f0 100644 (file)
@@ -18,3 +18,5 @@
 
 SYNC_OP_4(max_4)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 1eef2b223668bafff3bd71f033059bd76f2d4035..898fc6202ac81117063a9a1ef532609ee9a190d5 100644 (file)
@@ -19,3 +19,6 @@
 
 SYNC_OP_8(max_8)
 #endif
+
+NO_EXEC_STACK_DIRECTIVE
+
index 015626b63da593635d182d6dec6d48eb4a9b0d2d..bba31a03aace30ee3f080ac9aa728f7ed870f370 100644 (file)
@@ -18,3 +18,5 @@
 
 SYNC_OP_4(min_4)
 
+NO_EXEC_STACK_DIRECTIVE
+
index ad5cce07544c1b36d3169799afd57f42dbf77808..e7ccf9fb60efbb22a14f2db80d818aebbdb06d87 100644 (file)
@@ -19,3 +19,6 @@
 
 SYNC_OP_8(min_8)
 #endif
+
+NO_EXEC_STACK_DIRECTIVE
+
index b32a314b397439957773d03d0e4b1dcd9e1df74c..c13dd394588f0eb6c17ad7fd69fc94bd4c397cfc 100644 (file)
@@ -18,3 +18,5 @@
 
 SYNC_OP_4(nand_4)
 
+NO_EXEC_STACK_DIRECTIVE
+
index a2c17c09c08fe8a21e32e34371c9b10c7ad46cd2..e8107ab3a33cb86f8a584f572c430a4d7ec95a72 100644 (file)
@@ -22,3 +22,5 @@
 SYNC_OP_8(nand_8)
 #endif
 
+NO_EXEC_STACK_DIRECTIVE
+
index f2e08576aaabfb23004a9694ec699de3fe8f9b35..6726571a944f3d392b3538ff2511bfdc279a7e88 100644 (file)
@@ -18,3 +18,5 @@
 
 SYNC_OP_4(or_4)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 87b940bf620d895d00063a61e83f52c8b990327b..f7f162c7c3b3bac75cb3e0587ac61d57340c1c5b 100644 (file)
@@ -22,3 +22,5 @@
 SYNC_OP_8(or_8)
 #endif
 
+NO_EXEC_STACK_DIRECTIVE
+
index 460b2bc1ed62009e56ce5984cca5c545ce14e21e..b9326b14cdd5140998963c4f0823d27b551bfb8b 100644 (file)
@@ -19,3 +19,5 @@
 
 SYNC_OP_4(sub_4)
 
+NO_EXEC_STACK_DIRECTIVE
+
index a8035a2768536d441d91366048310b46039ef6ab..6ce743e5ee9f89820b8b8d6702a2861ea7fcc7a6 100644 (file)
@@ -22,3 +22,5 @@
 SYNC_OP_8(sub_8)
 #endif
 
+NO_EXEC_STACK_DIRECTIVE
+
index c59153031931c6e09908239490a87b9f02ade85a..b8d19ff3505742ada1f16078c14f79792e8e50f3 100644 (file)
@@ -18,3 +18,5 @@
 
 SYNC_OP_4(umax_4)
 
+NO_EXEC_STACK_DIRECTIVE
+
index d9b7965e52ba8ce8bcdcc456cb4637828dc4a77e..34442fd774542bdba3cf1e2977ff9fb184cda326 100644 (file)
@@ -19,3 +19,6 @@
 
 SYNC_OP_8(umax_8)
 #endif
+
+NO_EXEC_STACK_DIRECTIVE
+
index 9f3896fca80e221ba0eb20016feddea1544b6d3f..0998e3e10f58d13fca6ee0cef2784b2023492b6f 100644 (file)
@@ -18,3 +18,5 @@
 
 SYNC_OP_4(umin_4)
 
+NO_EXEC_STACK_DIRECTIVE
+
index 7bf5e235653cabf4481a3c6bb1cd8ddaf051b55f..558f91390512ae4ce000533964908e536cdf70fe 100644 (file)
@@ -19,3 +19,6 @@
 
 SYNC_OP_8(umin_8)
 #endif
+
+NO_EXEC_STACK_DIRECTIVE
+
index 7e7c90c9627748d0d62c306764ff096749372b8e..824f491468801f17b35d3dd3c126f1660d7a903b 100644 (file)
@@ -18,3 +18,5 @@
 
 SYNC_OP_4(xor_4)
 
+NO_EXEC_STACK_DIRECTIVE
+
index ea9aa6d4b0e2fcd1e75a01966bda3e644a29db65..073fb9c20f25e672f1fc6495db457e66ec4cb088 100644 (file)
@@ -22,3 +22,5 @@
 SYNC_OP_8(xor_8)
 #endif
 
+NO_EXEC_STACK_DIRECTIVE
+
index 178f24534c71483cc02ef9b1c0fd7dd837a1f5c3..61d1db910f0deaf586f2c40220b0f404fe21f4c4 100644 (file)
@@ -33,3 +33,6 @@ END_COMPILERRT_FUNCTION(__sync_synchronize)
        .subsections_via_symbols
                
 #endif
+
+NO_EXEC_STACK_DIRECTIVE
+
index fa4362c45e773306487f4883826eeea3cbd41946..04287ad27ce62918a556cc59e078f105919d8084 100644 (file)
@@ -24,3 +24,6 @@ DEFINE_COMPILERRT_FUNCTION(__truncdfsf2vfp)
        vmov    r0, s15      // return result in r0
        bx      lr
 END_COMPILERRT_FUNCTION(__truncdfsf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 85b84936c4b3c670960bca66607ad8c6a1bb1c39..1ad8ee34bdef8ffcd74e168f30f6bd3ff884dde4 100644 (file)
@@ -182,3 +182,6 @@ LOCAL_LABEL(divby0):
 #endif
 
 END_COMPILERRT_FUNCTION(__udivmodsi4)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 165b2b58acb43f532624e7ffe918d8a53f6e6601..085f8fb9e2df66358539d57431e4c45c99d8abdc 100644 (file)
@@ -168,3 +168,6 @@ LOCAL_LABEL(divby0):
 #endif
 
 END_COMPILERRT_FUNCTION(__udivsi3)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 9e7a148ce46f4e2440036eb90247f1694cdf8012..672487e81a637105ed44a43f2988f664e4d00fdc 100644 (file)
@@ -159,3 +159,6 @@ LOCAL_LABEL(divby0):
 #endif
 
 END_COMPILERRT_FUNCTION(__umodsi3)
+
+NO_EXEC_STACK_DIRECTIVE
+
index c4bea2d5eebdf26f378bb27163150784014400bc..022dd7a978af8f73a0d3bf60b7fd8e18038784f2 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__unorddf2vfp)
        movvc   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__unorddf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index 886e96568103bf23663ea88f80c3520b926be8ea..5ebdd3df5505579db3453f66f49bd39eaed9971f 100644 (file)
@@ -27,3 +27,6 @@ DEFINE_COMPILERRT_FUNCTION(__unordsf2vfp)
        movvc   r0, #0
        bx      lr
 END_COMPILERRT_FUNCTION(__unordsf2vfp)
+
+NO_EXEC_STACK_DIRECTIVE
+
index c28970534cc42dbfe955105c502f7c6da53049ef..5fc74f68f60324ba82459dce8da413b71bb0fa32 100644 (file)
@@ -30,6 +30,8 @@
 #define SYMBOL_IS_FUNC(name)
 #define CONST_SECTION .const
 
+#define NO_EXEC_STACK_DIRECTIVE
+
 #elif defined(__ELF__)
 
 #define HIDDEN(name) .hidden name
 #endif
 #define CONST_SECTION .section .rodata
 
+#if defined(__GNU__) || defined(__ANDROID__) || defined(__FreeBSD__)
+#define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack,"",%progbits
+#else
+#define NO_EXEC_STACK_DIRECTIVE
+#endif
+
 #else // !__APPLE__ && !__ELF__
 
 #define HIDDEN(name)
@@ -54,6 +62,8 @@
   .endef
 #define CONST_SECTION .section .rdata,"rd"
 
+#define NO_EXEC_STACK_DIRECTIVE
+
 #endif
 
 #if defined(__arm__)
diff --git a/src/compiler-rt/lib/builtins/divtc3.c b/src/compiler-rt/lib/builtins/divtc3.c
new file mode 100644 (file)
index 0000000..04693df
--- /dev/null
@@ -0,0 +1,60 @@
+/*===-- divtc3.c - Implement __divtc3 -------------------------------------===
+ *
+ *                     The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements __divtc3 for the compiler_rt library.
+ *
+ *===----------------------------------------------------------------------===
+ */
+
+#include "int_lib.h"
+#include "int_math.h"
+
+/* Returns: the quotient of (a + ib) / (c + id) */
+
+COMPILER_RT_ABI long double _Complex
+__divtc3(long double __a, long double __b, long double __c, long double __d)
+{
+    int __ilogbw = 0;
+    long double __logbw = crt_logbl(crt_fmaxl(crt_fabsl(__c), crt_fabsl(__d)));
+    if (crt_isfinite(__logbw))
+    {
+        __ilogbw = (int)__logbw;
+        __c = crt_scalbnl(__c, -__ilogbw);
+        __d = crt_scalbnl(__d, -__ilogbw);
+    }
+    long double __denom = __c * __c + __d * __d;
+    long double _Complex z;
+    __real__ z = crt_scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw);
+    __imag__ z = crt_scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw);
+    if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
+    {
+        if ((__denom == 0.0) && (!crt_isnan(__a) || !crt_isnan(__b)))
+        {
+            __real__ z = crt_copysignl(CRT_INFINITY, __c) * __a;
+            __imag__ z = crt_copysignl(CRT_INFINITY, __c) * __b;
+        }
+        else if ((crt_isinf(__a) || crt_isinf(__b)) &&
+                 crt_isfinite(__c) && crt_isfinite(__d))
+        {
+            __a = crt_copysignl(crt_isinf(__a) ? 1.0 : 0.0, __a);
+            __b = crt_copysignl(crt_isinf(__b) ? 1.0 : 0.0, __b);
+            __real__ z = CRT_INFINITY * (__a * __c + __b * __d);
+            __imag__ z = CRT_INFINITY * (__b * __c - __a * __d);
+        }
+        else if (crt_isinf(__logbw) && __logbw > 0.0 &&
+                 crt_isfinite(__a) && crt_isfinite(__b))
+        {
+            __c = crt_copysignl(crt_isinf(__c) ? 1.0 : 0.0, __c);
+            __d = crt_copysignl(crt_isinf(__d) ? 1.0 : 0.0, __d);
+            __real__ z = 0.0 * (__a * __c + __b * __d);
+            __imag__ z = 0.0 * (__b * __c - __a * __d);
+        }
+    }
+    return z;
+}
index afe643c4bd5799d8e3ecf6fa1b0d56e082747615..aba3865f7eab8f1013326021d1b8c25ab96de0fb 100644 (file)
@@ -131,6 +131,26 @@ static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding)
     return result;
 }
 
+#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) &&                 \
+    !defined(__ARM_DWARF_EH__)
+#define USING_ARM_EHABI 1
+_Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,
+                                       struct _Unwind_Context *);
+#endif
+
+static inline _Unwind_Reason_Code
+continueUnwind(struct _Unwind_Exception *exceptionObject,
+               struct _Unwind_Context *context) {
+#if USING_ARM_EHABI
+    /*
+     * On ARM EHABI the personality routine is responsible for actually
+     * unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
+     */
+    if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK)
+        return _URC_FAILURE;
+#endif
+    return _URC_CONTINUE_UNWIND;
+}
 
 /*
  * The C compiler makes references to __gcc_personality_v0 in
@@ -141,11 +161,17 @@ static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding)
  * throw through a C function compiled with -fexceptions.
  */
 #if __USING_SJLJ_EXCEPTIONS__
-// the setjump-longjump based exceptions personality routine has a different name
+/* the setjump-longjump based exceptions personality routine has a
+ * different name */
 COMPILER_RT_ABI _Unwind_Reason_Code
 __gcc_personality_sj0(int version, _Unwind_Action actions,
          uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject,
          struct _Unwind_Context *context)
+#elif USING_ARM_EHABI
+/* The ARM EHABI personality routine has a different signature. */
+COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
+         _Unwind_State state, struct _Unwind_Exception *exceptionObject,
+         struct _Unwind_Context *context)
 #else
 COMPILER_RT_ABI _Unwind_Reason_Code
 __gcc_personality_v0(int version, _Unwind_Action actions,
@@ -155,13 +181,19 @@ __gcc_personality_v0(int version, _Unwind_Action actions,
 {
     /* Since C does not have catch clauses, there is nothing to do during */
     /* phase 1 (the search phase). */
-    if ( actions & _UA_SEARCH_PHASE ) 
-        return _URC_CONTINUE_UNWIND;
-        
+#if USING_ARM_EHABI
+    /* After resuming from a cleanup we should also continue on to the next
+     * frame straight away. */
+    if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING)
+#else
+    if ( actions & _UA_SEARCH_PHASE )
+#endif
+        return continueUnwind(exceptionObject, context);
+
     /* There is nothing to do if there is no LSDA for this frame. */
     const uint8_t* lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context);
     if ( lsda == (uint8_t*) 0 )
-        return _URC_CONTINUE_UNWIND;
+        return continueUnwind(exceptionObject, context);
 
     uintptr_t pc = _Unwind_GetIP(context)-1;
 
@@ -226,7 +258,8 @@ __gcc_personality_v0(int version, _Unwind_Action actions,
 #endif // SJ/LJ
 
     if (resumeIp == 0) {
-        return _URC_CONTINUE_UNWIND;
+        /* No landing pad found, continue unwinding. */
+        return continueUnwind(exceptionObject, context);
     } else {
         _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
                       (uintptr_t)exceptionObject);
index 3fbd739038428d30be68d01540915b6baa825ee1..6f05dcf74443abe41ea05d5f52b20dd0cd7149c9 100644 (file)
@@ -56,3 +56,6 @@ END_COMPILERRT_FUNCTION(__ashldi3)
 
 #endif // __SSE2__
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 8f4742481b42251af3fd289a059f382ef337e907..206369f360aa36c6ccb3ca1c32d31bfbdb35ae5b 100644 (file)
@@ -67,3 +67,6 @@ END_COMPILERRT_FUNCTION(__ashrdi3)
 
 #endif // __SSE2__
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 2cb0ddd4c29f1099b083610badecdb7f8b7476f7..2fb4bdcad90d61a2193051a114e1e4e51847d892 100644 (file)
@@ -160,3 +160,6 @@ DEFINE_COMPILERRT_FUNCTION(__divdi3)
 END_COMPILERRT_FUNCTION(__divdi3)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index dcc32f8ed85dcd3d1a606d1f1a14de465b87f08d..d75dfe62d6a7377397942c983d195dc1e2a708c7 100644 (file)
@@ -37,3 +37,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatdidf)
 END_COMPILERRT_FUNCTION(__floatdidf)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index f6427670360772b792cc29f5cb33c3563805091d..0874eaaa9a98de7d2296c4c779efbe51b672658a 100644 (file)
@@ -30,3 +30,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatdisf)
 END_COMPILERRT_FUNCTION(__floatdisf)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 839b0434c0c64e4eaafe70c615e9124110c72f27..1044ef55a1a83eaa95b919da96d64a14f2790510 100644 (file)
@@ -28,3 +28,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatdixf)
 END_COMPILERRT_FUNCTION(__floatdixf)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 8058c2ac0aed90f0978fd5a79a9f181765a24694..fe032348e829f821a60c128e6579253387494bb5 100644 (file)
@@ -50,3 +50,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatundidf)
 END_COMPILERRT_FUNCTION(__floatundidf)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 94c97e25aa8c390e41c0860741b454986b8457a9..16000b576026b1c926bd27d2f7e12eb41b40b4db 100644 (file)
@@ -103,3 +103,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatundisf)
 END_COMPILERRT_FUNCTION(__floatundisf)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 814b52f941d80e0f554910b880f1c8889adcb7c1..c935670cb52f02c6b8b9664e312929f681333090 100644 (file)
@@ -41,3 +41,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatundixf)
 END_COMPILERRT_FUNCTION(__floatundixf)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index b80f11a3806b59766a55f90aff3f42dc3ccfbaf4..53e95cf76527c5f3a4efc81819703f7daba19554 100644 (file)
@@ -57,3 +57,6 @@ END_COMPILERRT_FUNCTION(__lshrdi3)
 
 #endif // __SSE2__
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index b9cee9d7aa700c373f1b6860213026e464ed05d3..a5bf9ce8ea0facb5c43b8a130fa40905824322e1 100644 (file)
@@ -164,3 +164,6 @@ DEFINE_COMPILERRT_FUNCTION(__moddi3)
 END_COMPILERRT_FUNCTION(__moddi3)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 15b6b4998456a911e53fd5ba0318a68b83a2d617..12394606421cc184c5ca4c16f0c536c20d05e18f 100644 (file)
@@ -28,3 +28,6 @@ DEFINE_COMPILERRT_FUNCTION(__muldi3)
 END_COMPILERRT_FUNCTION(__muldi3)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 41b2edf03e348d68c77a3a3ba3504f94d21e7ff8..727613639b12530aef88d1ae6093378c44646626 100644 (file)
@@ -113,3 +113,6 @@ DEFINE_COMPILERRT_FUNCTION(__udivdi3)
 END_COMPILERRT_FUNCTION(__udivdi3)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index a190a7d397d283be2a42e019665dbbf035ea3178..763e821946c088bce9f6c2c7b84b786bd349eb0a 100644 (file)
@@ -124,3 +124,6 @@ DEFINE_COMPILERRT_FUNCTION(__umoddi3)
 END_COMPILERRT_FUNCTION(__umoddi3)
 
 #endif // __i386__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 92511e6a15e3166b9df8cee74954f5cfb80be871..7995ddbb953cde765660d6c77590363dc7d17a33 100644 (file)
 
 #endif /* Windows */
 
-#if defined(__linux__)
-#include <endian.h>
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-#define _YUGA_LITTLE_ENDIAN 0
-#define _YUGA_BIG_ENDIAN    1
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
-#define _YUGA_LITTLE_ENDIAN 1
-#define _YUGA_BIG_ENDIAN    0
-#endif /* __BYTE_ORDER */
-
-#endif /* GNU/Linux */
-
 #endif /* Clang or GCC. */
 
 /* . */
index 272f9d9dad7b6c0f3a1aaa500c5777b228146f09..e66cda3fffb4b3921216a10d138636a96e9e417f 100644 (file)
@@ -128,6 +128,6 @@ uint32_t __inline __builtin_clzll(uint64_t value) {
 #endif
 
 #define __builtin_clzl __builtin_clzll
-#endif // defined(_MSC_VER) && !defined(__clang__)
+#endif /* defined(_MSC_VER) && !defined(__clang__) */
 
 #endif /* INT_LIB_H */
index ba1f7522a8351a103966e96d26326fbaf6e79d22..660385ecd6aedd145ea30f2d7705004b33f65648 100644 (file)
 
 #include "int_endianness.h"
 
+/* si_int is defined in Linux sysroot's asm-generic/siginfo.h */
+#ifdef si_int
+#undef si_int
+#endif
 typedef      int si_int;
 typedef unsigned su_int;
 
@@ -57,7 +61,8 @@ typedef union
 } udwords;
 
 /* MIPS64 issue: PR 20098 */
-#if defined(__LP64__) && !(defined(__mips__) && defined(__clang__))
+#if (defined(__LP64__) || defined(__wasm__)) && \
+    !(defined(__mips__) && defined(__clang__))
 #define CRT_HAS_128BIT
 #endif
 
index 679c7f186b2e3ffedb9cfb959a2ca2c6f1c4f861..3e5f9e58c1380fd8b0e47644a205714e4adce044 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __DD_HEADER
-#define __DD_HEADER
+#ifndef COMPILERRT_DD_HEADER
+#define COMPILERRT_DD_HEADER
 
 #include "../int_lib.h"
 
@@ -9,7 +9,7 @@ typedef union {
                double hi;
                double lo;
        }s;
-}DD;
+} DD;
 
 typedef union { 
        double d;
@@ -37,4 +37,9 @@ static __inline ALWAYS_INLINE int different_sign(double x, double y) {
   return result;
 }
 
-#endif /* __DD_HEADER */
+long double __gcc_qadd(long double, long double);
+long double __gcc_qsub(long double, long double);
+long double __gcc_qmul(long double, long double);
+long double __gcc_qdiv(long double, long double);
+
+#endif /* COMPILERRT_DD_HEADER */
index 299128186312e9430153434e9f24ee43170b8ab2..8ec41c528ab902b51d320b0dec7136ce962f7909 100644 (file)
     (x).s.lo = 0.0;                                                     \
   }
 
-long double __gcc_qadd(long double, long double);
-long double __gcc_qsub(long double, long double);
-long double __gcc_qmul(long double, long double);
-long double __gcc_qdiv(long double, long double);
-
 long double _Complex
 __divtc3(long double a, long double b, long double c, long double d)
 {
index 738b65a83b03a28c21c8df0c3f51960388e1ad0c..9dd79c975dde38a8604a95efb92fad6cf92fd08a 100644 (file)
     }                                                                   \
   }
 
-long double __gcc_qadd(long double, long double);
-long double __gcc_qsub(long double, long double);
-long double __gcc_qmul(long double, long double);
-
 long double _Complex
 __multc3(long double a, long double b, long double c, long double d)
 {
index 95032897c0daf0b2109e50d3f5950eb97614315b..507e756e18b15d9d9080d828f2dd5cbfa2f91577 100644 (file)
@@ -41,3 +41,6 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION_UNMANGLED(restFP)
         lwz     r0,8(r1)
         mtlr   r0
         blr
+
+NO_EXEC_STACK_DIRECTIVE
+
index 72bd459f4cc01150648c7931c281a53945d6d46c..20b06fff53d9ae0dd85a3ffe69262c83c3aa88c2 100644 (file)
@@ -38,3 +38,6 @@ DEFINE_COMPILERRT_PRIVATE_FUNCTION_UNMANGLED(saveFP)
         stfd    f31,-8(r1)
         stw      r0,8(r1)
         blr
+
+NO_EXEC_STACK_DIRECTIVE
+
index 3cd5d02a743a88375452c690645c1556cc4111b4..094a68dc3cd4e6fb58047f2fb10438e2cc34bf7f 100644 (file)
@@ -47,3 +47,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatundidf)
 END_COMPILERRT_FUNCTION(__floatundidf)
 
 #endif // __x86_64__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 61952f40470c012ded85da2d38aa582d5fb8655e..7c9f75e188ebf9c340fc98811f3f0e8afd371ab0 100644 (file)
@@ -33,3 +33,6 @@ DEFINE_COMPILERRT_FUNCTION(__floatundisf)
 END_COMPILERRT_FUNCTION(__floatundisf)
 
 #endif // __x86_64__
+
+NO_EXEC_STACK_DIRECTIVE
+
index 92961c89115d9b10ba609616c76c45dec5810203..28a096b71373dfe42516d1c00c38be038ae61c73 100644 (file)
@@ -66,3 +66,6 @@ END_COMPILERRT_FUNCTION(__floatundixf)
 #endif // __x86_64__
 
 */
+
+NO_EXEC_STACK_DIRECTIVE
+
index 90b66a84ccdd53f8a4806e8d95eaf49fb9d716e4..24e51814cdab5ad308b363f6f3dfb9287dc35ac3 100644 (file)
@@ -1,4 +1,40 @@
 add_custom_target(cfi)
+
+set(CFI_SOURCES cfi.cc)
+
+include_directories(..)
+
+set(CFI_CFLAGS
+  ${SANITIZER_COMMON_CFLAGS}
+)
+
+set(CFI_DIAG_CFLAGS
+  -DCFI_ENABLE_DIAG=1
+)
+
+foreach(arch ${CFI_SUPPORTED_ARCH})
+  add_compiler_rt_runtime(clang_rt.cfi
+    STATIC
+    ARCHS ${arch}
+    SOURCES ${CFI_SOURCES}
+    OBJECT_LIBS RTInterception
+                RTSanitizerCommon
+                RTSanitizerCommonLibc
+    CFLAGS ${CFI_CFLAGS}
+    PARENT_TARGET cfi)
+  add_compiler_rt_runtime(clang_rt.cfi_diag
+    STATIC
+    ARCHS ${arch}
+    SOURCES ${CFI_SOURCES}
+    OBJECT_LIBS RTInterception
+                RTSanitizerCommon
+                RTSanitizerCommonLibc
+               RTUbsan
+               RTUbsan_cxx
+    CFLAGS ${CFI_CFLAGS} ${CFI_DIAG_CFLAGS}
+    PARENT_TARGET cfi)
+endforeach()
+
 add_compiler_rt_resource_file(cfi_blacklist cfi_blacklist.txt)
 add_dependencies(cfi cfi_blacklist)
 add_dependencies(compiler-rt cfi)
diff --git a/src/compiler-rt/lib/cfi/cfi.cc b/src/compiler-rt/lib/cfi/cfi.cc
new file mode 100644 (file)
index 0000000..711866f
--- /dev/null
@@ -0,0 +1,271 @@
+//===-------- cfi.cc ------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the runtime support for the cross-DSO CFI.
+//
+//===----------------------------------------------------------------------===//
+
+// FIXME: Intercept dlopen/dlclose.
+// FIXME: Support diagnostic mode.
+// FIXME: Harden:
+//  * mprotect shadow, use mremap for updates
+//  * something else equally important
+
+#include <assert.h>
+#include <elf.h>
+#include <link.h>
+#include <string.h>
+
+typedef ElfW(Phdr) Elf_Phdr;
+typedef ElfW(Ehdr) Elf_Ehdr;
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "ubsan/ubsan_init.h"
+#include "ubsan/ubsan_flags.h"
+
+static uptr __cfi_shadow;
+static constexpr uptr kShadowGranularity = 12;
+static constexpr uptr kShadowAlign = 1UL << kShadowGranularity; // 4096
+
+static constexpr uint16_t kInvalidShadow = 0;
+static constexpr uint16_t kUncheckedShadow = 0xFFFFU;
+
+static uint16_t *mem_to_shadow(uptr x) {
+  return (uint16_t *)(__cfi_shadow + ((x >> kShadowGranularity) << 1));
+}
+
+typedef int (*CFICheckFn)(u64, void *);
+
+class ShadowValue {
+  uptr addr;
+  uint16_t v;
+  explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {}
+
+public:
+  bool is_invalid() const { return v == kInvalidShadow; }
+
+  bool is_unchecked() const { return v == kUncheckedShadow; }
+
+  CFICheckFn get_cfi_check() const {
+    assert(!is_invalid() && !is_unchecked());
+    uptr aligned_addr = addr & ~(kShadowAlign - 1);
+    uptr p = aligned_addr - (((uptr)v - 1) << kShadowGranularity);
+    return reinterpret_cast<CFICheckFn>(p);
+  }
+
+  // Load a shadow valud for the given application memory address.
+  static const ShadowValue load(uptr addr) {
+    return ShadowValue(addr, *mem_to_shadow(addr));
+  }
+};
+
+static void fill_shadow_constant(uptr begin, uptr end, uint16_t v) {
+  assert(v == kInvalidShadow || v == kUncheckedShadow);
+  uint16_t *shadow_begin = mem_to_shadow(begin);
+  uint16_t *shadow_end = mem_to_shadow(end - 1) + 1;
+  memset(shadow_begin, v, (shadow_end - shadow_begin) * sizeof(*shadow_begin));
+}
+
+static void fill_shadow(uptr begin, uptr end, uptr cfi_check) {
+  assert((cfi_check & (kShadowAlign - 1)) == 0);
+
+  // Don't fill anything below cfi_check. We can not represent those addresses
+  // in the shadow, and must make sure at codegen to place all valid call
+  // targets above cfi_check.
+  uptr p = Max(begin, cfi_check);
+  uint16_t *s = mem_to_shadow(p);
+  uint16_t *s_end = mem_to_shadow(end - 1) + 1;
+  uint16_t sv = ((p - cfi_check) >> kShadowGranularity) + 1;
+  for (; s < s_end; s++, sv++)
+    *s = sv;
+
+  // Sanity checks.
+  uptr q = p & ~(kShadowAlign - 1);
+  for (; q < end; q += kShadowAlign) {
+    assert((uptr)ShadowValue::load(q).get_cfi_check() == cfi_check);
+    assert((uptr)ShadowValue::load(q + kShadowAlign / 2).get_cfi_check() ==
+           cfi_check);
+    assert((uptr)ShadowValue::load(q + kShadowAlign - 1).get_cfi_check() ==
+           cfi_check);
+  }
+}
+
+// This is a workaround for a glibc bug:
+// https://sourceware.org/bugzilla/show_bug.cgi?id=15199
+// Other platforms can, hopefully, just do
+//    dlopen(RTLD_NOLOAD | RTLD_LAZY)
+//    dlsym("__cfi_check").
+static uptr find_cfi_check_in_dso(dl_phdr_info *info) {
+  const ElfW(Dyn) *dynamic = nullptr;
+  for (int i = 0; i < info->dlpi_phnum; ++i) {
+    if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
+      dynamic =
+          (const ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
+      break;
+    }
+  }
+  if (!dynamic) return 0;
+  uptr strtab = 0, symtab = 0;
+  for (const ElfW(Dyn) *p = dynamic; p->d_tag != PT_NULL; ++p) {
+    if (p->d_tag == DT_SYMTAB)
+      symtab = p->d_un.d_ptr;
+    else if (p->d_tag == DT_STRTAB)
+      strtab = p->d_un.d_ptr;
+  }
+
+  if (symtab > strtab) {
+    VReport(1, "Can not handle: symtab > strtab (%p > %zx)\n", symtab, strtab);
+    return 0;
+  }
+
+  // Verify that strtab and symtab are inside of the same LOAD segment.
+  // This excludes VDSO, which has (very high) bogus strtab and symtab pointers.
+  int phdr_idx;
+  for (phdr_idx = 0; phdr_idx < info->dlpi_phnum; phdr_idx++) {
+    const Elf_Phdr *phdr = &info->dlpi_phdr[phdr_idx];
+    if (phdr->p_type == PT_LOAD) {
+      uptr beg = info->dlpi_addr + phdr->p_vaddr;
+      uptr end = beg + phdr->p_memsz;
+      if (strtab >= beg && strtab < end && symtab >= beg && symtab < end)
+        break;
+    }
+  }
+  if (phdr_idx == info->dlpi_phnum) {
+    // Nope, either different segments or just bogus pointers.
+    // Can not handle this.
+    VReport(1, "Can not handle: symtab %p, strtab %zx\n", symtab, strtab);
+    return 0;
+  }
+
+  for (const ElfW(Sym) *p = (const ElfW(Sym) *)symtab; (ElfW(Addr))p < strtab;
+       ++p) {
+    char *name = (char*)(strtab + p->st_name);
+    if (strcmp(name, "__cfi_check") == 0) {
+      assert(p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC));
+      uptr addr = info->dlpi_addr + p->st_value;
+      return addr;
+    }
+  }
+  return 0;
+}
+
+static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *data) {
+  uptr cfi_check = find_cfi_check_in_dso(info);
+  if (cfi_check)
+    VReport(1, "Module '%s' __cfi_check %zx\n", info->dlpi_name, cfi_check);
+
+  for (int i = 0; i < info->dlpi_phnum; i++) {
+    const Elf_Phdr *phdr = &info->dlpi_phdr[i];
+    if (phdr->p_type == PT_LOAD) {
+      // Jump tables are in the executable segment.
+      // VTables are in the non-executable one.
+      // Need to fill shadow for both.
+      // FIXME: reject writable if vtables are in the r/o segment. Depend on
+      // PT_RELRO?
+      uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
+      uptr cur_end = cur_beg + phdr->p_memsz;
+      if (cfi_check) {
+        VReport(1, "   %zx .. %zx\n", cur_beg, cur_end);
+        fill_shadow(cur_beg, cur_end, cfi_check ? cfi_check : (uptr)(-1));
+      } else {
+        fill_shadow_constant(cur_beg, cur_end, kUncheckedShadow);
+      }
+    }
+  }
+  return 0;
+}
+
+// Fill shadow for the initial libraries.
+static void init_shadow() {
+  dl_iterate_phdr(dl_iterate_phdr_cb, nullptr);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __cfi_slowpath(u64 CallSiteTypeId, void *Ptr) {
+  uptr Addr = (uptr)Ptr;
+  VReport(3, "__cfi_slowpath: %llx, %p\n", CallSiteTypeId, Ptr);
+  ShadowValue sv = ShadowValue::load(Addr);
+  if (sv.is_invalid()) {
+    VReport(2, "CFI: invalid memory region for a function pointer (shadow==0): %p\n", Ptr);
+    Die();
+  }
+  if (sv.is_unchecked()) {
+    VReport(2, "CFI: unchecked call (shadow=FFFF): %p\n", Ptr);
+    return;
+  }
+  CFICheckFn cfi_check = sv.get_cfi_check();
+  VReport(2, "__cfi_check at %p\n", cfi_check);
+  cfi_check(CallSiteTypeId, Ptr);
+}
+
+static void InitializeFlags() {
+  SetCommonFlagsDefaults();
+#ifdef CFI_ENABLE_DIAG
+  __ubsan::Flags *uf = __ubsan::flags();
+  uf->SetDefaults();
+#endif
+
+  FlagParser cfi_parser;
+  RegisterCommonFlags(&cfi_parser);
+  cfi_parser.ParseString(GetEnv("CFI_OPTIONS"));
+
+#ifdef CFI_ENABLE_DIAG
+  FlagParser ubsan_parser;
+  __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+  RegisterCommonFlags(&ubsan_parser);
+
+  const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
+  ubsan_parser.ParseString(ubsan_default_options);
+  ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
+#endif
+
+  SetVerbosity(common_flags()->verbosity);
+
+  if (Verbosity()) ReportUnrecognizedFlags();
+
+  if (common_flags()->help) {
+    cfi_parser.PrintFlagDescriptions();
+  }
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+#if !SANITIZER_CAN_USE_PREINIT_ARRAY
+// On ELF platforms, the constructor is invoked using .preinit_array (see below)
+__attribute__((constructor(0)))
+#endif
+void __cfi_init() {
+  SanitizerToolName = "CFI";
+  InitializeFlags();
+
+  uptr vma = GetMaxVirtualAddress();
+  // Shadow is 2 -> 2**kShadowGranularity.
+  uptr shadow_size = (vma >> (kShadowGranularity - 1)) + 1;
+  VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma, shadow_size);
+  void *shadow = MmapNoReserveOrDie(shadow_size, "CFI shadow");
+  VReport(1, "CFI: shadow at %zx .. %zx\n", shadow,
+          reinterpret_cast<uptr>(shadow) + shadow_size);
+  __cfi_shadow = (uptr)shadow;
+  init_shadow();
+
+#ifdef CFI_ENABLE_DIAG
+  __ubsan::InitAsPlugin();
+#endif
+}
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+// On ELF platforms, run cfi initialization before any other constructors.
+// On other platforms we use the constructor attribute to arrange to run our
+// initialization early.
+extern "C" {
+__attribute__((section(".preinit_array"),
+               used)) void (*__cfi_preinit)(void) = __cfi_init;
+}
+#endif
diff --git a/src/compiler-rt/lib/dfsan/.clang-format b/src/compiler-rt/lib/dfsan/.clang-format
new file mode 100644 (file)
index 0000000..f6cb8ad
--- /dev/null
@@ -0,0 +1 @@
+BasedOnStyle: Google
index f4bef921a30dfb13e887f8931299573b3e12ee29..7285f202d060452b4739d49b0a0f9183834d9781 100644 (file)
@@ -42,6 +42,8 @@ Flags __dfsan::flags_data;
 SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_retval_tls;
 SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_arg_tls[64];
 
+SANITIZER_INTERFACE_ATTRIBUTE uptr __dfsan_shadow_ptr_mask;
+
 // On Linux/x86_64, memory is laid out as follows:
 //
 // +--------------------+ 0x800000000000 (top of memory)
@@ -114,35 +116,18 @@ SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_arg_tls[64];
 
 typedef atomic_dfsan_label dfsan_union_table_t[kNumLabels][kNumLabels];
 
-#if defined(__x86_64__)
-static const uptr kShadowAddr = 0x10000;
-static const uptr kUnionTableAddr = 0x200000000000;
-static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t);
-static const uptr kAppAddr = 0x700000008000;
-#elif defined(__mips64)
-static const uptr kShadowAddr = 0x10000;
-static const uptr kUnionTableAddr = 0x2000000000;
-static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t);
-static const uptr kAppAddr = 0xF000008000;
-#elif defined(__aarch64__)
-static const uptr kShadowAddr = 0x10000;
-# if SANITIZER_AARCH64_VMA == 39
-static const uptr kUnionTableAddr = 0x1000000000;
-# elif SANITIZER_AARCH64_VMA == 42
-static const uptr kUnionTableAddr = 0x8000000000;
-# endif
-static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t);
-# if SANITIZER_AARCH64_VMA == 39
-static const uptr kAppAddr = 0x7000008000;
-# elif SANITIZER_AARCH64_VMA == 42
-static const uptr kAppAddr = 0x3ff00008000;
-# endif
-#else
-# error "DFSan not supported for this platform!"
+#ifdef DFSAN_RUNTIME_VMA
+// Runtime detected VMA size.
+int __dfsan::vmaSize;
 #endif
 
+static uptr UnusedAddr() {
+  return MappingArchImpl<MAPPING_UNION_TABLE_ADDR>()
+         + sizeof(dfsan_union_table_t);
+}
+
 static atomic_dfsan_label *union_table(dfsan_label l1, dfsan_label l2) {
-  return &(*(dfsan_union_table_t *) kUnionTableAddr)[l1][l2];
+  return &(*(dfsan_union_table_t *) UnionTableAddr())[l1][l2];
 }
 
 // Checks we do not run out of labels.
@@ -382,6 +367,20 @@ static void InitializeFlags() {
   if (common_flags()->help) parser.PrintFlagDescriptions();
 }
 
+static void InitializePlatformEarly() {
+#ifdef DFSAN_RUNTIME_VMA
+  __dfsan::vmaSize =
+    (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+  if (__dfsan::vmaSize == 39 || __dfsan::vmaSize == 42) {
+    __dfsan_shadow_ptr_mask = ShadowMask();
+  } else {
+    Printf("FATAL: DataFlowSanitizer: unsupported VMA range\n");
+    Printf("FATAL: Found %d - Supported 39 and 42\n", __dfsan::vmaSize);
+    Die();
+  }
+#endif
+}
+
 static void dfsan_fini() {
   if (internal_strcmp(flags().dump_labels_at_exit, "") != 0) {
     fd_t fd = OpenFile(flags().dump_labels_at_exit, WrOnly);
@@ -401,9 +400,9 @@ static void dfsan_fini() {
 static void dfsan_init(int argc, char **argv, char **envp) {
   InitializeFlags();
 
-  CheckVMASize();
+  InitializePlatformEarly();
 
-  MmapFixedNoReserve(kShadowAddr, kUnusedAddr - kShadowAddr);
+  MmapFixedNoReserve(ShadowAddr(), UnusedAddr() - ShadowAddr());
 
   // Protect the region of memory we don't use, to preserve the one-to-one
   // mapping from application to shadow memory. But if ASLR is disabled, Linux
@@ -411,8 +410,8 @@ static void dfsan_init(int argc, char **argv, char **envp) {
   // works so long as the program doesn't use too much memory. We support this
   // case by disabling memory protection when ASLR is disabled.
   uptr init_addr = (uptr)&dfsan_init;
-  if (!(init_addr >= kUnusedAddr && init_addr < kAppAddr))
-    MmapNoAccess(kUnusedAddr, kAppAddr - kUnusedAddr);
+  if (!(init_addr >= UnusedAddr() && init_addr < AppAddr()))
+    MmapNoAccess(UnusedAddr(), AppAddr() - UnusedAddr());
 
   InitializeInterceptors();
 
index df222e49bb1a113f2f757f93c392e9908a8b514f..81f949e3019ec1ca12ba104cac138a5decfe4e44 100644 (file)
@@ -16,6 +16,7 @@
 #define DFSAN_H
 
 #include "sanitizer_common/sanitizer_internal_defs.h"
+#include "dfsan_platform.h"
 
 // Copy declarations from public sanitizer/dfsan_interface.h header here.
 typedef u16 dfsan_label;
@@ -44,17 +45,7 @@ namespace __dfsan {
 void InitializeInterceptors();
 
 inline dfsan_label *shadow_for(void *ptr) {
-#if defined(__x86_64__)
-  return (dfsan_label *) ((((uptr) ptr) & ~0x700000000000) << 1);
-#elif defined(__mips64)
-  return (dfsan_label *) ((((uptr) ptr) & ~0xF000000000) << 1);
-#elif defined(__aarch64__)
-# if SANITIZER_AARCH64_VMA == 39
-  return (dfsan_label *) ((((uptr) ptr) & ~0x7800000000) << 1);
-# elif SANITIZER_AARCH64_VMA == 42
-  return (dfsan_label *) ((((uptr) ptr) & ~0x3c000000000) << 1);
-# endif
-#endif
+  return (dfsan_label *) ((((uptr) ptr) & ShadowMask()) << 1);
 }
 
 inline const dfsan_label *shadow_for(const void *ptr) {
diff --git a/src/compiler-rt/lib/dfsan/dfsan_platform.h b/src/compiler-rt/lib/dfsan/dfsan_platform.h
new file mode 100644 (file)
index 0000000..f1d9f10
--- /dev/null
@@ -0,0 +1,107 @@
+//===-- dfsan_platform.h ----------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of DataFlowSanitizer.
+//
+// Platform specific information for DFSan.
+//===----------------------------------------------------------------------===//
+
+#ifndef DFSAN_PLATFORM_H
+#define DFSAN_PLATFORM_H
+
+namespace __dfsan {
+
+#if defined(__x86_64__)
+struct Mapping {
+  static const uptr kShadowAddr = 0x10000;
+  static const uptr kUnionTableAddr = 0x200000000000;
+  static const uptr kAppAddr = 0x700000008000;
+  static const uptr kShadowMask = ~0x700000000000;
+};
+#elif defined(__mips64)
+struct Mapping {
+  static const uptr kShadowAddr = 0x10000;
+  static const uptr kUnionTableAddr = 0x2000000000;
+  static const uptr kAppAddr = 0xF000008000;
+  static const uptr kShadowMask = ~0xF000000000;
+};
+#elif defined(__aarch64__)
+struct Mapping39 {
+  static const uptr kShadowAddr = 0x10000;
+  static const uptr kUnionTableAddr = 0x1000000000;
+  static const uptr kAppAddr = 0x7000008000;
+  static const uptr kShadowMask = ~0x7800000000;
+};
+
+struct Mapping42 {
+  static const uptr kShadowAddr = 0x10000;
+  static const uptr kUnionTableAddr = 0x8000000000;
+  static const uptr kAppAddr = 0x3ff00008000;
+  static const uptr kShadowMask = ~0x3c000000000;
+};
+
+extern int vmaSize;
+# define DFSAN_RUNTIME_VMA 1
+#else
+# error "DFSan not supported for this platform!"
+#endif
+
+enum MappingType {
+  MAPPING_SHADOW_ADDR,
+  MAPPING_UNION_TABLE_ADDR,
+  MAPPING_APP_ADDR,
+  MAPPING_SHADOW_MASK
+};
+
+template<typename Mapping, int Type>
+uptr MappingImpl(void) {
+  switch (Type) {
+    case MAPPING_SHADOW_ADDR: return Mapping::kShadowAddr;
+    case MAPPING_UNION_TABLE_ADDR: return Mapping::kUnionTableAddr;
+    case MAPPING_APP_ADDR: return Mapping::kAppAddr;
+    case MAPPING_SHADOW_MASK: return Mapping::kShadowMask;
+  }
+}
+
+template<int Type>
+uptr MappingArchImpl(void) {
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return MappingImpl<Mapping39, Type>();
+  else
+    return MappingImpl<Mapping42, Type>();
+  DCHECK(0);
+#else
+  return MappingImpl<Mapping, Type>();
+#endif
+}
+
+ALWAYS_INLINE
+uptr ShadowAddr() {
+  return MappingArchImpl<MAPPING_SHADOW_ADDR>();
+}
+
+ALWAYS_INLINE
+uptr UnionTableAddr() {
+  return MappingArchImpl<MAPPING_UNION_TABLE_ADDR>();
+}
+
+ALWAYS_INLINE
+uptr AppAddr() {
+  return MappingArchImpl<MAPPING_APP_ADDR>();
+}
+
+ALWAYS_INLINE
+uptr ShadowMask() {
+  return MappingArchImpl<MAPPING_SHADOW_MASK>();
+}
+
+}  // namespace __dfsan
+
+#endif
diff --git a/src/compiler-rt/lib/interception/.clang-format b/src/compiler-rt/lib/interception/.clang-format
new file mode 100644 (file)
index 0000000..f6cb8ad
--- /dev/null
@@ -0,0 +1 @@
+BasedOnStyle: Google
diff --git a/src/compiler-rt/lib/lsan/.clang-format b/src/compiler-rt/lib/lsan/.clang-format
new file mode 100644 (file)
index 0000000..f6cb8ad
--- /dev/null
@@ -0,0 +1 @@
+BasedOnStyle: Google
index 6600abb9a190bd14ed89b81548c6d15bfe447532..1cffac44395c5d4bc7565afed846324762bcc43b 100644 (file)
@@ -120,7 +120,9 @@ static inline bool CanBeAHeapPointer(uptr p) {
 #elif defined(__mips64)
   return ((p >> 40) == 0);
 #elif defined(__aarch64__)
-  return ((p >> SANITIZER_AARCH64_VMA) == 0);
+  unsigned runtimeVMA =
+    (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+  return ((p >> runtimeVMA) == 0);
 #else
   return true;
 #endif
diff --git a/src/compiler-rt/lib/msan/.clang-format b/src/compiler-rt/lib/msan/.clang-format
new file mode 100644 (file)
index 0000000..f6cb8ad
--- /dev/null
@@ -0,0 +1 @@
+BasedOnStyle: Google
index b64dcb6ffeb3cfa5284206b0b22b0c55f4dc6d5c..9949db4c13a03aa92210e8db660ce4886c8279e0 100644 (file)
@@ -55,7 +55,7 @@ SANITIZER_INTERFACE_ATTRIBUTE
 THREADLOCAL u32 __msan_retval_origin_tls;
 
 SANITIZER_INTERFACE_ATTRIBUTE
-THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSize / sizeof(u64)];
+ALIGNED(16) THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSize / sizeof(u64)];
 
 SANITIZER_INTERFACE_ATTRIBUTE
 THREADLOCAL u64 __msan_va_arg_overflow_size_tls;
index 1bf196ecdbeb5dfc93be5d2d45c7ee89b4cd7454..0db2ac5b226cda0b8b803727ce3b3a20eb301941 100644 (file)
@@ -1408,12 +1408,12 @@ int OnExit() {
   __msan_unpoison(ptr, size)
 #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...)                  \
   if (msan_init_is_running) return REAL(func)(__VA_ARGS__);       \
+  ENSURE_MSAN_INITED();                                           \
   MSanInterceptorContext msan_ctx = {IsInInterceptorScope()};     \
   ctx = (void *)&msan_ctx;                                        \
   (void)ctx;                                                      \
   InterceptorScope interceptor_scope;                             \
-  __msan_unpoison(__errno_location(), sizeof(int)); /* NOLINT */  \
-  ENSURE_MSAN_INITED();
+  __msan_unpoison(__errno_location(), sizeof(int)); /* NOLINT */
 #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
   do {                                            \
   } while (false)
@@ -1434,10 +1434,11 @@ int OnExit() {
   } while (false)  // FIXME
 #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
 #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
-#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle)  \
-  do {                                                       \
-    link_map *map = GET_LINK_MAP_BY_DLOPEN_HANDLE((handle)); \
-    if (map) ForEachMappedRegion(map, __msan_unpoison);      \
+#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle)                    \
+  do {                                                                         \
+    link_map *map = GET_LINK_MAP_BY_DLOPEN_HANDLE((handle));                   \
+    if (filename && map)                                                       \
+      ForEachMappedRegion(map, __msan_unpoison);                               \
   } while (false)
 
 #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end)                           \
index 57938329d2798e5cb5624f0823e0a122a2f867cc..b7162b3c081bd189e1fe97fd6b82ec7097adc260 100644 (file)
@@ -1883,7 +1883,7 @@ TEST(MemorySanitizer, swprintf) {
   ASSERT_EQ(buff[1], '2');
   ASSERT_EQ(buff[2], '3');
   ASSERT_EQ(buff[6], '7');
-  ASSERT_EQ(buff[7], 0);
+  ASSERT_EQ(buff[7], L'\0');
   EXPECT_POISONED(buff[8]);
 }
 
@@ -2886,6 +2886,8 @@ static void GetPathToLoadable(char *buf, size_t sz) {
   static const char basename[] = "libmsan_loadable.mips64.so";
 #elif defined(__mips64)
   static const char basename[] = "libmsan_loadable.mips64el.so";
+#elif defined(__aarch64__)
+  static const char basename[] = "libmsan_loadable.aarch64.so";
 #endif
   int res = snprintf(buf, sz, "%.*s/%s",
                      (int)dir_len, program_path, basename);
@@ -2992,6 +2994,14 @@ static void *SmallStackThread_threadfn(void* data) {
   return 0;
 }
 
+#ifdef PTHREAD_STACK_MIN
+# define SMALLSTACKSIZE    PTHREAD_STACK_MIN
+# define SMALLPRESTACKSIZE PTHREAD_STACK_MIN
+#else
+# define SMALLSTACKSIZE    64 * 1024
+# define SMALLPRESTACKSIZE 16 * 1024
+#endif
+
 TEST(MemorySanitizer, SmallStackThread) {
   pthread_attr_t attr;
   pthread_t t;
@@ -2999,7 +3009,7 @@ TEST(MemorySanitizer, SmallStackThread) {
   int res;
   res = pthread_attr_init(&attr);
   ASSERT_EQ(0, res);
-  res = pthread_attr_setstacksize(&attr, 64 * 1024);
+  res = pthread_attr_setstacksize(&attr, SMALLSTACKSIZE);
   ASSERT_EQ(0, res);
   res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL);
   ASSERT_EQ(0, res);
@@ -3016,7 +3026,7 @@ TEST(MemorySanitizer, SmallPreAllocatedStackThread) {
   res = pthread_attr_init(&attr);
   ASSERT_EQ(0, res);
   void *stack;
-  const size_t kStackSize = 16 * 1024;
+  const size_t kStackSize = SMALLPRESTACKSIZE;
   res = posix_memalign(&stack, 4096, kStackSize);
   ASSERT_EQ(0, res);
   res = pthread_attr_setstack(&attr, stack, kStackSize);
index 6b37b39b4fffa4b49d6861735d43158c0964b341..17eb48a5b27936e4cb70b18de00a73ca44187a8f 100644 (file)
@@ -1,16 +1,58 @@
+
+CHECK_CXX_SOURCE_COMPILES("
+#ifdef _MSC_VER
+#include <Intrin.h> /* Workaround for PR19898. */
+#include <windows.h>
+#endif
+int main() {
+#ifdef _MSC_VER
+        volatile LONG val = 1;
+        MemoryBarrier();
+        InterlockedCompareExchange(&val, 0, 1);
+        InterlockedIncrement(&val);
+        InterlockedDecrement(&val);
+#else
+        volatile unsigned long val = 1;
+        __sync_synchronize();
+        __sync_val_compare_and_swap(&val, 1, 0);
+        __sync_add_and_fetch(&val, 1);
+        __sync_sub_and_fetch(&val, 1);
+#endif
+        return 0;
+      }
+" COMPILER_RT_TARGET_HAS_ATOMICS)
+
 add_custom_target(profile)
 
 set(PROFILE_SOURCES
   GCDAProfiling.c
   InstrProfiling.c
+  InstrProfilingValue.c
   InstrProfilingBuffer.c
   InstrProfilingFile.c
+  InstrProfilingWriter.c
   InstrProfilingPlatformDarwin.c
   InstrProfilingPlatformLinux.c
   InstrProfilingPlatformOther.c
   InstrProfilingRuntime.cc
   InstrProfilingUtil.c)
 
+if(WIN32)
+    list(APPEND PROFILE_SOURCES WindowsMMap.c)
+endif()
+
+if(UNIX)
+ set(EXTRA_FLAGS
+     -fPIC
+     -Wno-pedantic)
+endif()
+
+if(COMPILER_RT_TARGET_HAS_ATOMICS)
+ set(EXTRA_FLAGS
+     ${EXTRA_FLAGS}
+     -DCOMPILER_RT_HAS_ATOMICS=1)
+endif() 
+
 if(APPLE)
   add_compiler_rt_runtime(clang_rt.profile
     STATIC
@@ -22,7 +64,7 @@ else()
   add_compiler_rt_runtime(clang_rt.profile
     STATIC
     ARCHS ${PROFILE_SUPPORTED_ARCH}
-    CFLAGS -fPIC
+    CFLAGS ${EXTRA_FLAGS}
     SOURCES ${PROFILE_SOURCES}
     PARENT_TARGET profile)
 endif()
index aec232856e74ef8d0f7d03365cc6faa1a7451db4..2338761ae1ab3c600ef0044972cc7a8721bf93ee 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+
+#if defined(_WIN32)
+#include "WindowsMMap.h"
+#else
 #include <sys/mman.h>
 #include <sys/file.h>
+#endif
 
 #define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__))
 
@@ -37,6 +42,7 @@
 #endif
 
 #if defined(_MSC_VER)
+typedef unsigned char uint8_t;
 typedef unsigned int uint32_t;
 typedef unsigned long long uint64_t;
 #elif I386_FREEBSD
diff --git a/src/compiler-rt/lib/profile/InstrProfData.inc b/src/compiler-rt/lib/profile/InstrProfData.inc
new file mode 100644 (file)
index 0000000..33c7d94
--- /dev/null
@@ -0,0 +1,767 @@
+/*===-- InstrProfData.inc - instr profiling runtime structures -*- C++ -*-=== *\
+|*
+|*                     The LLVM Compiler Infrastructure
+|*
+|* This file is distributed under the University of Illinois Open Source
+|* License. See LICENSE.TXT for details.
+|*
+\*===----------------------------------------------------------------------===*/
+/*
+ * This is the master file that defines all the data structure, signature,
+ * constant literals that are shared across profiling runtime library,
+ * compiler (instrumentation), and host tools (reader/writer). The entities
+ * defined in this file affect the profile runtime ABI, the raw profile format,
+ * or both.
+ *
+ * The file has two identical copies. The master copy lives in LLVM and
+ * the other one  sits in compiler-rt/lib/profile directory. To make changes
+ * in this file, first modify the master copy and copy it over to compiler-rt.
+ * Testing of any change in this file can start only after the two copies are
+ * synced up.
+ *
+ * The first part of the file includes macros that defines types, names, and
+ * initializers for the member fields of the core data structures. The field
+ * declarations for one structure is enabled by defining the field activation
+ * macro associated with that structure. Only one field activation record
+ * can be defined at one time and the rest definitions will be filtered out by
+ * the preprocessor.
+ *
+ * Examples of how the template is used to instantiate structure definition:
+ * 1. To declare a structure:
+ *
+ * struct ProfData {
+ * #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \
+ *    Type Name;
+ * #include "llvm/ProfileData/InstrProfData.inc"
+ * };
+ *
+ * 2. To construct LLVM type arrays for the struct type:
+ *
+ * Type *DataTypes[] = {
+ * #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \
+ *   LLVMType,
+ * #include "llvm/ProfileData/InstrProfData.inc"
+ * };
+ *
+ * 4. To construct constant array for the initializers:
+ * #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \
+ *   Initializer,
+ * Constant *ConstantVals[] = {
+ * #include "llvm/ProfileData/InstrProfData.inc"
+ * };
+ *
+ *
+ * The second part of the file includes definitions all other entities that
+ * are related to runtime ABI and format. When no field activation macro is
+ * defined, this file can be included to introduce the definitions.
+ *
+\*===----------------------------------------------------------------------===*/
+
+/* INSTR_PROF_DATA start. */
+/* Definition of member fields of the per-function control structure. */
+#ifndef INSTR_PROF_DATA
+#define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer)
+#else
+#define INSTR_PROF_DATA_DEFINED
+#endif
+
+INSTR_PROF_DATA(const uint32_t, llvm::Type::getInt32Ty(Ctx), NameSize, \
+                ConstantInt::get(llvm::Type::getInt32Ty(Ctx), \
+                NamePtr->getType()->getPointerElementType()->getArrayNumElements()))
+INSTR_PROF_DATA(const uint32_t, llvm::Type::getInt32Ty(Ctx), NumCounters, \
+                ConstantInt::get(llvm::Type::getInt32Ty(Ctx), NumCounters))
+INSTR_PROF_DATA(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \
+                ConstantInt::get(llvm::Type::getInt64Ty(Ctx), \
+                Inc->getHash()->getZExtValue()))
+INSTR_PROF_DATA(const IntPtrT, llvm::Type::getInt8PtrTy(Ctx), NamePtr, \
+                ConstantExpr::getBitCast(NamePtr, llvm::Type::getInt8PtrTy(Ctx)))
+INSTR_PROF_DATA(const IntPtrT, llvm::Type::getInt64PtrTy(Ctx), CounterPtr, \
+                ConstantExpr::getBitCast(CounterPtr, \
+                llvm::Type::getInt64PtrTy(Ctx)))
+INSTR_PROF_DATA(const IntPtrT, llvm::Type::getInt8PtrTy(Ctx), FunctionPointer, \
+                FunctionAddr)
+INSTR_PROF_DATA(IntPtrT, llvm::Type::getInt8PtrTy(Ctx), Values, \
+                ConstantPointerNull::get(Int8PtrTy))
+INSTR_PROF_DATA(const uint16_t, Int16ArrayTy, NumValueSites[IPVK_Last+1], \
+                ConstantArray::get(Int16ArrayTy, Int16ArrayVals))
+#undef INSTR_PROF_DATA
+/* INSTR_PROF_DATA end. */
+
+/* INSTR_PROF_RAW_HEADER  start */
+/* Definition of member fields of the raw profile header data structure. */
+#ifndef INSTR_PROF_RAW_HEADER
+#define INSTR_PROF_RAW_HEADER(Type, Name, Initializer)
+#else
+#define INSTR_PROF_DATA_DEFINED
+#endif
+INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic())
+INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version())
+INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize)
+INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize)
+INSTR_PROF_RAW_HEADER(uint64_t, NamesSize,  NamesSize)
+INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin)
+INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin)
+INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last)
+INSTR_PROF_RAW_HEADER(uint64_t, ValueDataSize, ValueDataSize)
+INSTR_PROF_RAW_HEADER(uint64_t, ValueDataDelta, (uintptr_t)ValueDataBegin)
+#undef INSTR_PROF_RAW_HEADER
+/* INSTR_PROF_RAW_HEADER  end */
+
+/* VALUE_PROF_FUNC_PARAM start */
+/* Definition of parameter types of the runtime API used to do value profiling
+ * for a given value site.
+ */
+#ifndef VALUE_PROF_FUNC_PARAM
+#define VALUE_PROF_FUNC_PARAM(ArgType, ArgName, ArgLLVMType)
+#define INSTR_PROF_COMMA
+#else
+#define INSTR_PROF_DATA_DEFINED
+#define INSTR_PROF_COMMA ,
+#endif
+VALUE_PROF_FUNC_PARAM(uint64_t, TargetValue, Type::getInt64Ty(Ctx)) \
+                      INSTR_PROF_COMMA
+VALUE_PROF_FUNC_PARAM(void *, Data, Type::getInt8PtrTy(Ctx)) INSTR_PROF_COMMA
+VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx))
+#undef VALUE_PROF_FUNC_PARAM
+#undef INSTR_PROF_COMMA
+/* VALUE_PROF_FUNC_PARAM end */
+
+/* VALUE_PROF_KIND start */
+#ifndef VALUE_PROF_KIND
+#define VALUE_PROF_KIND(Enumerator, Value)
+#else
+#define INSTR_PROF_DATA_DEFINED
+#endif
+VALUE_PROF_KIND(IPVK_IndirectCallTarget, 0)
+/* These two kinds must be the last to be
+ * declared. This is to make sure the string
+ * array created with the template can be
+ * indexed with the kind value.
+ */
+VALUE_PROF_KIND(IPVK_First, IPVK_IndirectCallTarget)
+VALUE_PROF_KIND(IPVK_Last, IPVK_IndirectCallTarget)
+
+#undef VALUE_PROF_KIND
+/* VALUE_PROF_KIND end */
+
+/* COVMAP_FUNC_RECORD start */
+/* Definition of member fields of the function record structure in coverage
+ * map.
+ */
+#ifndef COVMAP_FUNC_RECORD
+#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Initializer)
+#else
+#define INSTR_PROF_DATA_DEFINED
+#endif
+COVMAP_FUNC_RECORD(const IntPtrT, llvm::Type::getInt8PtrTy(Ctx), \
+                   NamePtr, llvm::ConstantExpr::getBitCast(NamePtr, \
+                   llvm::Type::getInt8PtrTy(Ctx)))
+COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), NameSize, \
+                   llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx),\
+                   NameValue.size()))
+COVMAP_FUNC_RECORD(const uint32_t, llvm::Type::getInt32Ty(Ctx), DataSize, \
+                   llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx),\
+                   CoverageMapping.size()))
+COVMAP_FUNC_RECORD(const uint64_t, llvm::Type::getInt64Ty(Ctx), FuncHash, \
+                   llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), FuncHash))
+#undef COVMAP_FUNC_RECORD
+/* COVMAP_FUNC_RECORD end.  */
+
+/* COVMAP_HEADER start */
+/* Definition of member fields of coverage map header.
+ */
+#ifndef COVMAP_HEADER
+#define COVMAP_HEADER(Type, LLVMType, Name, Initializer)
+#else
+#define INSTR_PROF_DATA_DEFINED
+#endif
+COVMAP_HEADER(uint32_t, Int32Ty, NRecords, \
+              llvm::ConstantInt::get(Int32Ty,  FunctionRecords.size()))
+COVMAP_HEADER(uint32_t, Int32Ty, FilenamesSize, \
+              llvm::ConstantInt::get(Int32Ty, FilenamesSize))
+COVMAP_HEADER(uint32_t, Int32Ty, CoverageSize, \
+              llvm::ConstantInt::get(Int32Ty, CoverageMappingSize))
+COVMAP_HEADER(uint32_t, Int32Ty, Version, \
+              llvm::ConstantInt::get(Int32Ty, CoverageMappingCurrentVersion))
+#undef COVMAP_HEADER
+/* COVMAP_HEADER end.  */
+
+
+#ifdef INSTR_PROF_VALUE_PROF_DATA
+#define INSTR_PROF_DATA_DEFINED
+
+#define INSTR_PROF_MAX_NUM_VAL_PER_SITE 255
+/*!
+ * This is the header of the data structure that defines the on-disk
+ * layout of the value profile data of a particular kind for one function.
+ */
+typedef struct ValueProfRecord {
+  /* The kind of the value profile record. */
+  uint32_t Kind;
+  /*
+   * The number of value profile sites. It is guaranteed to be non-zero;
+   * otherwise the record for this kind won't be emitted.
+   */
+  uint32_t NumValueSites;
+  /*
+   * The first element of the array that stores the number of profiled
+   * values for each value site. The size of the array is NumValueSites.
+   * Since NumValueSites is greater than zero, there is at least one
+   * element in the array.
+   */
+  uint8_t SiteCountArray[1];
+
+  /*
+   * The fake declaration is for documentation purpose only.
+   * Align the start of next field to be on 8 byte boundaries.
+  uint8_t Padding[X];
+   */
+
+  /* The array of value profile data. The size of the array is the sum
+   * of all elements in SiteCountArray[].
+  InstrProfValueData ValueData[];
+   */
+
+#ifdef __cplusplus
+  /*!
+   * \brief Return the number of value sites.
+   */
+  uint32_t getNumValueSites() const { return NumValueSites; }
+  /*!
+   * \brief Read data from this record and save it to Record.
+   */
+  void deserializeTo(InstrProfRecord &Record,
+                     InstrProfRecord::ValueMapType *VMap);
+  /*
+   * In-place byte swap:
+   * Do byte swap for this instance. \c Old is the original order before
+   * the swap, and \c New is the New byte order.
+   */
+  void swapBytes(support::endianness Old, support::endianness New);
+#endif
+} ValueProfRecord;
+
+/*!
+ * Per-function header/control data structure for value profiling
+ * data in indexed format.
+ */
+typedef struct ValueProfData {
+  /*
+   * Total size in bytes including this field. It must be a multiple
+   * of sizeof(uint64_t).
+   */
+  uint32_t TotalSize;
+  /*
+   *The number of value profile kinds that has value profile data.
+   * In this implementation, a value profile kind is considered to
+   * have profile data if the number of value profile sites for the
+   * kind is not zero. More aggressively, the implementation can
+   * choose to check the actual data value: if none of the value sites
+   * has any profiled values, the kind can be skipped.
+   */
+  uint32_t NumValueKinds;
+
+  /*
+   * Following are a sequence of variable length records. The prefix/header
+   * of each record is defined by ValueProfRecord type. The number of
+   * records is NumValueKinds.
+   * ValueProfRecord Record_1;
+   * ValueProfRecord Record_N;
+   */
+
+#if __cplusplus
+  /*!
+   * Return the total size in bytes of the on-disk value profile data
+   * given the data stored in Record.
+   */
+  static uint32_t getSize(const InstrProfRecord &Record);
+  /*!
+   * Return a pointer to \c ValueProfData instance ready to be streamed.
+   */
+  static std::unique_ptr<ValueProfData>
+  serializeFrom(const InstrProfRecord &Record);
+  /*!
+   * Check the integrity of the record. Return the error code when
+   * an error is detected, otherwise return instrprof_error::success.
+   */
+  instrprof_error checkIntegrity();
+  /*!
+   * Return a pointer to \c ValueProfileData instance ready to be read.
+   * All data in the instance are properly byte swapped. The input
+   * data is assumed to be in little endian order.
+   */
+  static ErrorOr<std::unique_ptr<ValueProfData>>
+  getValueProfData(const unsigned char *SrcBuffer,
+                   const unsigned char *const SrcBufferEnd,
+                   support::endianness SrcDataEndianness);
+  /*!
+   * Swap byte order from \c Endianness order to host byte order.
+   */
+  void swapBytesToHost(support::endianness Endianness);
+  /*!
+   * Swap byte order from host byte order to \c Endianness order.
+   */
+  void swapBytesFromHost(support::endianness Endianness);
+  /*!
+   * Return the total size of \c ValueProfileData.
+   */
+  uint32_t getSize() const { return TotalSize; }
+  /*!
+   * Read data from this data and save it to \c Record.
+   */
+  void deserializeTo(InstrProfRecord &Record,
+                     InstrProfRecord::ValueMapType *VMap);
+  void operator delete(void *ptr) { ::operator delete(ptr); }
+#endif
+} ValueProfData;
+
+/*
+ * The closure is designed to abstact away two types of value profile data:
+ * - InstrProfRecord which is the primary data structure used to
+ *   represent profile data in host tools (reader, writer, and profile-use)
+ * - value profile runtime data structure suitable to be used by C
+ *   runtime library.
+ *
+ * Both sources of data need to serialize to disk/memory-buffer in common
+ * format: ValueProfData. The abstraction allows compiler-rt's raw profiler
+ * writer to share the same format and code with indexed profile writer.
+ *
+ * For documentation of the member methods below, refer to corresponding methods
+ * in class InstrProfRecord.
+ */
+typedef struct ValueProfRecordClosure {
+  const void *Record;
+  uint32_t (*GetNumValueKinds)(const void *Record);
+  uint32_t (*GetNumValueSites)(const void *Record, uint32_t VKind);
+  uint32_t (*GetNumValueData)(const void *Record, uint32_t VKind);
+  uint32_t (*GetNumValueDataForSite)(const void *R, uint32_t VK, uint32_t S);
+
+  /*
+   * After extracting the value profile data from the value profile record,
+   * this method is used to map the in-memory value to on-disk value. If
+   * the method is null, value will be written out untranslated.
+   */
+  uint64_t (*RemapValueData)(uint32_t, uint64_t Value);
+  void (*GetValueForSite)(const void *R, InstrProfValueData *Dst, uint32_t K,
+                          uint32_t S, uint64_t (*Mapper)(uint32_t, uint64_t));
+  ValueProfData *(*AllocValueProfData)(size_t TotalSizeInBytes);
+} ValueProfRecordClosure;
+
+/*
+ * A wrapper struct that represents value profile runtime data.
+ * Like InstrProfRecord class which is used by profiling host tools,
+ * ValueProfRuntimeRecord also implements the abstract intefaces defined in
+ * ValueProfRecordClosure so that the runtime data can be serialized using
+ * shared C implementation. In this structure, NumValueSites and Nodes
+ * members are the primary fields while other fields hold the derived
+ * information for fast implementation of closure interfaces.
+ */
+typedef struct ValueProfRuntimeRecord {
+  /* Number of sites for each value profile kind.  */
+  const uint16_t *NumValueSites;
+  /* An array of linked-list headers. The size of of the array is the
+   * total number of value profile sites : sum(NumValueSites[*])). Each
+   * linked-list stores the values profiled for a value profile site. */
+  ValueProfNode **Nodes;
+
+  /* Total number of value profile kinds which have at least one
+   *  value profile sites. */
+  uint32_t NumValueKinds;
+  /* An array recording the number of values tracked at each site.
+   * The size of the array is TotalNumValueSites. */
+  uint8_t *SiteCountArray[IPVK_Last + 1];
+  ValueProfNode **NodesKind[IPVK_Last + 1];
+} ValueProfRuntimeRecord;
+
+/* Forward declarations of C interfaces.  */
+int initializeValueProfRuntimeRecord(ValueProfRuntimeRecord *RuntimeRecord,
+                                     const uint16_t *NumValueSites,
+                                     ValueProfNode **Nodes);
+void finalizeValueProfRuntimeRecord(ValueProfRuntimeRecord *RuntimeRecord);
+uint32_t getValueProfDataSizeRT(const ValueProfRuntimeRecord *Record);
+ValueProfData *
+serializeValueProfDataFromRT(const ValueProfRuntimeRecord *Record,
+                             ValueProfData *Dst);
+uint32_t getNumValueKindsRT(const void *R);
+
+#undef INSTR_PROF_VALUE_PROF_DATA
+#endif  /* INSTR_PROF_VALUE_PROF_DATA */
+
+
+#ifdef INSTR_PROF_COMMON_API_IMPL
+#define INSTR_PROF_DATA_DEFINED
+#ifdef __cplusplus
+#define INSTR_PROF_INLINE inline
+#else
+#define INSTR_PROF_INLINE
+#endif
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+/*!
+ * \brief Return the \c ValueProfRecord header size including the
+ * padding bytes.
+ */
+INSTR_PROF_INLINE
+uint32_t getValueProfRecordHeaderSize(uint32_t NumValueSites) {
+  uint32_t Size = offsetof(ValueProfRecord, SiteCountArray) +
+                  sizeof(uint8_t) * NumValueSites;
+  /* Round the size to multiple of 8 bytes. */
+  Size = (Size + 7) & ~7;
+  return Size;
+}
+
+/*!
+ * \brief Return the total size of the value profile record including the
+ * header and the value data.
+ */
+INSTR_PROF_INLINE
+uint32_t getValueProfRecordSize(uint32_t NumValueSites,
+                                uint32_t NumValueData) {
+  return getValueProfRecordHeaderSize(NumValueSites) +
+         sizeof(InstrProfValueData) * NumValueData;
+}
+
+/*!
+ * \brief Return the pointer to the start of value data array.
+ */
+INSTR_PROF_INLINE
+InstrProfValueData *getValueProfRecordValueData(ValueProfRecord *This) {
+  return (InstrProfValueData *)((char *)This + getValueProfRecordHeaderSize(
+                                                   This->NumValueSites));
+}
+
+/*!
+ * \brief Return the total number of value data for \c This record.
+ */
+INSTR_PROF_INLINE
+uint32_t getValueProfRecordNumValueData(ValueProfRecord *This) {
+  uint32_t NumValueData = 0;
+  uint32_t I;
+  for (I = 0; I < This->NumValueSites; I++)
+    NumValueData += This->SiteCountArray[I];
+  return NumValueData;
+}
+
+/*!
+ * \brief Use this method to advance to the next \c This \c ValueProfRecord.
+ */
+INSTR_PROF_INLINE
+ValueProfRecord *getValueProfRecordNext(ValueProfRecord *This) {
+  uint32_t NumValueData = getValueProfRecordNumValueData(This);
+  return (ValueProfRecord *)((char *)This +
+                             getValueProfRecordSize(This->NumValueSites,
+                                                    NumValueData));
+}
+
+/*!
+ * \brief Return the first \c ValueProfRecord instance.
+ */
+INSTR_PROF_INLINE
+ValueProfRecord *getFirstValueProfRecord(ValueProfData *This) {
+  return (ValueProfRecord *)((char *)This + sizeof(ValueProfData));
+}
+
+/* Closure based interfaces.  */
+
+/*!
+ * Return the total size in bytes of the on-disk value profile data
+ * given the data stored in Record.
+ */
+uint32_t getValueProfDataSize(ValueProfRecordClosure *Closure) {
+  uint32_t Kind;
+  uint32_t TotalSize = sizeof(ValueProfData);
+  const void *Record = Closure->Record;
+  uint32_t NumValueKinds = Closure->GetNumValueKinds(Record);
+  if (NumValueKinds == 0)
+    return TotalSize;
+
+  for (Kind = IPVK_First; Kind <= IPVK_Last; Kind++) {
+    uint32_t NumValueSites = Closure->GetNumValueSites(Record, Kind);
+    if (!NumValueSites)
+      continue;
+    TotalSize += getValueProfRecordSize(NumValueSites,
+                                        Closure->GetNumValueData(Record, Kind));
+  }
+  return TotalSize;
+}
+
+/*!
+ * Extract value profile data of a function for the profile kind \c ValueKind
+ * from the \c Closure and serialize the data into \c This record instance.
+ */
+void serializeValueProfRecordFrom(ValueProfRecord *This,
+                                  ValueProfRecordClosure *Closure,
+                                  uint32_t ValueKind, uint32_t NumValueSites) {
+  uint32_t S;
+  const void *Record = Closure->Record;
+  This->Kind = ValueKind;
+  This->NumValueSites = NumValueSites;
+  InstrProfValueData *DstVD = getValueProfRecordValueData(This);
+
+  for (S = 0; S < NumValueSites; S++) {
+    uint32_t ND = Closure->GetNumValueDataForSite(Record, ValueKind, S);
+    This->SiteCountArray[S] = ND;
+    Closure->GetValueForSite(Record, DstVD, ValueKind, S,
+                             Closure->RemapValueData);
+    DstVD += ND;
+  }
+}
+
+/*!
+ * Extract value profile data of a function  from the \c Closure
+ * and serialize the data into \c DstData if it is not NULL or heap
+ * memory allocated by the \c Closure's allocator method.
+ */
+ValueProfData *serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
+                                          ValueProfData *DstData) {
+  uint32_t Kind;
+  uint32_t TotalSize = getValueProfDataSize(Closure);
+
+  ValueProfData *VPD =
+      DstData ? DstData : Closure->AllocValueProfData(TotalSize);
+
+  VPD->TotalSize = TotalSize;
+  VPD->NumValueKinds = Closure->GetNumValueKinds(Closure->Record);
+  ValueProfRecord *VR = getFirstValueProfRecord(VPD);
+  for (Kind = IPVK_First; Kind <= IPVK_Last; Kind++) {
+    uint32_t NumValueSites = Closure->GetNumValueSites(Closure->Record, Kind);
+    if (!NumValueSites)
+      continue;
+    serializeValueProfRecordFrom(VR, Closure, Kind, NumValueSites);
+    VR = getValueProfRecordNext(VR);
+  }
+  return VPD;
+}
+
+/*
+ * The value profiler runtime library stores the value profile data
+ * for a given function in \c NumValueSites and \c Nodes structures.
+ * \c ValueProfRuntimeRecord class is used to encapsulate the runtime
+ * profile data and provides fast interfaces to retrieve the profile
+ * information. This interface is used to initialize the runtime record
+ * and pre-compute the information needed for efficient implementation
+ * of callbacks required by ValueProfRecordClosure class.
+ */
+int initializeValueProfRuntimeRecord(ValueProfRuntimeRecord *RuntimeRecord,
+                                     const uint16_t *NumValueSites,
+                                     ValueProfNode **Nodes) {
+  unsigned I, J, S = 0, NumValueKinds = 0;
+  RuntimeRecord->NumValueSites = NumValueSites;
+  RuntimeRecord->Nodes = Nodes;
+  for (I = 0; I <= IPVK_Last; I++) {
+    uint16_t N = NumValueSites[I];
+    if (!N) {
+      RuntimeRecord->SiteCountArray[I] = 0;
+      continue;
+    }
+    NumValueKinds++;
+    RuntimeRecord->SiteCountArray[I] = (uint8_t *)calloc(N, 1);
+    if (!RuntimeRecord->SiteCountArray[I])
+      return 1;
+    RuntimeRecord->NodesKind[I] = Nodes ? &Nodes[S] : NULL;
+    for (J = 0; J < N; J++) {
+      /* Compute value count for each site. */
+      uint32_t C = 0;
+      ValueProfNode *Site = Nodes ? RuntimeRecord->NodesKind[I][J] : NULL;
+      while (Site) {
+        C++;
+        Site = Site->Next;
+      }
+      if (C > UCHAR_MAX)
+        C = UCHAR_MAX;
+      RuntimeRecord->SiteCountArray[I][J] = C;
+    }
+    S += N;
+  }
+  RuntimeRecord->NumValueKinds = NumValueKinds;
+  return 0;
+}
+
+void finalizeValueProfRuntimeRecord(ValueProfRuntimeRecord *RuntimeRecord) {
+  unsigned I;
+  for (I = 0; I <= IPVK_Last; I++) {
+    if (RuntimeRecord->SiteCountArray[I])
+      free(RuntimeRecord->SiteCountArray[I]);
+  }
+}
+
+/* ValueProfRecordClosure Interface implementation for
+ * ValueProfDataRuntimeRecord.  */
+uint32_t getNumValueKindsRT(const void *R) {
+  return ((const ValueProfRuntimeRecord *)R)->NumValueKinds;
+}
+
+uint32_t getNumValueSitesRT(const void *R, uint32_t VK) {
+  return ((const ValueProfRuntimeRecord *)R)->NumValueSites[VK];
+}
+
+uint32_t getNumValueDataForSiteRT(const void *R, uint32_t VK, uint32_t S) {
+  const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R;
+  return Record->SiteCountArray[VK][S];
+}
+
+uint32_t getNumValueDataRT(const void *R, uint32_t VK) {
+  unsigned I, S = 0;
+  const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R;
+  if (Record->SiteCountArray[VK] == 0)
+    return 0;
+  for (I = 0; I < Record->NumValueSites[VK]; I++)
+    S += Record->SiteCountArray[VK][I];
+  return S;
+}
+
+void getValueForSiteRT(const void *R, InstrProfValueData *Dst, uint32_t VK,
+                       uint32_t S, uint64_t (*Mapper)(uint32_t, uint64_t)) {
+  unsigned I, N = 0;
+  const ValueProfRuntimeRecord *Record = (const ValueProfRuntimeRecord *)R;
+  N = getNumValueDataForSiteRT(R, VK, S);
+  if (N == 0)
+    return;
+  ValueProfNode *VNode = Record->NodesKind[VK][S];
+  for (I = 0; I < N; I++) {
+    Dst[I] = VNode->VData;
+    VNode = VNode->Next;
+  }
+}
+
+ValueProfData *allocValueProfDataRT(size_t TotalSizeInBytes) {
+  return (ValueProfData *)calloc(TotalSizeInBytes, 1);
+}
+
+static ValueProfRecordClosure RTRecordClosure = {0,
+                                                 getNumValueKindsRT,
+                                                 getNumValueSitesRT,
+                                                 getNumValueDataRT,
+                                                 getNumValueDataForSiteRT,
+                                                 0,
+                                                 getValueForSiteRT,
+                                                 allocValueProfDataRT};
+
+/*
+ * Return the size of ValueProfData structure to store data
+ * recorded in the runtime record.
+ */
+uint32_t getValueProfDataSizeRT(const ValueProfRuntimeRecord *Record) {
+  RTRecordClosure.Record = Record;
+  return getValueProfDataSize(&RTRecordClosure);
+}
+
+/*
+ * Return a ValueProfData instance that stores the data collected
+ * from runtime. If \c DstData is provided by the caller, the value
+ * profile data will be store in *DstData and DstData is returned,
+ * otherwise the method will allocate space for the value data and
+ * return pointer to the newly allocated space.
+ */
+ValueProfData *
+serializeValueProfDataFromRT(const ValueProfRuntimeRecord *Record,
+                             ValueProfData *DstData) {
+  RTRecordClosure.Record = Record;
+  return serializeValueProfDataFrom(&RTRecordClosure, DstData);
+}
+
+
+#undef INSTR_PROF_COMMON_API_IMPL
+#endif /* INSTR_PROF_COMMON_API_IMPL */
+
+/*============================================================================*/
+
+
+#ifndef INSTR_PROF_DATA_DEFINED
+
+#ifndef INSTR_PROF_DATA_INC_
+#define INSTR_PROF_DATA_INC_
+
+/* Helper macros.  */
+#define INSTR_PROF_SIMPLE_QUOTE(x) #x
+#define INSTR_PROF_QUOTE(x) INSTR_PROF_SIMPLE_QUOTE(x)
+#define INSTR_PROF_SIMPLE_CONCAT(x,y) x ## y
+#define INSTR_PROF_CONCAT(x,y) INSTR_PROF_SIMPLE_CONCAT(x,y)
+
+/* Magic number to detect file format and endianness.
+ * Use 255 at one end, since no UTF-8 file can use that character.  Avoid 0,
+ * so that utilities, like strings, don't grab it as a string.  129 is also
+ * invalid UTF-8, and high enough to be interesting.
+ * Use "lprofr" in the centre to stand for "LLVM Profile Raw", or "lprofR"
+ * for 32-bit platforms.
+ */
+#define INSTR_PROF_RAW_MAGIC_64 (uint64_t)255 << 56 | (uint64_t)'l' << 48 | \
+       (uint64_t)'p' << 40 | (uint64_t)'r' << 32 | (uint64_t)'o' << 24 |  \
+        (uint64_t)'f' << 16 | (uint64_t)'r' << 8 | (uint64_t)129
+#define INSTR_PROF_RAW_MAGIC_32 (uint64_t)255 << 56 | (uint64_t)'l' << 48 | \
+       (uint64_t)'p' << 40 | (uint64_t)'r' << 32 | (uint64_t)'o' << 24 |  \
+        (uint64_t)'f' << 16 | (uint64_t)'R' << 8 | (uint64_t)129
+
+/* Raw profile format version. */
+#define INSTR_PROF_RAW_VERSION 2
+#define INSTR_PROF_INDEX_VERSION 3
+#define INSTR_PROF_COVMAP_VERSION 0
+
+/* Profile version is always of type uint_64_t. Reserve the upper 8 bits in the
+ * version for other variants of profile. We set the lowest bit of the upper 8
+ * bits (i.e. bit 56) to 1 to indicate if this is an IR-level instrumentaiton
+ * generated profile, and 0 if this is a Clang FE generated profile.
+*/
+#define VARIANT_MASKS_ALL 0xff00000000000000ULL
+#define GET_VERSION(V) ((V) & ~VARIANT_MASKS_ALL)
+
+/* Runtime section names and name strings.  */
+#define INSTR_PROF_DATA_SECT_NAME __llvm_prf_data
+#define INSTR_PROF_NAME_SECT_NAME __llvm_prf_names
+#define INSTR_PROF_CNTS_SECT_NAME __llvm_prf_cnts
+#define INSTR_PROF_COVMAP_SECT_NAME __llvm_covmap
+
+#define INSTR_PROF_DATA_SECT_NAME_STR                                          \
+  INSTR_PROF_QUOTE(INSTR_PROF_DATA_SECT_NAME)
+#define INSTR_PROF_NAME_SECT_NAME_STR                                          \
+  INSTR_PROF_QUOTE(INSTR_PROF_NAME_SECT_NAME)
+#define INSTR_PROF_CNTS_SECT_NAME_STR                                          \
+  INSTR_PROF_QUOTE(INSTR_PROF_CNTS_SECT_NAME)
+#define INSTR_PROF_COVMAP_SECT_NAME_STR                                        \
+  INSTR_PROF_QUOTE(INSTR_PROF_COVMAP_SECT_NAME)
+
+/* Macros to define start/stop section symbol for a given
+ * section on Linux. For instance
+ * INSTR_PROF_SECT_START(INSTR_PROF_DATA_SECT_NAME) will
+ * expand to __start___llvm_prof_data
+ */
+#define INSTR_PROF_SECT_START(Sect) \
+        INSTR_PROF_CONCAT(__start_,Sect)
+#define INSTR_PROF_SECT_STOP(Sect) \
+        INSTR_PROF_CONCAT(__stop_,Sect)
+
+/* Value Profiling API linkage name.  */
+#define INSTR_PROF_VALUE_PROF_FUNC __llvm_profile_instrument_target
+#define INSTR_PROF_VALUE_PROF_FUNC_STR \
+        INSTR_PROF_QUOTE(INSTR_PROF_VALUE_PROF_FUNC)
+
+/* InstrProfile per-function control data alignment.  */
+#define INSTR_PROF_DATA_ALIGNMENT 8
+
+/* The data structure that represents a tracked value by the
+ * value profiler.
+ */
+typedef struct InstrProfValueData {
+  /* Profiled value. */
+  uint64_t Value;
+  /* Number of times the value appears in the training run. */
+  uint64_t Count;
+} InstrProfValueData;
+
+/* This is an internal data structure used by value profiler. It
+ * is defined here to allow serialization code sharing by LLVM
+ * to be used in unit test.
+ */
+typedef struct ValueProfNode {
+  InstrProfValueData VData;
+  struct ValueProfNode *Next;
+} ValueProfNode;
+
+#endif /* INSTR_PROF_DATA_INC_ */
+
+#else
+#undef INSTR_PROF_DATA_DEFINED
+#endif
index 8d010df28f18b4ec53000fda0fe2cfb440076062..711f2b608a5fda86a0ac8d35894555d3179bb37e 100644 (file)
@@ -8,41 +8,62 @@
 \*===----------------------------------------------------------------------===*/
 
 #include "InstrProfiling.h"
+#include "InstrProfilingInternal.h"
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#define INSTR_PROF_VALUE_PROF_DATA
+#include "InstrProfData.inc"
 
-__attribute__((visibility("hidden")))
-uint64_t __llvm_profile_get_magic(void) {
-  /* Magic number to detect file format and endianness.
-   *
-   * Use 255 at one end, since no UTF-8 file can use that character.  Avoid 0,
-   * so that utilities, like strings, don't grab it as a string.  129 is also
-   * invalid UTF-8, and high enough to be interesting.
-   *
-   * Use "lprofr" in the centre to stand for "LLVM Profile Raw", or "lprofR"
-   * for 32-bit platforms.
-   */
-  unsigned char R = sizeof(void *) == sizeof(uint64_t) ? 'r' : 'R';
-  return
-    (uint64_t)255 << 56 |
-    (uint64_t)'l' << 48 |
-    (uint64_t)'p' << 40 |
-    (uint64_t)'r' << 32 |
-    (uint64_t)'o' << 24 |
-    (uint64_t)'f' << 16 |
-    (uint64_t) R  <<  8 |
-    (uint64_t)129;
+char *(*GetEnvHook)(const char *) = 0;
+
+COMPILER_RT_WEAK uint64_t __llvm_profile_raw_version = INSTR_PROF_RAW_VERSION;
+
+COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_magic(void) {
+  return sizeof(void *) == sizeof(uint64_t) ? (INSTR_PROF_RAW_MAGIC_64)
+                                            : (INSTR_PROF_RAW_MAGIC_32);
 }
 
-__attribute__((visibility("hidden")))
-uint64_t __llvm_profile_get_version(void) {
-  /* This should be bumped any time the output format changes. */
-  return 1;
+/* Return the number of bytes needed to add to SizeInBytes to make it
+ *   the result a multiple of 8.
+ */
+COMPILER_RT_VISIBILITY uint8_t
+__llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes) {
+  return 7 & (sizeof(uint64_t) - SizeInBytes % sizeof(uint64_t));
 }
 
-__attribute__((visibility("hidden")))
-void __llvm_profile_reset_counters(void) {
+COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_version(void) {
+  return __llvm_profile_raw_version;
+}
+
+COMPILER_RT_VISIBILITY void __llvm_profile_reset_counters(void) {
   uint64_t *I = __llvm_profile_begin_counters();
   uint64_t *E = __llvm_profile_end_counters();
 
-  memset(I, 0, sizeof(uint64_t)*(E - I));
+  memset(I, 0, sizeof(uint64_t) * (E - I));
+
+  const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
+  const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
+  const __llvm_profile_data *DI;
+  for (DI = DataBegin; DI != DataEnd; ++DI) {
+    uint64_t CurrentVSiteCount = 0;
+    uint32_t VKI, i;
+    if (!DI->Values)
+      continue;
+
+    ValueProfNode **ValueCounters = (ValueProfNode **)DI->Values;
+
+    for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI)
+      CurrentVSiteCount += DI->NumValueSites[VKI];
+
+    for (i = 0; i < CurrentVSiteCount; ++i) {
+      ValueProfNode *CurrentVNode = ValueCounters[i];
+
+      while (CurrentVNode) {
+        CurrentVNode->VData.Count = 0;
+        CurrentVNode = CurrentVNode->Next;
+      }
+    }
+  }
 }
index 7aac3b4c29c0b14578a244f38edf17636dc24c96..d27ca569d535eeb720496b8692f1b6e5cc1e2aee 100644 (file)
 #ifndef PROFILE_INSTRPROFILING_H_
 #define PROFILE_INSTRPROFILING_H_
 
-#if defined(__FreeBSD__) && defined(__i386__)
-
-/* System headers define 'size_t' incorrectly on x64 FreeBSD (prior to
- * FreeBSD 10, r232261) when compiled in 32-bit mode.
- */
-#define PRIu64 "llu"
-typedef unsigned int uint32_t;
-typedef unsigned long long uint64_t;
-typedef uint32_t uintptr_t;
-
-#else /* defined(__FreeBSD__) && defined(__i386__) */
-
-#include <inttypes.h>
-#include <stdint.h>
-
-#endif /* defined(__FreeBSD__) && defined(__i386__) */
-
-
-typedef struct __llvm_profile_data {
-  const uint32_t NameSize;
-  const uint32_t NumCounters;
-  const uint64_t FuncHash;
-  const char *const Name;
-  uint64_t *const Counters;
+#include "InstrProfilingPort.h"
+#include "InstrProfData.inc"
+
+enum ValueKind {
+#define VALUE_PROF_KIND(Enumerator, Value) Enumerator = Value,
+#include "InstrProfData.inc"
+};
+
+typedef void *IntPtrT;
+typedef struct COMPILER_RT_ALIGNAS(INSTR_PROF_DATA_ALIGNMENT)
+    __llvm_profile_data {
+#define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) Type Name;
+#include "InstrProfData.inc"
 } __llvm_profile_data;
 
 typedef struct __llvm_profile_header {
-  uint64_t Magic;
-  uint64_t Version;
-  uint64_t DataSize;
-  uint64_t CountersSize;
-  uint64_t NamesSize;
-  uint64_t CountersDelta;
-  uint64_t NamesDelta;
+#define INSTR_PROF_RAW_HEADER(Type, Name, Initializer) Type Name;
+#include "InstrProfData.inc"
 } __llvm_profile_header;
 
+/*!
+ * \brief Get number of bytes necessary to pad the argument to eight
+ * byte boundary.
+ */
+uint8_t __llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes);
 
 /*!
  * \brief Get required size for profile buffer.
@@ -67,10 +56,38 @@ const char *__llvm_profile_end_names(void);
 uint64_t *__llvm_profile_begin_counters(void);
 uint64_t *__llvm_profile_end_counters(void);
 
+/*!
+ * \brief Clear profile counters to zero.
+ *
+ */
+void __llvm_profile_reset_counters(void);
+
+/*!
+ * \brief Counts the number of times a target value is seen.
+ *
+ * Records the target value for the CounterIndex if not seen before. Otherwise,
+ * increments the counter associated w/ the target value.
+ * void __llvm_profile_instrument_target(uint64_t TargetValue, void *Data,
+ *                                       uint32_t CounterIndex);
+ */
+void INSTR_PROF_VALUE_PROF_FUNC(
+#define VALUE_PROF_FUNC_PARAM(ArgType, ArgName, ArgLLVMType) ArgType ArgName
+#include "InstrProfData.inc"
+);
+
+/*!
+ * \brief Prepares the value profiling data for output.
+ *
+ * Returns an array of pointers to value profile data.
+ */
+struct ValueProfData;
+struct ValueProfData **__llvm_profile_gather_value_data(uint64_t *Size);
+
 /*!
  * \brief Write instrumentation data to the current file.
  *
- * Writes to the file with the last name given to \a __llvm_profile_set_filename(),
+ * Writes to the file with the last name given to \a *
+ * __llvm_profile_set_filename(),
  * or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable,
  * or if that's not set, the last name given to
  * \a __llvm_profile_override_default_filename(), or if that's not set,
index e587932da0a6ce60c7fa0d265b1e8765954c91b0..4227ca6b66ea65e1de92fca4f1cdc1df2179e023 100644 (file)
@@ -10,9 +10,7 @@
 #include "InstrProfiling.h"
 #include "InstrProfilingInternal.h"
 
-#include <string.h>
-
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 uint64_t __llvm_profile_get_size_for_buffer(void) {
   const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
   const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
@@ -27,78 +25,28 @@ uint64_t __llvm_profile_get_size_for_buffer(void) {
 
 #define PROFILE_RANGE_SIZE(Range) (Range##End - Range##Begin)
 
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 uint64_t __llvm_profile_get_size_for_buffer_internal(
-        const __llvm_profile_data *DataBegin,
-        const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin,
-        const uint64_t *CountersEnd, const char *NamesBegin,
-        const char *NamesEnd) {
+    const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd,
+    const uint64_t *CountersBegin, const uint64_t *CountersEnd,
+    const char *NamesBegin, const char *NamesEnd) {
   /* Match logic in __llvm_profile_write_buffer(). */
   const uint64_t NamesSize = PROFILE_RANGE_SIZE(Names) * sizeof(char);
-  const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t);
+  const uint8_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize);
   return sizeof(__llvm_profile_header) +
-      PROFILE_RANGE_SIZE(Data) * sizeof(__llvm_profile_data) +
-      PROFILE_RANGE_SIZE(Counters) * sizeof(uint64_t) +
-      NamesSize + Padding;
+         PROFILE_RANGE_SIZE(Data) * sizeof(__llvm_profile_data) +
+         PROFILE_RANGE_SIZE(Counters) * sizeof(uint64_t) + NamesSize + Padding;
 }
 
-__attribute__((visibility("hidden")))
-int __llvm_profile_write_buffer(char *Buffer) {
-  /* Match logic in __llvm_profile_get_size_for_buffer().
-   * Match logic in __llvm_profile_write_file().
-   */
-  const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
-  const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
-  const uint64_t *CountersBegin = __llvm_profile_begin_counters();
-  const uint64_t *CountersEnd   = __llvm_profile_end_counters();
-  const char *NamesBegin = __llvm_profile_begin_names();
-  const char *NamesEnd   = __llvm_profile_end_names();
-
-  return __llvm_profile_write_buffer_internal(Buffer, DataBegin, DataEnd,
-                                              CountersBegin, CountersEnd,
-                                              NamesBegin, NamesEnd);
+COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer(char *Buffer) {
+  return llvmWriteProfData(llvmBufferWriter, Buffer, 0, 0);
 }
 
-__attribute__((visibility("hidden")))
-int __llvm_profile_write_buffer_internal(
+COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer_internal(
     char *Buffer, const __llvm_profile_data *DataBegin,
     const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin,
     const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd) {
-  /* Match logic in __llvm_profile_get_size_for_buffer().
-   * Match logic in __llvm_profile_write_file().
-   */
-
-  /* Calculate size of sections. */
-  const uint64_t DataSize = DataEnd - DataBegin;
-  const uint64_t CountersSize = CountersEnd - CountersBegin;
-  const uint64_t NamesSize = NamesEnd - NamesBegin;
-  const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t);
-
-  /* Enough zeroes for padding. */
-  const char Zeroes[sizeof(uint64_t)] = {0};
-
-  /* Create the header. */
-  __llvm_profile_header Header;
-  Header.Magic = __llvm_profile_get_magic();
-  Header.Version = __llvm_profile_get_version();
-  Header.DataSize = DataSize;
-  Header.CountersSize = CountersSize;
-  Header.NamesSize = NamesSize;
-  Header.CountersDelta = (uintptr_t)CountersBegin;
-  Header.NamesDelta = (uintptr_t)NamesBegin;
-
-  /* Write the data. */
-#define UPDATE_memcpy(Data, Size) \
-  do {                            \
-    memcpy(Buffer, Data, Size);   \
-    Buffer += Size;               \
-  } while (0)
-  UPDATE_memcpy(&Header,  sizeof(__llvm_profile_header));
-  UPDATE_memcpy(DataBegin,     DataSize      * sizeof(__llvm_profile_data));
-  UPDATE_memcpy(CountersBegin, CountersSize  * sizeof(uint64_t));
-  UPDATE_memcpy(NamesBegin,    NamesSize     * sizeof(char));
-  UPDATE_memcpy(Zeroes,        Padding       * sizeof(char));
-#undef UPDATE_memcpy
-
-  return 0;
+  return llvmWriteProfDataImpl(llvmBufferWriter, Buffer, DataBegin, DataEnd,
+                               CountersBegin, CountersEnd, 0, 0, NamesBegin,
+                               NamesEnd);
 }
index 6af835e484acea1dbf99d31114e83ee60d842dcc..68d088a1956ad2341590ce8a1627ee69e4f373f5 100644 (file)
@@ -8,6 +8,7 @@
 \*===----------------------------------------------------------------------===*/
 
 #include "InstrProfiling.h"
+#include "InstrProfilingInternal.h"
 #include "InstrProfilingUtil.h"
 #include <errno.h>
 #include <stdio.h>
 
 #define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
 
-static int writeFile(FILE *File) {
-  /* Match logic in __llvm_profile_write_buffer(). */
-  const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
-  const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
-  const uint64_t *CountersBegin = __llvm_profile_begin_counters();
-  const uint64_t *CountersEnd   = __llvm_profile_end_counters();
-  const char *NamesBegin = __llvm_profile_begin_names();
-  const char *NamesEnd   = __llvm_profile_end_names();
-
-  /* Calculate size of sections. */
-  const uint64_t DataSize = DataEnd - DataBegin;
-  const uint64_t CountersSize = CountersEnd - CountersBegin;
-  const uint64_t NamesSize = NamesEnd - NamesBegin;
-  const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t);
-
-  /* Enough zeroes for padding. */
-  const char Zeroes[sizeof(uint64_t)] = {0};
-
-  /* Create the header. */
-  __llvm_profile_header Header;
-  Header.Magic = __llvm_profile_get_magic();
-  Header.Version = __llvm_profile_get_version();
-  Header.DataSize = DataSize;
-  Header.CountersSize = CountersSize;
-  Header.NamesSize = NamesSize;
-  Header.CountersDelta = (uintptr_t)CountersBegin;
-  Header.NamesDelta = (uintptr_t)NamesBegin;
-
-  /* Write the data. */
-#define CHECK_fwrite(Data, Size, Length, File) \
-  do { if (fwrite(Data, Size, Length, File) != Length) return -1; } while (0)
-  CHECK_fwrite(&Header,        sizeof(__llvm_profile_header), 1, File);
-  CHECK_fwrite(DataBegin,     sizeof(__llvm_profile_data), DataSize, File);
-  CHECK_fwrite(CountersBegin, sizeof(uint64_t), CountersSize, File);
-  CHECK_fwrite(NamesBegin,    sizeof(char), NamesSize, File);
-  CHECK_fwrite(Zeroes,        sizeof(char), Padding, File);
-#undef CHECK_fwrite
-
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#endif
+
+/* Return 1 if there is an error, otherwise return  0.  */
+static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs,
+                           void **WriterCtx) {
+  uint32_t I;
+  FILE *File = (FILE *)*WriterCtx;
+  for (I = 0; I < NumIOVecs; I++) {
+    if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) !=
+        IOVecs[I].NumElm)
+      return 1;
+  }
   return 0;
 }
 
+COMPILER_RT_VISIBILITY ProfBufferIO *
+llvmCreateBufferIOInternal(void *File, uint32_t BufferSz) {
+  CallocHook = calloc;
+  FreeHook = free;
+  return llvmCreateBufferIO(fileWriter, File, BufferSz);
+}
+
+static int writeFile(FILE *File) {
+  const char *BufferSzStr = 0;
+  uint64_t ValueDataSize = 0;
+  struct ValueProfData **ValueDataArray =
+      __llvm_profile_gather_value_data(&ValueDataSize);
+  FreeHook = &free;
+  CallocHook = &calloc;
+  BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE");
+  if (BufferSzStr && BufferSzStr[0])
+    VPBufferSize = atoi(BufferSzStr);
+  return llvmWriteProfData(fileWriter, File, ValueDataArray, ValueDataSize);
+}
+
 static int writeFileWithName(const char *OutputName) {
   int RetVal;
   FILE *OutputFile;
@@ -64,7 +61,7 @@ static int writeFileWithName(const char *OutputName) {
     return -1;
 
   /* Append to the file to support profiling multiple shared objects. */
-  OutputFile = fopen(OutputName, "a");
+  OutputFile = fopen(OutputName, "ab");
   if (!OutputFile)
     return -1;
 
@@ -74,8 +71,8 @@ static int writeFileWithName(const char *OutputName) {
   return RetVal;
 }
 
-__attribute__((weak)) int __llvm_profile_OwnsFilename = 0;
-__attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL;
+COMPILER_RT_WEAK int __llvm_profile_OwnsFilename = 0;
+COMPILER_RT_WEAK const char *__llvm_profile_CurrentFilename = NULL;
 
 static void truncateCurrentFile(void) {
   const char *Filename;
@@ -182,7 +179,7 @@ static void setFilenameAutomatically(void) {
   resetFilenameToDefault();
 }
 
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 void __llvm_profile_initialize_file(void) {
   /* Check if the filename has been initialized. */
   if (__llvm_profile_CurrentFilename)
@@ -192,12 +189,12 @@ void __llvm_profile_initialize_file(void) {
   setFilenameAutomatically();
 }
 
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 void __llvm_profile_set_filename(const char *Filename) {
   setFilenamePossiblyWithPid(Filename);
 }
 
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 void __llvm_profile_override_default_filename(const char *Filename) {
   /* If the env var is set, skip setting filename from argument. */
   const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
@@ -206,27 +203,37 @@ void __llvm_profile_override_default_filename(const char *Filename) {
   setFilenamePossiblyWithPid(Filename);
 }
 
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 int __llvm_profile_write_file(void) {
   int rc;
 
+  GetEnvHook = &getenv;
   /* Check the filename. */
-  if (!__llvm_profile_CurrentFilename)
+  if (!__llvm_profile_CurrentFilename) {
+    PROF_ERR("LLVM Profile: Failed to write file : %s\n", "Filename not set");
+    return -1;
+  }
+
+  /* Check if there is llvm/runtime version mismatch.  */
+  if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
+    PROF_ERR("LLVM Profile: runtime and instrumentation version mismatch : "
+             "expected %d, but get %d\n",
+             INSTR_PROF_RAW_VERSION,
+             (int)GET_VERSION(__llvm_profile_get_version()));
     return -1;
+  }
 
   /* Write the file. */
   rc = writeFileWithName(__llvm_profile_CurrentFilename);
-  if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS"))
-    fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n",
+  if (rc)
+    PROF_ERR("LLVM Profile: Failed to write file \"%s\": %s\n",
             __llvm_profile_CurrentFilename, strerror(errno));
   return rc;
 }
 
-static void writeFileWithoutReturn(void) {
-  __llvm_profile_write_file();
-}
+static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); }
 
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 int __llvm_profile_register_write_file_atexit(void) {
   static int HasBeenRegistered = 0;
 
index ede39cd9d7138c8fcda2aed43f27d1801584b179..4aab78ea509ce28ddcdcd78b91c8e05c5b2b68ce 100644 (file)
@@ -11,6 +11,7 @@
 #define PROFILE_INSTRPROFILING_INTERNALH_
 
 #include "InstrProfiling.h"
+#include "stddef.h"
 
 /*!
  * \brief Write instrumentation data to the given buffer, given explicit
@@ -37,4 +38,81 @@ int __llvm_profile_write_buffer_internal(
     const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin,
     const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd);
 
+/*!
+ * The data structure describing the data to be written by the
+ * low level writer callback function.
+ */
+typedef struct ProfDataIOVec {
+  const void *Data;
+  size_t ElmSize;
+  size_t NumElm;
+} ProfDataIOVec;
+
+typedef uint32_t (*WriterCallback)(ProfDataIOVec *, uint32_t NumIOVecs,
+                                   void **WriterCtx);
+
+/*!
+ * The data structure for buffered IO of profile data.
+ */
+typedef struct ProfBufferIO {
+  /* File handle.  */
+  void *File;
+  /* Low level IO callback. */
+  WriterCallback FileWriter;
+  /* The start of the buffer. */
+  uint8_t *BufferStart;
+  /* Total size of the buffer. */
+  uint32_t BufferSz;
+  /* Current byte offset from the start of the buffer. */
+  uint32_t CurOffset;
+} ProfBufferIO;
+
+/* The creator interface used by testing.  */
+ProfBufferIO *llvmCreateBufferIOInternal(void *File, uint32_t DefaultBufferSz);
+/*!
+ * This is the interface to create a handle for buffered IO.
+ */
+ProfBufferIO *llvmCreateBufferIO(WriterCallback FileWriter, void *File,
+                                 uint32_t DefaultBufferSz);
+/*!
+ * The interface to destroy the bufferIO handle and reclaim
+ * the memory.
+ */
+void llvmDeleteBufferIO(ProfBufferIO *BufferIO);
+
+/*!
+ * This is the interface to write \c Data of \c Size bytes through
+ * \c BufferIO. Returns 0 if successful, otherwise return -1.
+ */
+int llvmBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data,
+                      uint32_t Size);
+/*!
+ * The interface to flush the remaining data in the buffer.
+ * through the low level writer callback.
+ */
+int llvmBufferIOFlush(ProfBufferIO *BufferIO);
+
+/* The low level interface to write data into a buffer. It is used as the
+ * callback by other high level writer methods such as buffered IO writer
+ * and profile data writer.  */
+uint32_t llvmBufferWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs,
+                          void **WriterCtx);
+
+int llvmWriteProfData(WriterCallback Writer, void *WriterCtx,
+                      struct ValueProfData **ValueDataArray,
+                      const uint64_t ValueDataSize);
+int llvmWriteProfDataImpl(WriterCallback Writer, void *WriterCtx,
+                          const __llvm_profile_data *DataBegin,
+                          const __llvm_profile_data *DataEnd,
+                          const uint64_t *CountersBegin,
+                          const uint64_t *CountersEnd,
+                          struct ValueProfData **ValueDataBeginArray,
+                          const uint64_t ValueDataSize, const char *NamesBegin,
+                          const char *NamesEnd);
+
+extern char *(*GetEnvHook)(const char *);
+extern void (*FreeHook)(void *);
+extern void* (*CallocHook)(size_t, size_t);
+extern uint32_t VPBufferSize;
+
 #endif
index 02299cc4630c04c20f013284c87dd51deff79db1..30ddbd2e4982af2f2deaa503271e22ec6ab7a140 100644 (file)
 
 #if defined(__APPLE__)
 /* Use linker magic to find the bounds of the Data section. */
-__attribute__((visibility("hidden")))
-extern __llvm_profile_data DataStart __asm("section$start$__DATA$__llvm_prf_data");
-__attribute__((visibility("hidden")))
-extern __llvm_profile_data DataEnd   __asm("section$end$__DATA$__llvm_prf_data");
-__attribute__((visibility("hidden")))
-extern char NamesStart __asm("section$start$__DATA$__llvm_prf_names");
-__attribute__((visibility("hidden")))
-extern char NamesEnd   __asm("section$end$__DATA$__llvm_prf_names");
-__attribute__((visibility("hidden")))
-extern uint64_t CountersStart __asm("section$start$__DATA$__llvm_prf_cnts");
-__attribute__((visibility("hidden")))
-extern uint64_t CountersEnd   __asm("section$end$__DATA$__llvm_prf_cnts");
+COMPILER_RT_VISIBILITY
+extern __llvm_profile_data
+    DataStart __asm("section$start$__DATA$" INSTR_PROF_DATA_SECT_NAME_STR);
+COMPILER_RT_VISIBILITY
+extern __llvm_profile_data
+    DataEnd __asm("section$end$__DATA$" INSTR_PROF_DATA_SECT_NAME_STR);
+COMPILER_RT_VISIBILITY
+extern char
+    NamesStart __asm("section$start$__DATA$" INSTR_PROF_NAME_SECT_NAME_STR);
+COMPILER_RT_VISIBILITY
+extern char NamesEnd __asm("section$end$__DATA$" INSTR_PROF_NAME_SECT_NAME_STR);
+COMPILER_RT_VISIBILITY
+extern uint64_t
+    CountersStart __asm("section$start$__DATA$" INSTR_PROF_CNTS_SECT_NAME_STR);
+COMPILER_RT_VISIBILITY
+extern uint64_t
+    CountersEnd __asm("section$end$__DATA$" INSTR_PROF_CNTS_SECT_NAME_STR);
 
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 const __llvm_profile_data *__llvm_profile_begin_data(void) {
   return &DataStart;
 }
-__attribute__((visibility("hidden")))
-const __llvm_profile_data *__llvm_profile_end_data(void) {
-  return &DataEnd;
-}
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
+const __llvm_profile_data *__llvm_profile_end_data(void) { return &DataEnd; }
+COMPILER_RT_VISIBILITY
 const char *__llvm_profile_begin_names(void) { return &NamesStart; }
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 const char *__llvm_profile_end_names(void) { return &NamesEnd; }
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 uint64_t *__llvm_profile_begin_counters(void) { return &CountersStart; }
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 uint64_t *__llvm_profile_end_counters(void) { return &CountersEnd; }
 #endif
index 7a74e0e45fb2681b813ccaa92a3c678c8631c40b..7843f47caa1b483e5478cebbfae4e7bdf2f7ca8b 100644 (file)
 #if defined(__linux__) || defined(__FreeBSD__)
 #include <stdlib.h>
 
-extern __llvm_profile_data __start___llvm_prf_data
-    __attribute__((visibility("hidden")));
-extern __llvm_profile_data __stop___llvm_prf_data
-    __attribute__((visibility("hidden")));
-extern uint64_t __start___llvm_prf_cnts __attribute__((visibility("hidden")));
-extern uint64_t __stop___llvm_prf_cnts __attribute__((visibility("hidden")));
-extern char __start___llvm_prf_names __attribute__((visibility("hidden")));
-extern char __stop___llvm_prf_names __attribute__((visibility("hidden")));
+#define PROF_DATA_START INSTR_PROF_SECT_START(INSTR_PROF_DATA_SECT_NAME)
+#define PROF_DATA_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_DATA_SECT_NAME)
+#define PROF_NAME_START INSTR_PROF_SECT_START(INSTR_PROF_NAME_SECT_NAME)
+#define PROF_NAME_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_NAME_SECT_NAME)
+#define PROF_CNTS_START INSTR_PROF_SECT_START(INSTR_PROF_CNTS_SECT_NAME)
+#define PROF_CNTS_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_CNTS_SECT_NAME)
 
-__attribute__((visibility("hidden"))) const __llvm_profile_data *
+/* Declare section start and stop symbols for various sections
+ * generated by compiler instrumentation.
+ */
+extern __llvm_profile_data PROF_DATA_START COMPILER_RT_VISIBILITY;
+extern __llvm_profile_data PROF_DATA_STOP COMPILER_RT_VISIBILITY;
+extern uint64_t PROF_CNTS_START COMPILER_RT_VISIBILITY;
+extern uint64_t PROF_CNTS_STOP COMPILER_RT_VISIBILITY;
+extern char PROF_NAME_START COMPILER_RT_VISIBILITY;
+extern char PROF_NAME_STOP COMPILER_RT_VISIBILITY;
+
+/* Add dummy data to ensure the section is always created. */
+__llvm_profile_data
+    __prof_data_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_DATA_SECT_NAME_STR);
+uint64_t
+    __prof_cnts_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_CNTS_SECT_NAME_STR);
+char __prof_nms_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_NAME_SECT_NAME_STR);
+
+COMPILER_RT_VISIBILITY const __llvm_profile_data *
 __llvm_profile_begin_data(void) {
-  return &__start___llvm_prf_data;
+  return &PROF_DATA_START;
 }
-__attribute__((visibility("hidden"))) const __llvm_profile_data *
+COMPILER_RT_VISIBILITY const __llvm_profile_data *
 __llvm_profile_end_data(void) {
-  return &__stop___llvm_prf_data;
+  return &PROF_DATA_STOP;
 }
-__attribute__((visibility("hidden"))) const char *__llvm_profile_begin_names(
-    void) {
-  return &__start___llvm_prf_names;
+COMPILER_RT_VISIBILITY const char *__llvm_profile_begin_names(void) {
+  return &PROF_NAME_START;
 }
-__attribute__((visibility("hidden"))) const char *__llvm_profile_end_names(
-    void) {
-  return &__stop___llvm_prf_names;
+COMPILER_RT_VISIBILITY const char *__llvm_profile_end_names(void) {
+  return &PROF_NAME_STOP;
 }
-__attribute__((visibility("hidden"))) uint64_t *__llvm_profile_begin_counters(
-    void) {
-  return &__start___llvm_prf_cnts;
+COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_begin_counters(void) {
+  return &PROF_CNTS_START;
 }
-__attribute__((visibility("hidden"))) uint64_t *__llvm_profile_end_counters(
-    void) {
-  return &__stop___llvm_prf_cnts;
+COMPILER_RT_VISIBILITY uint64_t *__llvm_profile_end_counters(void) {
+  return &PROF_CNTS_STOP;
 }
 #endif
index 2ad058beb90b3365708bfaec9a5aca1a90762057..58ceb3458a0ad09d16ff1e1e57a5c97a62efc684 100644 (file)
@@ -26,49 +26,43 @@ static uint64_t *CountersLast = NULL;
  * calls are only required (and only emitted) on targets where we haven't
  * implemented linker magic to find the bounds of the sections.
  */
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 void __llvm_profile_register_function(void *Data_) {
   /* TODO: Only emit this function if we can't use linker magic. */
-  const __llvm_profile_data *Data = (__llvm_profile_data*)Data_;
+  const __llvm_profile_data *Data = (__llvm_profile_data *)Data_;
   if (!DataFirst) {
     DataFirst = Data;
     DataLast = Data + 1;
-    NamesFirst = Data->Name;
-    NamesLast = Data->Name + Data->NameSize;
-    CountersFirst = Data->Counters;
-    CountersLast = Data->Counters + Data->NumCounters;
+    NamesFirst = Data->NamePtr;
+    NamesLast = (const char *)Data->NamePtr + Data->NameSize;
+    CountersFirst = Data->CounterPtr;
+    CountersLast = (uint64_t *)Data->CounterPtr + Data->NumCounters;
     return;
   }
 
-#define UPDATE_FIRST(First, New) \
-  First = New < First ? New : First
+#define UPDATE_FIRST(First, New) First = New < First ? New : First
   UPDATE_FIRST(DataFirst, Data);
-  UPDATE_FIRST(NamesFirst, Data->Name);
-  UPDATE_FIRST(CountersFirst, Data->Counters);
+  UPDATE_FIRST(NamesFirst, (const char *)Data->NamePtr);
+  UPDATE_FIRST(CountersFirst, (uint64_t *)Data->CounterPtr);
 #undef UPDATE_FIRST
 
-#define UPDATE_LAST(Last, New) \
-  Last = New > Last ? New : Last
+#define UPDATE_LAST(Last, New) Last = New > Last ? New : Last
   UPDATE_LAST(DataLast, Data + 1);
-  UPDATE_LAST(NamesLast, Data->Name + Data->NameSize);
-  UPDATE_LAST(CountersLast, Data->Counters + Data->NumCounters);
+  UPDATE_LAST(NamesLast, (const char *)Data->NamePtr + Data->NameSize);
+  UPDATE_LAST(CountersLast, (uint64_t *)Data->CounterPtr + Data->NumCounters);
 #undef UPDATE_LAST
 }
 
-__attribute__((visibility("hidden")))
-const __llvm_profile_data *__llvm_profile_begin_data(void) {
-  return DataFirst;
-}
-__attribute__((visibility("hidden")))
-const __llvm_profile_data *__llvm_profile_end_data(void) {
-  return DataLast;
-}
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
+const __llvm_profile_data *__llvm_profile_begin_data(void) { return DataFirst; }
+COMPILER_RT_VISIBILITY
+const __llvm_profile_data *__llvm_profile_end_data(void) { return DataLast; }
+COMPILER_RT_VISIBILITY
 const char *__llvm_profile_begin_names(void) { return NamesFirst; }
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 const char *__llvm_profile_end_names(void) { return NamesLast; }
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 uint64_t *__llvm_profile_begin_counters(void) { return CountersFirst; }
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 uint64_t *__llvm_profile_end_counters(void) { return CountersLast; }
 #endif
diff --git a/src/compiler-rt/lib/profile/InstrProfilingPort.h b/src/compiler-rt/lib/profile/InstrProfilingPort.h
new file mode 100644 (file)
index 0000000..e07f598
--- /dev/null
@@ -0,0 +1,62 @@
+/*===- InstrProfilingPort.h- Support library for PGO instrumentation ------===*\
+|*
+|*                     The LLVM Compiler Infrastructure
+|*
+|* This file is distributed under the University of Illinois Open Source
+|* License. See LICENSE.TXT for details.
+|*
+\*===----------------------------------------------------------------------===*/
+
+#ifndef PROFILE_INSTRPROFILING_PORT_H_
+#define PROFILE_INSTRPROFILING_PORT_H_
+
+#ifdef _MSC_VER
+#define COMPILER_RT_ALIGNAS(x) __declspec(align(x))
+#define COMPILER_RT_VISIBILITY
+#define COMPILER_RT_WEAK __declspec(selectany)
+#elif __GNUC__
+#define COMPILER_RT_ALIGNAS(x) __attribute__((aligned(x)))
+#define COMPILER_RT_VISIBILITY __attribute__((visibility("hidden")))
+#define COMPILER_RT_WEAK __attribute__((weak))
+#endif
+
+#define COMPILER_RT_SECTION(Sect) __attribute__((section(Sect)))
+
+#if COMPILER_RT_HAS_ATOMICS == 1
+#ifdef _MSC_VER
+#include <windows.h>
+#if defined(_WIN64)
+#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV)                              \
+  (InterlockedCompareExchange64((LONGLONG volatile *)Ptr, (LONGLONG)NewV,      \
+                                (LONGLONG)OldV) == (LONGLONG)OldV)
+#else /* !defined(_WIN64) */
+#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV)                              \
+  (InterlockedCompareExchange((LONG volatile *)Ptr, (LONG)NewV, (LONG)OldV) == \
+   (LONG)OldV)
+#endif
+#else /* !defined(_MSC_VER) */
+#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV)                              \
+  __sync_bool_compare_and_swap(Ptr, OldV, NewV)
+#endif
+#else /* COMPILER_RT_HAS_ATOMICS != 1 */
+#define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV)                              \
+  BoolCmpXchg((void **)Ptr, OldV, NewV)
+#endif
+
+#define PROF_ERR(Format, ...)                                                  \
+  if (GetEnvHook && GetEnvHook("LLVM_PROFILE_VERBOSE_ERRORS"))                 \
+    fprintf(stderr, Format, __VA_ARGS__);
+
+#if defined(__FreeBSD__)
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#else /* defined(__FreeBSD__) */
+
+#include <inttypes.h>
+#include <stdint.h>
+
+#endif /* defined(__FreeBSD__) && defined(__i386__) */
+
+#endif /* PROFILE_INSTRPROFILING_PORT_H_ */
index 081ecb29e9874011622100484e49204ae229ea0b..12ad9f1573f4f4d99d8ce8dde465968c6441d0d4 100644 (file)
@@ -11,8 +11,7 @@ extern "C" {
 
 #include "InstrProfiling.h"
 
-__attribute__((visibility("hidden"))) int __llvm_profile_runtime;
-
+COMPILER_RT_VISIBILITY int __llvm_profile_runtime;
 }
 
 namespace {
index e146dfca83c864b246b23b8565ce063bc3f73964..6f0443d3bb5dfcf639657ea7ad49a014d5735da9 100644 (file)
@@ -8,6 +8,7 @@
 \*===----------------------------------------------------------------------===*/
 
 #include "InstrProfilingUtil.h"
+#include "InstrProfiling.h"
 
 #ifdef _WIN32
 #include <direct.h>
@@ -18,7 +19,7 @@ int mkdir(const char*, unsigned short);
 #include <sys/types.h>
 #endif
 
-__attribute__((visibility("hidden")))
+COMPILER_RT_VISIBILITY
 void __llvm_profile_recursive_mkdir(char *path) {
   int i;
 
diff --git a/src/compiler-rt/lib/profile/InstrProfilingValue.c b/src/compiler-rt/lib/profile/InstrProfilingValue.c
new file mode 100644 (file)
index 0000000..68e16cf
--- /dev/null
@@ -0,0 +1,180 @@
+/*===- InstrProfilingValue.c - Support library for PGO instrumentation ----===*\
+|*
+|*                     The LLVM Compiler Infrastructure
+|*
+|* This file is distributed under the University of Illinois Open Source
+|* License. See LICENSE.TXT for details.
+|*
+\*===----------------------------------------------------------------------===*/
+
+#include "InstrProfiling.h"
+#include "InstrProfilingInternal.h"
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define INSTR_PROF_VALUE_PROF_DATA
+#define INSTR_PROF_COMMON_API_IMPL
+#include "InstrProfData.inc"
+
+#define PROF_OOM(Msg) PROF_ERR(Msg ":%s\n", "Out of memory");
+#define PROF_OOM_RETURN(Msg)                                                   \
+  {                                                                            \
+    PROF_OOM(Msg)                                                              \
+    free(ValueDataArray);                                                      \
+    return NULL;                                                               \
+  }
+
+#if COMPILER_RT_HAS_ATOMICS != 1
+COMPILER_RT_VISIBILITY
+uint32_t BoolCmpXchg(void **Ptr, void *OldV, void *NewV) {
+  void *R = *Ptr;
+  if (R == OldV) {
+    *Ptr = NewV;
+    return 1;
+  }
+  return 0;
+}
+#endif
+
+/* This method is only used in value profiler mock testing.  */
+COMPILER_RT_VISIBILITY void
+__llvm_profile_set_num_value_sites(__llvm_profile_data *Data,
+                                   uint32_t ValueKind, uint16_t NumValueSites) {
+  *((uint16_t *)&Data->NumValueSites[ValueKind]) = NumValueSites;
+}
+
+/* This method is only used in value profiler mock testing.  */
+COMPILER_RT_VISIBILITY const __llvm_profile_data *
+__llvm_profile_iterate_data(const __llvm_profile_data *Data) {
+  return Data + 1;
+}
+
+/* This method is only used in value profiler mock testing.  */
+COMPILER_RT_VISIBILITY void *
+__llvm_get_function_addr(const __llvm_profile_data *Data) {
+  return Data->FunctionPointer;
+}
+
+/* Allocate an array that holds the pointers to the linked lists of
+ * value profile counter nodes. The number of element of the array
+ * is the total number of value profile sites instrumented. Returns
+ * 0 if allocation fails.
+ */
+
+static int allocateValueProfileCounters(__llvm_profile_data *Data) {
+  uint64_t NumVSites = 0;
+  uint32_t VKI;
+  for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI)
+    NumVSites += Data->NumValueSites[VKI];
+
+  ValueProfNode **Mem =
+      (ValueProfNode **)calloc(NumVSites, sizeof(ValueProfNode *));
+  if (!Mem)
+    return 0;
+  if (!COMPILER_RT_BOOL_CMPXCHG(&Data->Values, 0, Mem)) {
+    free(Mem);
+    return 0;
+  }
+  return 1;
+}
+
+COMPILER_RT_VISIBILITY void
+__llvm_profile_instrument_target(uint64_t TargetValue, void *Data,
+                                 uint32_t CounterIndex) {
+
+  __llvm_profile_data *PData = (__llvm_profile_data *)Data;
+  if (!PData)
+    return;
+
+  if (!PData->Values) {
+    if (!allocateValueProfileCounters(PData))
+      return;
+  }
+
+  ValueProfNode **ValueCounters = (ValueProfNode **)PData->Values;
+  ValueProfNode *PrevVNode = NULL;
+  ValueProfNode *CurrentVNode = ValueCounters[CounterIndex];
+
+  uint8_t VDataCount = 0;
+  while (CurrentVNode) {
+    if (TargetValue == CurrentVNode->VData.Value) {
+      CurrentVNode->VData.Count++;
+      return;
+    }
+    PrevVNode = CurrentVNode;
+    CurrentVNode = CurrentVNode->Next;
+    ++VDataCount;
+  }
+
+  if (VDataCount >= INSTR_PROF_MAX_NUM_VAL_PER_SITE)
+    return;
+
+  CurrentVNode = (ValueProfNode *)calloc(1, sizeof(ValueProfNode));
+  if (!CurrentVNode)
+    return;
+
+  CurrentVNode->VData.Value = TargetValue;
+  CurrentVNode->VData.Count++;
+
+  uint32_t Success = 0;
+  if (!ValueCounters[CounterIndex])
+    Success =
+        COMPILER_RT_BOOL_CMPXCHG(&ValueCounters[CounterIndex], 0, CurrentVNode);
+  else if (PrevVNode && !PrevVNode->Next)
+    Success = COMPILER_RT_BOOL_CMPXCHG(&(PrevVNode->Next), 0, CurrentVNode);
+
+  if (!Success) {
+    free(CurrentVNode);
+    return;
+  }
+}
+
+COMPILER_RT_VISIBILITY ValueProfData **
+__llvm_profile_gather_value_data(uint64_t *ValueDataSize) {
+  size_t S = 0;
+  __llvm_profile_data *I;
+  ValueProfData **ValueDataArray;
+
+  const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
+  const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
+
+  if (!ValueDataSize)
+    return NULL;
+
+  ValueDataArray =
+      (ValueProfData **)calloc(DataEnd - DataBegin, sizeof(void *));
+  if (!ValueDataArray)
+    PROF_OOM_RETURN("Failed to write value profile data ");
+
+  /*
+   * Compute the total Size of the buffer to hold ValueProfData
+   * structures for functions with value profile data.
+   */
+  for (I = (__llvm_profile_data *)DataBegin; I != DataEnd; ++I) {
+    ValueProfRuntimeRecord R;
+    if (initializeValueProfRuntimeRecord(&R, I->NumValueSites, I->Values))
+      PROF_OOM_RETURN("Failed to write value profile data ");
+
+    /* Compute the size of ValueProfData from this runtime record.  */
+    if (getNumValueKindsRT(&R) != 0) {
+      ValueProfData *VD = NULL;
+      uint32_t VS = getValueProfDataSizeRT(&R);
+      VD = (ValueProfData *)calloc(VS, sizeof(uint8_t));
+      if (!VD)
+        PROF_OOM_RETURN("Failed to write value profile data ");
+      serializeValueProfDataFromRT(&R, VD);
+      ValueDataArray[I - DataBegin] = VD;
+      S += VS;
+    }
+    finalizeValueProfRuntimeRecord(&R);
+  }
+
+  if (!S) {
+    free(ValueDataArray);
+    ValueDataArray = NULL;
+  }
+
+  *ValueDataSize = S;
+  return ValueDataArray;
+}
diff --git a/src/compiler-rt/lib/profile/InstrProfilingWriter.c b/src/compiler-rt/lib/profile/InstrProfilingWriter.c
new file mode 100644 (file)
index 0000000..a07bc53
--- /dev/null
@@ -0,0 +1,175 @@
+/*===- InstrProfilingWriter.c - Write instrumentation to a file or buffer -===*\
+|*
+|*                     The LLVM Compiler Infrastructure
+|*
+|* This file is distributed under the University of Illinois Open Source
+|* License. See LICENSE.TXT for details.
+|*
+\*===----------------------------------------------------------------------===*/
+
+#include "InstrProfiling.h"
+#include "InstrProfilingInternal.h"
+#include <string.h>
+
+#define INSTR_PROF_VALUE_PROF_DATA
+#include "InstrProfData.inc"
+void (*FreeHook)(void *) = NULL;
+void* (*CallocHook)(size_t, size_t) = NULL;
+uint32_t VPBufferSize = 0;
+
+/* The buffer writer is reponsponsible in keeping writer state
+ * across the call.
+ */
+COMPILER_RT_VISIBILITY uint32_t llvmBufferWriter(ProfDataIOVec *IOVecs,
+                                                 uint32_t NumIOVecs,
+                                                 void **WriterCtx) {
+  uint32_t I;
+  char **Buffer = (char **)WriterCtx;
+  for (I = 0; I < NumIOVecs; I++) {
+    size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm;
+    memcpy(*Buffer, IOVecs[I].Data, Length);
+    *Buffer += Length;
+  }
+  return 0;
+}
+
+static void llvmInitBufferIO(ProfBufferIO *BufferIO, WriterCallback FileWriter,
+                             void *File, uint8_t *Buffer, uint32_t BufferSz) {
+  BufferIO->File = File;
+  BufferIO->FileWriter = FileWriter;
+  BufferIO->BufferStart = Buffer;
+  BufferIO->BufferSz = BufferSz;
+  BufferIO->CurOffset = 0;
+}
+
+COMPILER_RT_VISIBILITY ProfBufferIO *
+llvmCreateBufferIO(WriterCallback FileWriter, void *File, uint32_t BufferSz) {
+  ProfBufferIO *BufferIO = (ProfBufferIO *)CallocHook(1, sizeof(ProfBufferIO));
+  uint8_t *Buffer = (uint8_t *)CallocHook(1, BufferSz);
+  if (!Buffer) {
+    FreeHook(BufferIO);
+    return 0;
+  }
+  llvmInitBufferIO(BufferIO, FileWriter, File, Buffer, BufferSz);
+  return BufferIO;
+}
+
+COMPILER_RT_VISIBILITY void llvmDeleteBufferIO(ProfBufferIO *BufferIO) {
+  FreeHook(BufferIO->BufferStart);
+  FreeHook(BufferIO);
+}
+
+COMPILER_RT_VISIBILITY int
+llvmBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data, uint32_t Size) {
+  /* Buffer is not large enough, it is time to flush.  */
+  if (Size + BufferIO->CurOffset > BufferIO->BufferSz) {
+     if (llvmBufferIOFlush(BufferIO) != 0)
+       return -1;
+  }
+  /* Special case, bypass the buffer completely. */
+  ProfDataIOVec IO[] = {{Data, sizeof(uint8_t), Size}};
+  if (Size > BufferIO->BufferSz) {
+    if (BufferIO->FileWriter(IO, 1, &BufferIO->File))
+      return -1;
+  } else {
+    /* Write the data to buffer */
+    uint8_t *Buffer = BufferIO->BufferStart + BufferIO->CurOffset;
+    llvmBufferWriter(IO, 1, (void **)&Buffer);
+    BufferIO->CurOffset = Buffer - BufferIO->BufferStart;
+  }
+  return 0;
+}
+
+COMPILER_RT_VISIBILITY int llvmBufferIOFlush(ProfBufferIO *BufferIO) {
+  if (BufferIO->CurOffset) {
+    ProfDataIOVec IO[] = {
+        {BufferIO->BufferStart, sizeof(uint8_t), BufferIO->CurOffset}};
+    if (BufferIO->FileWriter(IO, 1, &BufferIO->File))
+      return -1;
+    BufferIO->CurOffset = 0;
+  }
+  return 0;
+}
+
+COMPILER_RT_VISIBILITY int llvmWriteProfData(WriterCallback Writer,
+                                             void *WriterCtx,
+                                             ValueProfData **ValueDataArray,
+                                             const uint64_t ValueDataSize) {
+  /* Match logic in __llvm_profile_write_buffer(). */
+  const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
+  const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
+  const uint64_t *CountersBegin = __llvm_profile_begin_counters();
+  const uint64_t *CountersEnd = __llvm_profile_end_counters();
+  const char *NamesBegin = __llvm_profile_begin_names();
+  const char *NamesEnd = __llvm_profile_end_names();
+  return llvmWriteProfDataImpl(Writer, WriterCtx, DataBegin, DataEnd,
+                               CountersBegin, CountersEnd, ValueDataArray,
+                               ValueDataSize, NamesBegin, NamesEnd);
+}
+
+#define VP_BUFFER_SIZE 8 * 1024
+static int writeValueProfData(WriterCallback Writer, void *WriterCtx,
+                              ValueProfData **ValueDataBegin,
+                              uint64_t NumVData) {
+  ProfBufferIO *BufferIO;
+  uint32_t I = 0, BufferSz;
+
+  if (!ValueDataBegin)
+    return 0;
+
+  BufferSz = VPBufferSize ? VPBufferSize : VP_BUFFER_SIZE;
+  BufferIO = llvmCreateBufferIO(Writer, WriterCtx, BufferSz);
+
+  for (I = 0; I < NumVData; I++) {
+    ValueProfData *CurVData = ValueDataBegin[I];
+    if (!CurVData)
+      continue;
+    if (llvmBufferIOWrite(BufferIO, (const uint8_t *)CurVData,
+                          CurVData->TotalSize) != 0)
+      return -1;
+  }
+
+  if (llvmBufferIOFlush(BufferIO) != 0)
+    return -1;
+  llvmDeleteBufferIO(BufferIO);
+
+  return 0;
+}
+
+COMPILER_RT_VISIBILITY int llvmWriteProfDataImpl(
+    WriterCallback Writer, void *WriterCtx,
+    const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd,
+    const uint64_t *CountersBegin, const uint64_t *CountersEnd,
+    ValueProfData **ValueDataBegin, const uint64_t ValueDataSize,
+    const char *NamesBegin, const char *NamesEnd) {
+
+  /* Calculate size of sections. */
+  const uint64_t DataSize = DataEnd - DataBegin;
+  const uint64_t CountersSize = CountersEnd - CountersBegin;
+  const uint64_t NamesSize = NamesEnd - NamesBegin;
+  const uint64_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize);
+
+  /* Enough zeroes for padding. */
+  const char Zeroes[sizeof(uint64_t)] = {0};
+
+  /* Create the header. */
+  __llvm_profile_header Header;
+
+  if (!DataSize)
+    return 0;
+
+  /* Initialize header struture.  */
+#define INSTR_PROF_RAW_HEADER(Type, Name, Init) Header.Name = Init;
+#include "InstrProfData.inc"
+
+  /* Write the data. */
+  ProfDataIOVec IOVec[] = {{&Header, sizeof(__llvm_profile_header), 1},
+                           {DataBegin, sizeof(__llvm_profile_data), DataSize},
+                           {CountersBegin, sizeof(uint64_t), CountersSize},
+                           {NamesBegin, sizeof(uint8_t), NamesSize},
+                           {Zeroes, sizeof(uint8_t), Padding}};
+  if (Writer(IOVec, sizeof(IOVec) / sizeof(*IOVec), &WriterCtx))
+    return -1;
+
+  return writeValueProfData(Writer, WriterCtx, ValueDataBegin, DataSize);
+}
diff --git a/src/compiler-rt/lib/profile/WindowsMMap.c b/src/compiler-rt/lib/profile/WindowsMMap.c
new file mode 100644 (file)
index 0000000..1f73420
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * This code is derived from uClibc (original license follows).
+ * https://git.uclibc.org/uClibc/tree/utils/mmap-windows.c
+ */
+ /* mmap() replacement for Windows
+ *
+ * Author: Mike Frysinger <vapier@gentoo.org>
+ * Placed into the public domain
+ */
+
+/* References:
+ * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
+ * CloseHandle:       http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
+ * MapViewOfFile:     http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
+ * UnmapViewOfFile:   http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
+ */
+
+#if defined(_WIN32)
+
+#include "WindowsMMap.h"
+#include "InstrProfiling.h"
+
+#ifdef __USE_FILE_OFFSET64
+# define DWORD_HI(x) (x >> 32)
+# define DWORD_LO(x) ((x) & 0xffffffff)
+#else
+# define DWORD_HI(x) (0)
+# define DWORD_LO(x) (x)
+#endif
+
+COMPILER_RT_VISIBILITY
+void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
+{
+  if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
+    return MAP_FAILED;
+  if (fd == -1) {
+    if (!(flags & MAP_ANON) || offset)
+      return MAP_FAILED;
+  } else if (flags & MAP_ANON)
+    return MAP_FAILED;
+
+  DWORD flProtect;
+  if (prot & PROT_WRITE) {
+    if (prot & PROT_EXEC)
+      flProtect = PAGE_EXECUTE_READWRITE;
+    else
+      flProtect = PAGE_READWRITE;
+  } else if (prot & PROT_EXEC) {
+    if (prot & PROT_READ)
+      flProtect = PAGE_EXECUTE_READ;
+    else if (prot & PROT_EXEC)
+      flProtect = PAGE_EXECUTE;
+  } else
+    flProtect = PAGE_READONLY;
+
+  off_t end = length + offset;
+  HANDLE mmap_fd, h;
+  if (fd == -1)
+    mmap_fd = INVALID_HANDLE_VALUE;
+  else
+    mmap_fd = (HANDLE)_get_osfhandle(fd);
+  h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL);
+  if (h == NULL)
+    return MAP_FAILED;
+
+  DWORD dwDesiredAccess;
+  if (prot & PROT_WRITE)
+    dwDesiredAccess = FILE_MAP_WRITE;
+  else
+    dwDesiredAccess = FILE_MAP_READ;
+  if (prot & PROT_EXEC)
+    dwDesiredAccess |= FILE_MAP_EXECUTE;
+  if (flags & MAP_PRIVATE)
+    dwDesiredAccess |= FILE_MAP_COPY;
+  void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length);
+  if (ret == NULL) {
+    CloseHandle(h);
+    ret = MAP_FAILED;
+  }
+  return ret;
+}
+
+COMPILER_RT_VISIBILITY
+void munmap(void *addr, size_t length)
+{
+  UnmapViewOfFile(addr);
+  /* ruh-ro, we leaked handle from CreateFileMapping() ... */
+}
+
+COMPILER_RT_VISIBILITY
+int msync(void *addr, size_t length, int flags)
+{
+  if (flags & MS_INVALIDATE)
+    return -1; /* Not supported. */
+
+  /* Exactly one of MS_ASYNC or MS_SYNC must be specified. */
+  switch (flags & (MS_ASYNC | MS_SYNC)) {
+    case MS_SYNC:
+    case MS_ASYNC:
+      break;
+    default:
+      return -1;
+  }
+
+  if (!FlushViewOfFile(addr, length))
+    return -1;
+
+  if (flags & MS_SYNC) {
+    /* FIXME: No longer have access to handle from CreateFileMapping(). */
+    /*
+     * if (!FlushFileBuffers(h))
+     *   return -1;
+     */
+  }
+
+  return 0;
+}
+
+COMPILER_RT_VISIBILITY
+int flock(int fd, int operation)
+{
+  return -1; /* Not supported. */
+}
+
+#undef DWORD_HI
+#undef DWORD_LO
+
+#endif /* _WIN32 */
diff --git a/src/compiler-rt/lib/profile/WindowsMMap.h b/src/compiler-rt/lib/profile/WindowsMMap.h
new file mode 100644 (file)
index 0000000..7b94eb2
--- /dev/null
@@ -0,0 +1,65 @@
+/*===- WindowsMMap.h - Support library for PGO instrumentation ------------===*\
+|*
+|*                     The LLVM Compiler Infrastructure
+|*
+|* This file is distributed under the University of Illinois Open Source
+|* License. See LICENSE.TXT for details.
+|*
+\*===----------------------------------------------------------------------===*/
+
+#ifndef PROFILE_INSTRPROFILING_WINDOWS_MMAP_H
+#define PROFILE_INSTRPROFILING_WINDOWS_MMAP_H
+
+#if defined(_WIN32)
+
+#include <BaseTsd.h>
+#include <io.h>
+#include <sys/types.h>
+
+/*
+ * mmap() flags
+ */
+#define PROT_READ     0x1
+#define PROT_WRITE    0x2
+/* This flag is only available in WinXP+ */
+#ifdef FILE_MAP_EXECUTE
+#define PROT_EXEC     0x4
+#else
+#define PROT_EXEC        0x0
+#define FILE_MAP_EXECUTE 0
+#endif
+
+#define MAP_FILE      0x00
+#define MAP_SHARED    0x01
+#define MAP_PRIVATE   0x02
+#define MAP_ANONYMOUS 0x20
+#define MAP_ANON      MAP_ANONYMOUS
+#define MAP_FAILED    ((void *) -1)
+
+/*
+ * msync() flags
+ */
+#define MS_ASYNC        0x0001  /* return immediately */
+#define MS_INVALIDATE   0x0002  /* invalidate all cached data */
+#define MS_SYNC         0x0010  /* msync synchronously */
+
+/*
+ * flock() operations
+ */
+#define   LOCK_SH   1    /* shared lock */
+#define   LOCK_EX   2    /* exclusive lock */
+#define   LOCK_NB   4    /* don't block when locking */
+#define   LOCK_UN   8    /* unlock */
+
+void *mmap(void *start, size_t length, int prot, int flags, int fd,
+           off_t offset);
+
+void munmap(void *addr, size_t length);
+
+int msync(void *addr, size_t length, int flags);
+
+int flock(int fd, int operation);
+
+#endif /* _WIN32 */
+
+#endif /* PROFILE_INSTRPROFILING_WINDOWS_MMAP_H */
diff --git a/src/compiler-rt/lib/safestack/.clang-format b/src/compiler-rt/lib/safestack/.clang-format
new file mode 100644 (file)
index 0000000..f6cb8ad
--- /dev/null
@@ -0,0 +1 @@
+BasedOnStyle: Google
index 5254462d92ce41d64686525a4ee2746c3da59c0b..92c24b35d6d0c95702fd1e98f502a16ecf2bd1a0 100644 (file)
@@ -18,6 +18,7 @@
 #include <pthread.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <unistd.h>
 #include <sys/resource.h>
 #include <sys/types.h>
 #include <sys/user.h>
@@ -68,6 +69,9 @@ const unsigned kStackAlign = 16;
 /// size rlimit is set to infinity.
 const unsigned kDefaultUnsafeStackSize = 0x2800000;
 
+/// Runtime page size obtained through sysconf
+static unsigned pageSize;
+
 // TODO: To make accessing the unsafe stack pointer faster, we plan to
 // eventually store it directly in the thread control block data structure on
 // platforms where this structure is pointed to by %fs or %gs. This is exactly
@@ -185,7 +189,7 @@ INTERCEPTOR(int, pthread_create, pthread_t *thread,
 
   CHECK_NE(size, 0);
   CHECK_EQ((size & (kStackAlign - 1)), 0);
-  CHECK_EQ((guard & (PAGE_SIZE - 1)), 0);
+  CHECK_EQ((guard & (pageSize - 1)), 0);
 
   void *addr = unsafe_stack_alloc(size, guard);
   struct tinfo *tinfo =
@@ -217,6 +221,7 @@ void __safestack_init() {
   void *addr = unsafe_stack_alloc(size, guard);
 
   unsafe_stack_setup(addr, size, guard);
+  pageSize = sysconf(_SC_PAGESIZE);
 
   // Initialize pthread interceptors for thread allocation
   INTERCEPT_FUNCTION(pthread_create);
diff --git a/src/compiler-rt/lib/sanitizer_common/.clang-format b/src/compiler-rt/lib/sanitizer_common/.clang-format
new file mode 100644 (file)
index 0000000..f6cb8ad
--- /dev/null
@@ -0,0 +1 @@
+BasedOnStyle: Google
index 906012a96f11f1939220fa0cfae3f93fac049b51..47c2b12a204916f387c77e49be4189a05b8c9903 100644 (file)
 # define CFI_STARTPROC .cfi_startproc
 # define CFI_ENDPROC .cfi_endproc
 # define CFI_ADJUST_CFA_OFFSET(n) .cfi_adjust_cfa_offset n
+# define CFI_DEF_CFA_OFFSET(n) .cfi_def_cfa_offset n
 # define CFI_REL_OFFSET(reg, n) .cfi_rel_offset reg, n
+# define CFI_OFFSET(reg, n) .cfi_offset reg, n
 # define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg
+# define CFI_DEF_CFA(reg, n) .cfi_def_cfa reg, n
 # define CFI_RESTORE(reg) .cfi_restore reg
 
 #else  // No CFI
 # define CFI_STARTPROC
 # define CFI_ENDPROC
 # define CFI_ADJUST_CFA_OFFSET(n)
+# define CFI_DEF_CFA_OFFSET(n)
 # define CFI_REL_OFFSET(reg, n)
+# define CFI_OFFSET(reg, n)
 # define CFI_DEF_CFA_REGISTER(reg)
+# define CFI_DEF_CFA(reg, n)
 # define CFI_RESTORE(reg)
 #endif
 
-
+#if !defined(__APPLE__)
+# define ASM_HIDDEN(symbol) .hidden symbol
+# define ASM_TYPE_FUNCTION(symbol) .type symbol, @function
+# define ASM_SIZE(symbol) .size symbol, .-symbol
+# define ASM_TSAN_SYMBOL(symbol) symbol
+# define ASM_TSAN_SYMBOL_INTERCEPTOR(symbol) symbol
+#else
+# define ASM_HIDDEN(symbol)
+# define ASM_TYPE_FUNCTION(symbol)
+# define ASM_SIZE(symbol)
+# define ASM_TSAN_SYMBOL(symbol) _##symbol
+# define ASM_TSAN_SYMBOL_INTERCEPTOR(symbol) _wrap_##symbol
+#endif
index b40a457c0b3489262addff18151e8395eb3cc798..9b41a3aa0af9fe26213ce5618f5b95f5e2bb1d92 100644 (file)
@@ -164,11 +164,12 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
 }
 
 void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
-                                      const char *mmap_type, error_t err) {
+                                      const char *mmap_type, error_t err,
+                                      bool raw_report) {
   static int recursion_count;
-  if (recursion_count) {
+  if (raw_report || recursion_count) {
+    // If raw report is requested or we went into recursion, just die.
     // The Report() and CHECK calls below may call mmap recursively and fail.
-    // If we went into recursion, just die.
     RawWrite("ERROR: Failed to mmap\n");
     Die();
   }
@@ -176,7 +177,9 @@ void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
   Report("ERROR: %s failed to "
          "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
          SanitizerToolName, mmap_type, size, size, mem_type, err);
+#ifndef SANITIZER_GO
   DumpProcessMap();
+#endif
   UNREACHABLE("unable to mmap");
 }
 
@@ -295,6 +298,40 @@ void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
 }
 #endif
 
+// Removes the ANSI escape sequences from the input string (in-place).
+void RemoveANSIEscapeSequencesFromString(char *str) {
+  if (!str)
+    return;
+
+  // We are going to remove the escape sequences in place.
+  char *s = str;
+  char *z = str;
+  while (*s != '\0') {
+    CHECK_GE(s, z);
+    // Skip over ANSI escape sequences with pointer 's'.
+    if (*s == '\033' && *(s + 1) == '[') {
+      s = internal_strchrnul(s, 'm');
+      if (*s == '\0') {
+        break;
+      }
+      s++;
+      continue;
+    }
+    // 's' now points at a character we want to keep. Copy over the buffer
+    // content if the escape sequence has been perviously skipped andadvance
+    // both pointers.
+    if (s != z)
+      *z = *s;
+
+    // If we have not seen an escape sequence, just advance both pointers.
+    z++;
+    s++;
+  }
+
+  // Null terminate the string.
+  *z = '\0';
+}
+
 void LoadedModule::set(const char *module_name, uptr base_address) {
   clear();
   full_name_ = internal_strdup(module_name);
index 43cd2d0e8c3d0ad1b72a1f1a9f7aca8fc3decfd3..7e80507ba0cf100333cf03c626e6ce1c48af21d3 100644 (file)
@@ -49,6 +49,8 @@ static const uptr kMaxNumberOfModules = 1 << 14;
 
 const uptr kMaxThreadStackSize = 1 << 30;  // 1Gb
 
+static const uptr kErrorMessageBufferSize = 1 << 16;
+
 // Denotes fake PC values that come from JIT/JAVA/etc.
 // For such PC values __tsan_symbolize_external() will be called.
 const u64 kExternalPCBit = 1ULL << 60;
@@ -76,7 +78,10 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
                           uptr *tls_addr, uptr *tls_size);
 
 // Memory management
-void *MmapOrDie(uptr size, const char *mem_type);
+void *MmapOrDie(uptr size, const char *mem_type, bool raw_report = false);
+INLINE void *MmapOrDieQuietly(uptr size, const char *mem_type) {
+  return MmapOrDie(size, mem_type, /*raw_report*/ true);
+}
 void UnmapOrDie(void *addr, uptr size);
 void *MmapFixedNoReserve(uptr fixed_addr, uptr size,
                          const char *name = nullptr);
@@ -162,6 +167,7 @@ void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback);
 // IO
 void RawWrite(const char *buffer);
 bool ColorizeReports();
+void RemoveANSIEscapeSequencesFromString(char *buffer);
 void Printf(const char *format, ...);
 void Report(const char *format, ...);
 void SetPrintfAndReportCallback(void (*callback)(const char *));
@@ -309,7 +315,8 @@ void NORETURN Die();
 void NORETURN
 CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2);
 void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
-                                      const char *mmap_type, error_t err);
+                                      const char *mmap_type, error_t err,
+                                      bool raw_report = false);
 
 // Set the name of the current thread to 'name', return true on succees.
 // The name may be truncated to a system-dependent limit.
@@ -422,7 +429,7 @@ INLINE uptr RoundUpToPowerOfTwo(uptr size) {
 }
 
 INLINE uptr RoundUpTo(uptr size, uptr boundary) {
-  CHECK(IsPowerOfTwo(boundary));
+  RAW_CHECK(IsPowerOfTwo(boundary));
   return (size + boundary - 1) & ~(boundary - 1);
 }
 
@@ -648,22 +655,34 @@ enum AndroidApiLevel {
   ANDROID_POST_LOLLIPOP = 23
 };
 
+void WriteToSyslog(const char *buffer);
+
+#if SANITIZER_MAC
+void LogFullErrorReport(const char *buffer);
+#else
+INLINE void LogFullErrorReport(const char *buffer) {}
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_MAC
+void WriteOneLineToSyslog(const char *s);
+void LogMessageOnPrintf(const char *str);
+#else
+INLINE void WriteOneLineToSyslog(const char *s) {}
+INLINE void LogMessageOnPrintf(const char *str) {}
+#endif
+
 #if SANITIZER_LINUX
 // Initialize Android logging. Any writes before this are silently lost.
 void AndroidLogInit();
-void WriteToSyslog(const char *buffer);
 #else
 INLINE void AndroidLogInit() {}
-INLINE void WriteToSyslog(const char *buffer) {}
 #endif
 
 #if SANITIZER_ANDROID
-void GetExtraActivationFlags(char *buf, uptr size);
 void SanitizerInitializeUnwinder();
 AndroidApiLevel AndroidGetApiLevel();
 #else
 INLINE void AndroidLogWrite(const char *buffer_unused) {}
-INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
 INLINE void SanitizerInitializeUnwinder() {}
 INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; }
 #endif
@@ -712,6 +731,9 @@ struct SignalContext {
 
 void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
 
+void DisableReexec();
+void MaybeReexec();
+
 }  // namespace __sanitizer
 
 inline void *operator new(__sanitizer::operator_new_size_type size,
index 62902a306c5b674be54731629fc798f8100efdb2..2a748cdc68524bd470ca2d3aff181191092ba882 100644 (file)
@@ -214,13 +214,11 @@ static inline int CharCmpX(unsigned char c1, unsigned char c2) {
 }
 
 DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, uptr called_pc,
-                              const char *s1, const char *s2)
+                              const char *s1, const char *s2, int result)
 
 INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
   void *ctx;
   COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2);
-  CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, GET_CALLER_PC(), s1,
-                             s2);
   unsigned char c1, c2;
   uptr i;
   for (i = 0;; i++) {
@@ -230,19 +228,21 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
   }
   COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
   COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
-  return CharCmpX(c1, c2);
+  int result = CharCmpX(c1, c2);
+  CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, GET_CALLER_PC(), s1,
+                             s2, result);
+  return result;
 }
 
 DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, uptr called_pc,
-                              const char *s1, const char *s2, uptr n)
+                              const char *s1, const char *s2, uptr n,
+                              int result)
 
 INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
   if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
     return internal_strncmp(s1, s2, size);
   void *ctx;
   COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size);
-  CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, GET_CALLER_PC(), s1,
-                             s2, size);
   unsigned char c1 = 0, c2 = 0;
   uptr i;
   for (i = 0; i < size; i++) {
@@ -252,7 +252,10 @@ INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
   }
   COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size));
   COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size));
-  return CharCmpX(c1, c2);
+  int result = CharCmpX(c1, c2);
+  CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, GET_CALLER_PC(), s1,
+                             s2, size, result);
+  return result;
 }
 
 #define INIT_STRCMP COMMON_INTERCEPT_FUNCTION(strcmp)
@@ -400,15 +403,14 @@ INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) {
 #if SANITIZER_INTERCEPT_MEMCMP
 
 DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, uptr called_pc,
-                              const void *s1, const void *s2, uptr n)
+                              const void *s1, const void *s2, uptr n,
+                              int result)
 
 INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
-  void *ctx;
-  COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size);
   if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
     return internal_memcmp(a1, a2, size);
-  CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1,
-                             a2, size);
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size);
   if (common_flags()->intercept_memcmp) {
     if (common_flags()->strict_memcmp) {
       // Check the entire regions even if the first bytes of the buffers are
@@ -428,10 +430,16 @@ INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
       }
       COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size));
       COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size));
-      return CharCmpX(c1, c2);
+      int r = CharCmpX(c1, c2);
+      CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(),
+                                 a1, a2, size, r);
+      return r;
     }
   }
-  return REAL(memcmp(a1, a2, size));
+  int result = REAL(memcmp(a1, a2, size));
+  CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1,
+                             a2, size, result);
+  return result;
 }
 
 #define INIT_MEMCMP COMMON_INTERCEPT_FUNCTION(memcmp)
@@ -490,7 +498,7 @@ INTERCEPTOR(float, frexpf, float x, int *exp) {
   COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   float res = REAL(frexpf)(x, exp);
   COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
   return res;
@@ -501,7 +509,7 @@ INTERCEPTOR(long double, frexpl, long double x, int *exp) {
   COMMON_INTERCEPTOR_ENTER(ctx, frexpl, x, exp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   long double res = REAL(frexpl)(x, exp);
   COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
   return res;
@@ -542,7 +550,7 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
   COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(read)(fd, ptr, count);
   if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
   if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
@@ -560,7 +568,7 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
   COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(pread)(fd, ptr, count, offset);
   if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
   if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
@@ -578,7 +586,7 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
   COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(pread64)(fd, ptr, count, offset);
   if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
   if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
@@ -825,7 +833,7 @@ INTERCEPTOR(char *, ctime, unsigned long *timep) {
   COMMON_INTERCEPTOR_ENTER(ctx, ctime, timep);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(ctime)(timep);
   if (res) {
     COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
@@ -838,7 +846,7 @@ INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) {
   COMMON_INTERCEPTOR_ENTER(ctx, ctime_r, timep, result);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(ctime_r)(timep, result);
   if (res) {
     COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
@@ -851,7 +859,7 @@ INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) {
   COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(asctime)(tm);
   if (res) {
     COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm));
@@ -864,7 +872,7 @@ INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) {
   COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(asctime_r)(tm, result);
   if (res) {
     COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm));
@@ -908,7 +916,7 @@ INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) {
     COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(strptime)(s, format, tm);
   COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0);
   if (res && tm) {
@@ -1045,7 +1053,7 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
 
 // FIXME: under ASan the REAL() call below may write to freed memory and
 // corrupt its metadata. See
-// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+// https://github.com/google/sanitizers/issues/321.
 #define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...)                             \
   {                                                                            \
     VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__)                         \
@@ -1062,7 +1070,7 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
 
 // FIXME: under ASan the REAL() call below may write to freed memory and
 // corrupt its metadata. See
-// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+// https://github.com/google/sanitizers/issues/321.
 #define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...)                      \
   {                                                                            \
     VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__)                   \
@@ -1079,7 +1087,7 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
 
 // FIXME: under ASan the REAL() call below may write to freed memory and
 // corrupt its metadata. See
-// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+// https://github.com/google/sanitizers/issues/321.
 #define VASPRINTF_INTERCEPTOR_IMPL(vname, strp, ...)                           \
   {                                                                            \
     VPRINTF_INTERCEPTOR_ENTER(vname, strp, __VA_ARGS__)                        \
@@ -1364,7 +1372,7 @@ INTERCEPTOR(int, getpwnam_r, const char *name, __sanitizer_passwd *pwd,
   COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result);
   if (!res) {
     if (result && *result) unpoison_passwd(ctx, *result);
@@ -1379,7 +1387,7 @@ INTERCEPTOR(int, getpwuid_r, u32 uid, __sanitizer_passwd *pwd, char *buf,
   COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result);
   if (!res) {
     if (result && *result) unpoison_passwd(ctx, *result);
@@ -1395,7 +1403,7 @@ INTERCEPTOR(int, getgrnam_r, const char *name, __sanitizer_group *grp,
   COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getgrnam_r)(name, grp, buf, buflen, result);
   if (!res) {
     if (result && *result) unpoison_group(ctx, *result);
@@ -1410,7 +1418,7 @@ INTERCEPTOR(int, getgrgid_r, u32 gid, __sanitizer_group *grp, char *buf,
   COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result);
   if (!res) {
     if (result && *result) unpoison_group(ctx, *result);
@@ -1479,7 +1487,7 @@ INTERCEPTOR(int, getpwent_r, __sanitizer_passwd *pwbuf, char *buf,
   COMMON_INTERCEPTOR_ENTER(ctx, getpwent_r, pwbuf, buf, buflen, pwbufp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getpwent_r)(pwbuf, buf, buflen, pwbufp);
   if (!res) {
     if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
@@ -1494,7 +1502,7 @@ INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf,
   COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp);
   if (!res) {
     if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
@@ -1509,7 +1517,7 @@ INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen,
   COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp);
   if (!res) {
     if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
@@ -1524,7 +1532,7 @@ INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf,
   COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent_r, fp, pwbuf, buf, buflen, pwbufp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(fgetgrent_r)(fp, pwbuf, buf, buflen, pwbufp);
   if (!res) {
     if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
@@ -1581,7 +1589,7 @@ INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) {
   COMMON_INTERCEPTOR_ENTER(ctx, clock_getres, clk_id, tp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(clock_getres)(clk_id, tp);
   if (!res && tp) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz);
@@ -1593,7 +1601,7 @@ INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) {
   COMMON_INTERCEPTOR_ENTER(ctx, clock_gettime, clk_id, tp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(clock_gettime)(clk_id, tp);
   if (!res) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz);
@@ -1620,7 +1628,7 @@ INTERCEPTOR(int, getitimer, int which, void *curr_value) {
   COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getitimer)(which, curr_value);
   if (!res && curr_value) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz);
@@ -1634,7 +1642,7 @@ INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) {
     COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(setitimer)(which, new_value, old_value);
   if (!res && old_value) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz);
@@ -1691,16 +1699,19 @@ static int wrapped_gl_stat(const char *s, void *st) {
   return pglob_copy->gl_stat(s, st);
 }
 
+static const __sanitizer_glob_t kGlobCopy = {
+      0,                  0,                   0,
+      0,                  wrapped_gl_closedir, wrapped_gl_readdir,
+      wrapped_gl_opendir, wrapped_gl_lstat,    wrapped_gl_stat};
+
 INTERCEPTOR(int, glob, const char *pattern, int flags,
             int (*errfunc)(const char *epath, int eerrno),
             __sanitizer_glob_t *pglob) {
   void *ctx;
   COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob);
   COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
-  __sanitizer_glob_t glob_copy = {
-      0,                  0,                   0,
-      0,                  wrapped_gl_closedir, wrapped_gl_readdir,
-      wrapped_gl_opendir, wrapped_gl_lstat,    wrapped_gl_stat};
+  __sanitizer_glob_t glob_copy;
+  internal_memcpy(&glob_copy, &kGlobCopy, sizeof(glob_copy));
   if (flags & glob_altdirfunc) {
     Swap(pglob->gl_closedir, glob_copy.gl_closedir);
     Swap(pglob->gl_readdir, glob_copy.gl_readdir);
@@ -1728,10 +1739,8 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags,
   void *ctx;
   COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob);
   COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
-  __sanitizer_glob_t glob_copy = {
-      0,                  0,                   0,
-      0,                  wrapped_gl_closedir, wrapped_gl_readdir,
-      wrapped_gl_opendir, wrapped_gl_lstat,    wrapped_gl_stat};
+  __sanitizer_glob_t glob_copy;
+  internal_memcpy(&glob_copy, &kGlobCopy, sizeof(glob_copy));
   if (flags & glob_altdirfunc) {
     Swap(pglob->gl_closedir, glob_copy.gl_closedir);
     Swap(pglob->gl_readdir, glob_copy.gl_readdir);
@@ -1768,7 +1777,7 @@ INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) {
   COMMON_INTERCEPTOR_ENTER(ctx, wait, status);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(wait)(status);
   if (res != -1 && status)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
@@ -1786,7 +1795,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop,
   COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(waitid)(idtype, id, infop, options);
   if (res != -1 && infop)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz);
@@ -1797,7 +1806,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) {
   COMMON_INTERCEPTOR_ENTER(ctx, waitpid, pid, status, options);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(waitpid)(pid, status, options);
   if (res != -1 && status)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
@@ -1808,7 +1817,7 @@ INTERCEPTOR(int, wait3, int *status, int options, void *rusage) {
   COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(wait3)(status, options, rusage);
   if (res != -1) {
     if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
@@ -1822,7 +1831,7 @@ INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) {
   COMMON_INTERCEPTOR_ENTER(ctx, __wait4, pid, status, options, rusage);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(__wait4)(pid, status, options, rusage);
   if (res != -1) {
     if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
@@ -1837,7 +1846,7 @@ INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) {
   COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(wait4)(pid, status, options, rusage);
   if (res != -1) {
     if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
@@ -1866,7 +1875,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) {
   // FIXME: figure out read size based on the address family.
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(inet_ntop)(af, src, dst, size);
   if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
   return res;
@@ -1878,7 +1887,7 @@ INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) {
   // FIXME: figure out read size based on the address family.
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(inet_pton)(af, src, dst);
   if (res == 1) {
     uptr sz = __sanitizer_in_addr_sz(af);
@@ -1900,7 +1909,7 @@ INTERCEPTOR(int, inet_aton, const char *cp, void *dst) {
   if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(inet_aton)(cp, dst);
   if (res != 0) {
     uptr sz = __sanitizer_in_addr_sz(af_inet);
@@ -1919,7 +1928,7 @@ INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) {
   COMMON_INTERCEPTOR_ENTER(ctx, pthread_getschedparam, thread, policy, param);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(pthread_getschedparam)(thread, policy, param);
   if (res == 0) {
     if (policy) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, policy, sizeof(*policy));
@@ -1946,7 +1955,7 @@ INTERCEPTOR(int, getaddrinfo, char *node, char *service,
     COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo));
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getaddrinfo)(node, service, hints, out);
   if (res == 0 && out) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out));
@@ -1978,7 +1987,7 @@ INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host,
   // There is padding in in_addr that may make this too noisy
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res =
       REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags);
   if (res == 0) {
@@ -2002,7 +2011,7 @@ INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) {
   int addrlen_in = *addrlen;
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getsockname)(sock_fd, addr, addrlen);
   if (res == 0) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen));
@@ -2088,7 +2097,7 @@ INTERCEPTOR(int, gethostbyname_r, char *name, struct __sanitizer_hostent *ret,
                            h_errnop);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(gethostbyname_r)(name, ret, buf, buflen, result, h_errnop);
   if (result) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
@@ -2111,7 +2120,7 @@ INTERCEPTOR(int, gethostent_r, struct __sanitizer_hostent *ret, char *buf,
                            h_errnop);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(gethostent_r)(ret, buf, buflen, result, h_errnop);
   if (result) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
@@ -2137,7 +2146,7 @@ INTERCEPTOR(int, gethostbyaddr_r, void *addr, int len, int type,
   COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(gethostbyaddr_r)(addr, len, type, ret, buf, buflen, result,
                                   h_errnop);
   if (result) {
@@ -2163,7 +2172,7 @@ INTERCEPTOR(int, gethostbyname2_r, char *name, int af,
                            result, h_errnop);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res =
       REAL(gethostbyname2_r)(name, af, ret, buf, buflen, result, h_errnop);
   if (result) {
@@ -2189,7 +2198,7 @@ INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval,
   if (optlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, optlen, sizeof(*optlen));
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getsockopt)(sockfd, level, optname, optval, optlen);
   if (res == 0)
     if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen);
@@ -2233,7 +2242,7 @@ INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) {
   }
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int fd2 = REAL(accept4)(fd, addr, addrlen, f);
   if (fd2 >= 0) {
     if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
@@ -2253,7 +2262,7 @@ INTERCEPTOR(double, modf, double x, double *iptr) {
   COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   double res = REAL(modf)(x, iptr);
   if (iptr) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr));
@@ -2265,7 +2274,7 @@ INTERCEPTOR(float, modff, float x, float *iptr) {
   COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   float res = REAL(modff)(x, iptr);
   if (iptr) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr));
@@ -2277,7 +2286,7 @@ INTERCEPTOR(long double, modfl, long double x, long double *iptr) {
   COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   long double res = REAL(modfl)(x, iptr);
   if (iptr) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr));
@@ -2312,7 +2321,7 @@ INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg,
   COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(recvmsg)(fd, msg, flags);
   if (res >= 0) {
     if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
@@ -2336,7 +2345,7 @@ INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) {
   if (addrlen) addr_sz = *addrlen;
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getpeername)(sockfd, addr, addrlen);
   if (!res && addr && addrlen)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen));
@@ -2352,7 +2361,7 @@ INTERCEPTOR(int, sysinfo, void *info) {
   void *ctx;
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info);
   int res = REAL(sysinfo)(info);
   if (!res && info)
@@ -2380,7 +2389,7 @@ INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) {
   COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   __sanitizer_dirent *res = REAL(readdir)(dirp);
   if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen);
   return res;
@@ -2392,7 +2401,7 @@ INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry,
   COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(readdir_r)(dirp, entry, result);
   if (!res) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
@@ -2416,7 +2425,7 @@ INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) {
   COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   __sanitizer_dirent64 *res = REAL(readdir64)(dirp);
   if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen);
   return res;
@@ -2428,7 +2437,7 @@ INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry,
   COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(readdir64_r)(dirp, entry, result);
   if (!res) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
@@ -2475,7 +2484,7 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
 
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   uptr res = REAL(ptrace)(request, pid, addr, data);
 
   if (!res && data) {
@@ -2530,7 +2539,7 @@ INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) {
   COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(getcwd)(buf, size);
   if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
   return res;
@@ -2546,7 +2555,7 @@ INTERCEPTOR(char *, get_current_dir_name, int fake) {
   COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(get_current_dir_name)(fake);
   if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
   return res;
@@ -2595,7 +2604,7 @@ INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
   COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *real_endptr;
   INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base);
   StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
@@ -2607,7 +2616,7 @@ INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
   COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *real_endptr;
   INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
   StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
@@ -2627,7 +2636,7 @@ INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) {
   COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SIZE_T res = REAL(mbstowcs)(dest, src, len);
   if (res != (SIZE_T) - 1 && dest) {
     SIZE_T write_cnt = res + (res < len);
@@ -2644,7 +2653,7 @@ INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len,
   if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps);
   if (res != (SIZE_T)(-1) && dest && src) {
     // This function, and several others, may or may not write the terminating
@@ -2674,7 +2683,7 @@ INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms,
   if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps);
   if (res != (SIZE_T)(-1) && dest && src) {
     SIZE_T write_cnt = res + !*src;
@@ -2694,7 +2703,7 @@ INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) {
   COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SIZE_T res = REAL(wcstombs)(dest, src, len);
   if (res != (SIZE_T) - 1 && dest) {
     SIZE_T write_cnt = res + (res < len);
@@ -2711,7 +2720,7 @@ INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len,
   if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps);
   if (res != (SIZE_T) - 1 && dest && src) {
     SIZE_T write_cnt = res + !*src;
@@ -2739,7 +2748,7 @@ INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms,
   if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps);
   if (res != ((SIZE_T)-1) && dest && src) {
     SIZE_T write_cnt = res + !*src;
@@ -2761,7 +2770,7 @@ INTERCEPTOR(SIZE_T, wcrtomb, char *dest, wchar_t src, void *ps) {
   if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SIZE_T res = REAL(wcrtomb)(dest, src, ps);
   if (res != ((SIZE_T)-1) && dest) {
     SIZE_T write_cnt = res;
@@ -2781,7 +2790,7 @@ INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) {
   COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(tcgetattr)(fd, termios_p);
   if (!res && termios_p)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz);
@@ -2838,7 +2847,7 @@ INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) {
   COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SIZE_T res = REAL(confstr)(name, buf, len);
   if (buf && res)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len);
@@ -2855,7 +2864,7 @@ INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) {
   COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(sched_getaffinity)(pid, cpusetsize, mask);
   if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize);
   return res;
@@ -2897,7 +2906,7 @@ INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) {
   COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(strerror_r)(errnum, buf, buflen);
   // There are 2 versions of strerror_r:
   //  * POSIX version returns 0 on success, negative error code on failure,
@@ -2928,7 +2937,7 @@ INTERCEPTOR(int, __xpg_strerror_r, int errnum, char *buf, SIZE_T buflen) {
   COMMON_INTERCEPTOR_ENTER(ctx, __xpg_strerror_r, errnum, buf, buflen);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(__xpg_strerror_r)(errnum, buf, buflen);
   // This version always returns a null-terminated string.
   if (buf && buflen)
@@ -2973,7 +2982,7 @@ INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist,
   scandir_compar = compar;
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(scandir)(dirp, namelist,
                           filter ? wrapped_scandir_filter : nullptr,
                           compar ? wrapped_scandir_compar : nullptr);
@@ -3026,7 +3035,7 @@ INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist,
   scandir64_compar = compar;
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res =
       REAL(scandir64)(dirp, namelist,
                       filter ? wrapped_scandir64_filter : nullptr,
@@ -3053,7 +3062,7 @@ INTERCEPTOR(int, getgroups, int size, u32 *lst) {
   COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getgroups)(size, lst);
   if (res && lst) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst));
   return res;
@@ -3119,7 +3128,7 @@ INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) {
   if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(wordexp)(s, p, flags);
   if (!res && p) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
@@ -3145,7 +3154,7 @@ INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) {
   // FIXME: read sigset_t when all of sigemptyset, etc are intercepted
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(sigwait)(set, sig);
   if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig));
   return res;
@@ -3162,7 +3171,7 @@ INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) {
   // FIXME: read sigset_t when all of sigemptyset, etc are intercepted
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(sigwaitinfo)(set, info);
   if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz);
   return res;
@@ -3181,7 +3190,7 @@ INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info,
   // FIXME: read sigset_t when all of sigemptyset, etc are intercepted
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(sigtimedwait)(set, info, timeout);
   if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz);
   return res;
@@ -3197,7 +3206,7 @@ INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) {
   COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(sigemptyset)(set);
   if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set));
   return res;
@@ -3208,7 +3217,7 @@ INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) {
   COMMON_INTERCEPTOR_ENTER(ctx, sigfillset, set);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(sigfillset)(set);
   if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set));
   return res;
@@ -3226,7 +3235,7 @@ INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) {
   COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(sigpending)(set);
   if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set));
   return res;
@@ -3244,7 +3253,7 @@ INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
   // FIXME: read sigset_t when all of sigemptyset, etc are intercepted
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(sigprocmask)(how, set, oldset);
   if (!res && oldset)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
@@ -3261,7 +3270,7 @@ INTERCEPTOR(int, backtrace, void **buffer, int size) {
   COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(backtrace)(buffer, size);
   if (res && buffer)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer));
@@ -3275,7 +3284,7 @@ INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) {
     COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer));
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char **res = REAL(backtrace_symbols)(buffer, size);
   if (res && size) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res));
@@ -3383,7 +3392,7 @@ INTERCEPTOR(int, statfs, char *path, void *buf) {
   if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(statfs)(path, buf);
   if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz);
   return res;
@@ -3393,7 +3402,7 @@ INTERCEPTOR(int, fstatfs, int fd, void *buf) {
   COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(fstatfs)(fd, buf);
   if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz);
   return res;
@@ -3412,7 +3421,7 @@ INTERCEPTOR(int, statfs64, char *path, void *buf) {
   if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(statfs64)(path, buf);
   if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz);
   return res;
@@ -3422,7 +3431,7 @@ INTERCEPTOR(int, fstatfs64, int fd, void *buf) {
   COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(fstatfs64)(fd, buf);
   if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz);
   return res;
@@ -3441,7 +3450,7 @@ INTERCEPTOR(int, statvfs, char *path, void *buf) {
   if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(statvfs)(path, buf);
   if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
   return res;
@@ -3451,7 +3460,7 @@ INTERCEPTOR(int, fstatvfs, int fd, void *buf) {
   COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(fstatvfs)(fd, buf);
   if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
   return res;
@@ -3470,7 +3479,7 @@ INTERCEPTOR(int, statvfs64, char *path, void *buf) {
   if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(statvfs64)(path, buf);
   if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz);
   return res;
@@ -3480,7 +3489,7 @@ INTERCEPTOR(int, fstatvfs64, int fd, void *buf) {
   COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(fstatvfs64)(fd, buf);
   if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz);
   return res;
@@ -3536,7 +3545,7 @@ INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) {
   if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr));
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(ether_ntohost)(hostname, addr);
   if (!res && hostname)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1);
@@ -3549,7 +3558,7 @@ INTERCEPTOR(int, ether_hostton, char *hostname, __sanitizer_ether_addr *addr) {
     COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(ether_hostton)(hostname, addr);
   if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr));
   return res;
@@ -3561,7 +3570,7 @@ INTERCEPTOR(int, ether_line, char *line, __sanitizer_ether_addr *addr,
   if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(ether_line)(line, addr, hostname);
   if (!res) {
     if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr));
@@ -3585,7 +3594,7 @@ INTERCEPTOR(char *, ether_ntoa_r, __sanitizer_ether_addr *addr, char *buf) {
   if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr));
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(ether_ntoa_r)(addr, buf);
   if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
   return res;
@@ -3597,7 +3606,7 @@ INTERCEPTOR(__sanitizer_ether_addr *, ether_aton_r, char *buf,
   if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   __sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr);
   if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res));
   return res;
@@ -3615,7 +3624,7 @@ INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) {
   COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(shmctl)(shmid, cmd, buf);
   if (res >= 0) {
     unsigned sz = 0;
@@ -3640,7 +3649,7 @@ INTERCEPTOR(int, random_r, void *buf, u32 *result) {
   COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(random_r)(buf, result);
   if (!res && result)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
@@ -3653,7 +3662,7 @@ INTERCEPTOR(int, random_r, void *buf, u32 *result) {
 
 // FIXME: under ASan the REAL() call below may write to freed memory and corrupt
 // its metadata. See
-// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+// https://github.com/google/sanitizers/issues/321.
 #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET ||              \
     SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED || \
     SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GET ||         \
@@ -3692,7 +3701,7 @@ INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) {
   COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(pthread_attr_getstack)(attr, addr, size);
   if (!res) {
     if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr));
@@ -3741,7 +3750,7 @@ INTERCEPTOR(int, pthread_attr_getaffinity_np, void *attr, SIZE_T cpusetsize,
                            cpuset);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset);
   if (!res && cpusetsize && cpuset)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize);
@@ -3851,7 +3860,7 @@ INTERCEPTOR(char *, tmpnam, char *s) {
     if (s)
       // FIXME: under ASan the call below may write to freed memory and corrupt
       // its metadata. See
-      // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+      // https://github.com/google/sanitizers/issues/321.
       COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1);
     else
       COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
@@ -3869,7 +3878,7 @@ INTERCEPTOR(char *, tmpnam_r, char *s) {
   COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(tmpnam_r)(s);
   if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1);
   return res;
@@ -3913,7 +3922,7 @@ INTERCEPTOR(void, sincos, double x, double *sin, double *cos) {
   COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   REAL(sincos)(x, sin, cos);
   if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin));
   if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos));
@@ -3923,7 +3932,7 @@ INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) {
   COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   REAL(sincosf)(x, sin, cos);
   if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin));
   if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos));
@@ -3933,7 +3942,7 @@ INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) {
   COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   REAL(sincosl)(x, sin, cos);
   if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin));
   if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos));
@@ -3952,7 +3961,7 @@ INTERCEPTOR(double, remquo, double x, double y, int *quo) {
   COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   double res = REAL(remquo)(x, y, quo);
   if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
   return res;
@@ -3962,7 +3971,7 @@ INTERCEPTOR(float, remquof, float x, float y, int *quo) {
   COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   float res = REAL(remquof)(x, y, quo);
   if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
   return res;
@@ -3972,7 +3981,7 @@ INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) {
   COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   long double res = REAL(remquol)(x, y, quo);
   if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
   return res;
@@ -4022,7 +4031,7 @@ INTERCEPTOR(double, lgamma_r, double x, int *signp) {
   COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   double res = REAL(lgamma_r)(x, signp);
   if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp));
   return res;
@@ -4032,7 +4041,7 @@ INTERCEPTOR(float, lgammaf_r, float x, int *signp) {
   COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   float res = REAL(lgammaf_r)(x, signp);
   if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp));
   return res;
@@ -4050,7 +4059,7 @@ INTERCEPTOR(long double, lgammal_r, long double x, int *signp) {
   COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   long double res = REAL(lgammal_r)(x, signp);
   if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp));
   return res;
@@ -4066,7 +4075,7 @@ INTERCEPTOR(int, drand48_r, void *buffer, double *result) {
   COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(drand48_r)(buffer, result);
   if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
   return res;
@@ -4076,7 +4085,7 @@ INTERCEPTOR(int, lrand48_r, void *buffer, long *result) {
   COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(lrand48_r)(buffer, result);
   if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
   return res;
@@ -4106,7 +4115,7 @@ INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) {
   COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, stream);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(getline)(lineptr, n, stream);
   if (res > 0) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr));
@@ -4118,7 +4127,7 @@ INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) {
 
 // FIXME: under ASan the call below may write to freed memory and corrupt its
 // metadata. See
-// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+// https://github.com/google/sanitizers/issues/321.
 #define GETDELIM_INTERCEPTOR_IMPL(vname)                                       \
   {                                                                            \
     void *ctx;                                                                 \
@@ -4165,7 +4174,7 @@ INTERCEPTOR(SIZE_T, iconv, void *cd, char **inbuf, SIZE_T *inbytesleft,
   void *outbuf_orig = outbuf ? *outbuf : nullptr;
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SIZE_T res = REAL(iconv)(cd, inbuf, inbytesleft, outbuf, outbytesleft);
   if (res != (SIZE_T) - 1 && outbuf && *outbuf > outbuf_orig) {
     SIZE_T sz = (char *)*outbuf - (char *)outbuf_orig;
@@ -4184,7 +4193,7 @@ INTERCEPTOR(__sanitizer_clock_t, times, void *tms) {
   COMMON_INTERCEPTOR_ENTER(ctx, times, tms);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   __sanitizer_clock_t res = REAL(times)(tms);
   if (res != (__sanitizer_clock_t)-1 && tms)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tms, struct_tms_sz);
@@ -4227,7 +4236,7 @@ INTERCEPTOR(SSIZE_T, listxattr, const char *path, char *list, SIZE_T size) {
   if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(listxattr)(path, list, size);
   // Here and below, size == 0 is a special case where nothing is written to the
   // buffer, and res contains the desired buffer size.
@@ -4240,7 +4249,7 @@ INTERCEPTOR(SSIZE_T, llistxattr, const char *path, char *list, SIZE_T size) {
   if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(llistxattr)(path, list, size);
   if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res);
   return res;
@@ -4250,7 +4259,7 @@ INTERCEPTOR(SSIZE_T, flistxattr, int fd, char *list, SIZE_T size) {
   COMMON_INTERCEPTOR_ENTER(ctx, flistxattr, fd, list, size);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(flistxattr)(fd, list, size);
   if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res);
   return res;
@@ -4272,7 +4281,7 @@ INTERCEPTOR(SSIZE_T, getxattr, const char *path, const char *name, char *value,
   if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(getxattr)(path, name, value, size);
   if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res);
   return res;
@@ -4285,7 +4294,7 @@ INTERCEPTOR(SSIZE_T, lgetxattr, const char *path, const char *name, char *value,
   if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(lgetxattr)(path, name, value, size);
   if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res);
   return res;
@@ -4297,7 +4306,7 @@ INTERCEPTOR(SSIZE_T, fgetxattr, int fd, const char *name, char *value,
   if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   SSIZE_T res = REAL(fgetxattr)(fd, name, value, size);
   if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res);
   return res;
@@ -4316,7 +4325,7 @@ INTERCEPTOR(int, getresuid, void *ruid, void *euid, void *suid) {
   COMMON_INTERCEPTOR_ENTER(ctx, getresuid, ruid, euid, suid);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getresuid)(ruid, euid, suid);
   if (res >= 0) {
     if (ruid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ruid, uid_t_sz);
@@ -4330,7 +4339,7 @@ INTERCEPTOR(int, getresgid, void *rgid, void *egid, void *sgid) {
   COMMON_INTERCEPTOR_ENTER(ctx, getresgid, rgid, egid, sgid);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getresgid)(rgid, egid, sgid);
   if (res >= 0) {
     if (rgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rgid, gid_t_sz);
@@ -4355,7 +4364,7 @@ INTERCEPTOR(int, getifaddrs, __sanitizer_ifaddrs **ifap) {
   COMMON_INTERCEPTOR_ENTER(ctx, getifaddrs, ifap);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(getifaddrs)(ifap);
   if (res == 0 && ifap) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifap, sizeof(void *));
@@ -4391,7 +4400,7 @@ INTERCEPTOR(char *, if_indextoname, unsigned int ifindex, char* ifname) {
   COMMON_INTERCEPTOR_ENTER(ctx, if_indextoname, ifindex, ifname);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   char *res = REAL(if_indextoname)(ifindex, ifname);
   if (res && ifname)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1);
@@ -4419,7 +4428,7 @@ INTERCEPTOR(int, capget, void *hdrp, void *datap) {
     COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(capget)(hdrp, datap);
   if (res == 0 && datap)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datap, __user_cap_data_struct_sz);
@@ -4520,7 +4529,7 @@ INTERCEPTOR(int, ftime, __sanitizer_timeb *tp) {
   COMMON_INTERCEPTOR_ENTER(ctx, ftime, tp);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(ftime)(tp);
   if (tp)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, sizeof(*tp));
@@ -4538,7 +4547,7 @@ INTERCEPTOR(void, xdrmem_create, __sanitizer_XDR *xdrs, uptr addr,
   COMMON_INTERCEPTOR_ENTER(ctx, xdrmem_create, xdrs, addr, size, op);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   REAL(xdrmem_create)(xdrs, addr, size, op);
   COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs));
   if (op == __sanitizer_XDR_ENCODE) {
@@ -4553,14 +4562,14 @@ INTERCEPTOR(void, xdrstdio_create, __sanitizer_XDR *xdrs, void *file, int op) {
   COMMON_INTERCEPTOR_ENTER(ctx, xdrstdio_create, xdrs, file, op);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   REAL(xdrstdio_create)(xdrs, file, op);
   COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs));
 }
 
 // FIXME: under ASan the call below may write to freed memory and corrupt
 // its metadata. See
-// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+// https://github.com/google/sanitizers/issues/321.
 #define XDR_INTERCEPTOR(F, T)                             \
   INTERCEPTOR(int, F, __sanitizer_XDR *xdrs, T *p) {      \
     void *ctx;                                            \
@@ -4614,7 +4623,7 @@ INTERCEPTOR(int, xdr_bytes, __sanitizer_XDR *xdrs, char **p, unsigned *sizep,
   }
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(xdr_bytes)(xdrs, p, sizep, maxsize);
   if (p && sizep && xdrs->x_op == __sanitizer_XDR_DECODE) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
@@ -4634,7 +4643,7 @@ INTERCEPTOR(int, xdr_string, __sanitizer_XDR *xdrs, char **p,
   }
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   int res = REAL(xdr_string)(xdrs, p, maxsize);
   if (p && xdrs->x_op == __sanitizer_XDR_DECODE) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
@@ -4686,7 +4695,7 @@ INTERCEPTOR(void *, tsearch, void *key, void **rootp,
   COMMON_INTERCEPTOR_ENTER(ctx, tsearch, key, rootp, compar);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   void *res = REAL(tsearch)(key, rootp, compar);
   if (res && *(void **)res == key)
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(void *));
@@ -4768,7 +4777,7 @@ INTERCEPTOR(int, __woverflow, __sanitizer_FILE *fp, int ch) {
 INTERCEPTOR(__sanitizer_FILE *, fopen, const char *path, const char *mode) {
   void *ctx;
   COMMON_INTERCEPTOR_ENTER(ctx, fopen, path, mode);
-  COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+  if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
   COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
   __sanitizer_FILE *res = REAL(fopen)(path, mode);
   COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path);
@@ -4839,7 +4848,7 @@ INTERCEPTOR(__sanitizer_FILE *, open_memstream, char **ptr, SIZE_T *sizeloc) {
   COMMON_INTERCEPTOR_ENTER(ctx, open_memstream, ptr, sizeloc);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   __sanitizer_FILE *res = REAL(open_memstream)(ptr, sizeloc);
   if (res) {
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr));
@@ -4870,7 +4879,7 @@ INTERCEPTOR(__sanitizer_FILE *, fmemopen, void *buf, SIZE_T size,
   COMMON_INTERCEPTOR_ENTER(ctx, fmemopen, buf, size, mode);
   // FIXME: under ASan the call below may write to freed memory and corrupt
   // its metadata. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+  // https://github.com/google/sanitizers/issues/321.
   __sanitizer_FILE *res = REAL(fmemopen)(buf, size, mode);
   if (res) unpoison_file(res);
   return res;
@@ -5284,6 +5293,36 @@ INTERCEPTOR(SSIZE_T, process_vm_writev, int pid, __sanitizer_iovec *local_iov,
 #define INIT_PROCESS_VM_READV
 #endif
 
+#if SANITIZER_INTERCEPT_CTERMID
+INTERCEPTOR(char *, ctermid, char *s) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ctermid, s);
+  char *res = REAL(ctermid)(s);
+  if (res) {
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
+  }
+  return res;
+}
+#define INIT_CTERMID COMMON_INTERCEPT_FUNCTION(ctermid);
+#else
+#define INIT_CTERMID
+#endif
+
+#if SANITIZER_INTERCEPT_CTERMID_R
+INTERCEPTOR(char *, ctermid_r, char *s) {
+  void *ctx;
+  COMMON_INTERCEPTOR_ENTER(ctx, ctermid_r, s);
+  char *res = REAL(ctermid_r)(s);
+  if (res) {
+    COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
+  }
+  return res;
+}
+#define INIT_CTERMID_R COMMON_INTERCEPT_FUNCTION(ctermid_r);
+#else
+#define INIT_CTERMID_R
+#endif
+
 static void InitializeCommonInterceptors() {
   static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
   interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
@@ -5458,4 +5497,6 @@ static void InitializeCommonInterceptors() {
   INIT_PTHREAD_SETCANCEL;
   INIT_MINCORE;
   INIT_PROCESS_VM_READV;
+  INIT_CTERMID;
+  INIT_CTERMID_R;
 }
index 1b65bced75d5a84626789b9e39d2066ab7766df6..596f5bcd317340560fcd38671221fce665927a36 100644 (file)
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "sanitizer_common.h"
+
 #include "sanitizer_flags.h"
 #include "sanitizer_stackdepot.h"
 #include "sanitizer_stacktrace.h"
@@ -46,6 +47,7 @@ void SetSandboxingCallback(void (*f)()) {
 }
 
 void ReportErrorSummary(const char *error_type, StackTrace *stack) {
+#if !SANITIZER_GO
   if (!common_flags()->print_summary)
     return;
   if (stack->size == 0) {
@@ -58,6 +60,7 @@ void ReportErrorSummary(const char *error_type, StackTrace *stack) {
   SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
   ReportErrorSummary(error_type, frame->info);
   frame->ClearAll();
+#endif
 }
 
 static void (*SoftRssLimitExceededCallback)(bool exceeded);
@@ -116,8 +119,27 @@ void BackgroundThread(void *arg) {
   }
 }
 
+void WriteToSyslog(const char *msg) {
+  InternalScopedString msg_copy(kErrorMessageBufferSize);
+  msg_copy.append("%s", msg);
+  char *p = msg_copy.data();
+  char *q;
+
+  // Print one line at a time.
+  // syslog, at least on Android, has an implicit message length limit.
+  do {
+    q = internal_strchr(p, '\n');
+    if (q)
+      *q = '\0';
+    WriteOneLineToSyslog(p);
+    if (q)
+      p = q + 1;
+  } while (q);
+}
+
 void MaybeStartBackgroudThread() {
-#if SANITIZER_LINUX  // Need to implement/test on other platforms.
+#if SANITIZER_LINUX && \
+    !SANITIZER_GO  // Need to implement/test on other platforms.
   // Start the background thread if one of the rss limits is given.
   if (!common_flags()->hard_rss_limit_mb &&
       !common_flags()->soft_rss_limit_mb) return;
index 65d1e37f62efbb7b3b130617314ff56384fc5fcf..9f4f97224e18e78042812d4068e0a03d66763134 100644 (file)
 namespace __sanitizer {
 
 #if SANITIZER_LINUX
-void WriteToSyslog(const char *buffer) {}
+bool ShouldLogAfterPrintf() { return false; }
+void LogMessageOnPrintf(const char *str) {}
 #endif
-
+void WriteToSyslog(const char *buffer) {}
 void Abort() { internal__exit(1); }
 
 }  // namespace __sanitizer
index 9098cec91f223820988199775a6be58b1fcc16a3..eaa1446afd4424b6a9aeb152b34e3e9f8bd35c2e 100644 (file)
@@ -55,6 +55,11 @@ static atomic_uint32_t dump_once_guard;  // Ensure that CovDump runs only once.
 static atomic_uintptr_t coverage_counter;
 static atomic_uintptr_t caller_callee_counter;
 
+static void ResetGlobalCounters() {
+  return atomic_store(&coverage_counter, 0, memory_order_relaxed);
+  return atomic_store(&caller_callee_counter, 0, memory_order_relaxed);
+}
+
 // pc_array is the array containing the covered PCs.
 // To make the pc_array thread- and async-signal-safe it has to be large enough.
 // 128M counters "ought to be enough for anybody" (4M on 32-bit).
@@ -91,7 +96,7 @@ class CoverageData {
   void DumpAll();
 
   ALWAYS_INLINE
-  void TraceBasicBlock(s32 *id);
+  void TraceBasicBlock(u32 *id);
 
   void InitializeGuardArray(s32 *guards);
   void InitializeGuards(s32 *guards, uptr n, const char *module_name,
@@ -103,6 +108,7 @@ class CoverageData {
 
   uptr *data();
   uptr size();
+  uptr *buffer() const { return pc_buffer; }
 
  private:
   void DirectOpen();
@@ -128,6 +134,8 @@ class CoverageData {
   // Descriptor of the file mapped pc array.
   fd_t pc_fd;
 
+  uptr *pc_buffer;
+
   // Vector of coverage guard arrays, protected by mu.
   InternalMmapVectorNoCtor<s32*> guard_array_vec;
 
@@ -204,6 +212,11 @@ void CoverageData::Enable() {
     atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed);
   }
 
+  pc_buffer = nullptr;
+  if (common_flags()->coverage_pc_buffer)
+    pc_buffer = reinterpret_cast<uptr *>(MmapNoReserveOrDie(
+        sizeof(uptr) * kPcArrayMaxSize, "CovInit::pc_buffer"));
+
   cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie(
       sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array"));
   atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
@@ -226,7 +239,8 @@ void CoverageData::InitializeGuardArray(s32 *guards) {
   Enable();  // Make sure coverage is enabled at this point.
   s32 n = guards[0];
   for (s32 j = 1; j <= n; j++) {
-    uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed);
+    uptr idx = atomic_load_relaxed(&pc_array_index);
+    atomic_store_relaxed(&pc_array_index, idx + 1);
     guards[j] = -static_cast<s32>(idx + 1);
   }
 }
@@ -240,6 +254,10 @@ void CoverageData::Disable() {
     UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize);
     cc_array = nullptr;
   }
+  if (pc_buffer) {
+    UnmapOrDie(pc_buffer, sizeof(uptr) * kPcArrayMaxSize);
+    pc_buffer = nullptr;
+  }
   if (tr_event_array) {
     UnmapOrDie(tr_event_array,
                sizeof(tr_event_array[0]) * kTrEventArrayMaxSize +
@@ -408,6 +426,7 @@ void CoverageData::Add(uptr pc, u32 *guard) {
            atomic_load(&pc_array_size, memory_order_acquire));
   uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
   pc_array[idx] = BundlePcAndCounter(pc, counter);
+  if (pc_buffer) pc_buffer[counter] = pc;
 }
 
 // Registers a pair caller=>callee.
@@ -676,11 +695,11 @@ void CoverageData::DumpCallerCalleePairs() {
 // it once and then cache in the provided 'cache' storage.
 //
 // This function will eventually be inlined by the compiler.
-void CoverageData::TraceBasicBlock(s32 *id) {
+void CoverageData::TraceBasicBlock(u32 *id) {
   // Will trap here if
   //  1. coverage is not enabled at run-time.
   //  2. The array tr_event_array is full.
-  *tr_event_pointer = static_cast<u32>(*id - 1);
+  *tr_event_pointer = *id - 1;
   tr_event_pointer++;
 }
 
@@ -914,15 +933,18 @@ uptr __sanitizer_get_total_unique_caller_callee_pairs() {
 }
 
 SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_cov_trace_func_enter(s32 *id) {
+void __sanitizer_cov_trace_func_enter(u32 *id) {
+  __sanitizer_cov_with_check(id);
   coverage_data.TraceBasicBlock(id);
 }
 SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_cov_trace_basic_block(s32 *id) {
+void __sanitizer_cov_trace_basic_block(u32 *id) {
+  __sanitizer_cov_with_check(id);
   coverage_data.TraceBasicBlock(id);
 }
 SANITIZER_INTERFACE_ATTRIBUTE
 void __sanitizer_reset_coverage() {
+  ResetGlobalCounters();
   coverage_data.ReinitializeGuards();
   internal_bzero_aligned16(
       coverage_data.data(),
@@ -934,6 +956,12 @@ uptr __sanitizer_get_coverage_guards(uptr **data) {
   return coverage_data.size();
 }
 
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_coverage_pc_buffer(uptr **data) {
+  *data = coverage_data.buffer();
+  return __sanitizer_get_total_unique_coverage();
+}
+
 SANITIZER_INTERFACE_ATTRIBUTE
 uptr __sanitizer_get_number_of_counters() {
   return coverage_data.GetNumberOf8bitCounters();
index e742d8546639da85d7f809090abee5882b533bac..c28f7f4a6034b70bf1815e55889f9eec819886ca 100644 (file)
@@ -56,7 +56,7 @@ COMMON_FLAG(
     "Mention name of executable when reporting error and "
     "append executable name to logs (as in \"log_path.exe_name.pid\").")
 COMMON_FLAG(
-    bool, log_to_syslog, SANITIZER_ANDROID,
+    bool, log_to_syslog, SANITIZER_ANDROID || SANITIZER_MAC,
     "Write all sanitizer output to syslog in addition to other means of "
     "logging.")
 COMMON_FLAG(
@@ -79,6 +79,8 @@ COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV,
             "If set, registers the tool's custom SIGSEGV/SIGBUS handler.")
 COMMON_FLAG(bool, handle_abort, false,
             "If set, registers the tool's custom SIGABRT handler.")
+COMMON_FLAG(bool, handle_sigill, false,
+            "If set, registers the tool's custom SIGILL handler.")
 COMMON_FLAG(bool, handle_sigfpe, true,
             "If set, registers the tool's custom SIGFPE handler.")
 COMMON_FLAG(bool, allow_user_segv_handler, false,
@@ -142,6 +144,9 @@ COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID,
 COMMON_FLAG(const char *, coverage_dir, ".",
             "Target directory for coverage dumps. Defaults to the current "
             "directory.")
+COMMON_FLAG(bool, coverage_pc_buffer, true,
+            "If set (and if 'coverage' is set too), the pcs would be collected "
+            "in a buffer.")
 COMMON_FLAG(bool, full_address_space, false,
             "Sanitize complete address space; "
             "by default kernel area on 32-bit platforms will not be sanitized")
@@ -192,3 +197,6 @@ COMMON_FLAG(
     bool, abort_on_error, SANITIZER_MAC,
     "If set, the tool calls abort() instead of _exit() after printing the "
     "error report.")
+COMMON_FLAG(bool, suppress_equal_pcs, true,
+            "Deduplicate multiple reports for single source location in "
+            "halt_on_error=false mode (asan only).")
index dfc9d2833ba328cacb49548b82386c5b2e4ee0ed..e83eed0830d848bb2364e7c363992981759c78f8 100644 (file)
@@ -117,7 +117,10 @@ using namespace __sanitizer;  // NOLINT
 // Common defs.
 #define INLINE inline
 #define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
-#define WEAK SANITIZER_WEAK_ATTRIBUTE
+#define SANITIZER_WEAK_DEFAULT_IMPL \
+  extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
+#define SANITIZER_WEAK_CXX_DEFAULT_IMPL \
+  extern "C++" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
 
 // Platform-specific defs.
 #if defined(_MSC_VER)
index 43e4546946b0b9e276b1349c5ce80d5af0947586..cf31e689653f0ef4e6aa9929d339127c2f7b64a5 100644 (file)
@@ -175,6 +175,19 @@ uptr internal_strlen(const char *s) {
   return i;
 }
 
+uptr internal_strlcat(char *dst, const char *src, uptr maxlen) {
+  const uptr srclen = internal_strlen(src);
+  const uptr dstlen = internal_strnlen(dst, maxlen);
+  if (dstlen == maxlen) return maxlen + srclen;
+  if (srclen < maxlen - dstlen) {
+    internal_memmove(dst + dstlen, src, srclen + 1);
+  } else {
+    internal_memmove(dst + dstlen, src, maxlen - dstlen - 1);
+    dst[maxlen - 1] = '\0';
+  }
+  return dstlen + srclen;
+}
+
 char *internal_strncat(char *dst, const char *src, uptr n) {
   uptr len = internal_strlen(dst);
   uptr i;
@@ -184,6 +197,17 @@ char *internal_strncat(char *dst, const char *src, uptr n) {
   return dst;
 }
 
+uptr internal_strlcpy(char *dst, const char *src, uptr maxlen) {
+  const uptr srclen = internal_strlen(src);
+  if (srclen < maxlen) {
+    internal_memmove(dst, src, srclen + 1);
+  } else if (maxlen != 0) {
+    internal_memmove(dst, src, maxlen - 1);
+    dst[maxlen - 1] = '\0';
+  }
+  return srclen;
+}
+
 char *internal_strncpy(char *dst, const char *src, uptr n) {
   uptr i;
   for (i = 0; i < n && src[i]; i++)
index 99de140ec1db4c31496fc53a156504b44fe5c012..df28677c6700612be68b04ed845da4df7d698675 100644 (file)
@@ -43,8 +43,10 @@ uptr internal_strcspn(const char *s, const char *reject);
 char *internal_strdup(const char *s);
 char *internal_strndup(const char *s, uptr n);
 uptr internal_strlen(const char *s);
+uptr internal_strlcat(char *dst, const char *src, uptr maxlen);
 char *internal_strncat(char *dst, const char *src, uptr n);
 int internal_strncmp(const char *s1, const char *s2, uptr n);
+uptr internal_strlcpy(char *dst, const char *src, uptr maxlen);
 char *internal_strncpy(char *dst, const char *src, uptr n);
 uptr internal_strnlen(const char *s, uptr maxlen);
 char *internal_strrchr(const char *s, int c);
index a001c1a7de9789718903f9127c8c845b08a25fcc..cba38c8c3057d8c4e659f8b1c10297aeb5464a85 100644 (file)
@@ -89,7 +89,8 @@ const int FUTEX_WAKE = 1;
 // Are we using 32-bit or 64-bit Linux syscalls?
 // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
 // but it still needs to use 64-bit syscalls.
-#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_WORDSIZE == 64)
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \
+    SANITIZER_WORDSIZE == 64)
 # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
 #else
 # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
@@ -983,18 +984,91 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                        : "x30", "memory");
   return res;
 }
-#endif  // defined(__x86_64__) && SANITIZER_LINUX
+#elif defined(__powerpc64__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+                   int *parent_tidptr, void *newtls, int *child_tidptr) {
+  long long res;
+/* Stack frame offsets.  */
+#if _CALL_ELF != 2
+#define FRAME_MIN_SIZE         112
+#define FRAME_TOC_SAVE         40
+#else
+#define FRAME_MIN_SIZE         32
+#define FRAME_TOC_SAVE         24
+#endif
+  if (!fn || !child_stack)
+    return -EINVAL;
+  CHECK_EQ(0, (uptr)child_stack % 16);
+  child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+  ((unsigned long long *)child_stack)[0] = (uptr)fn;
+  ((unsigned long long *)child_stack)[1] = (uptr)arg;
 
-#if SANITIZER_ANDROID
-#define PROP_VALUE_MAX 92
-extern "C" SANITIZER_WEAK_ATTRIBUTE int __system_property_get(const char *name,
-                                                              char *value);
-void GetExtraActivationFlags(char *buf, uptr size) {
-  CHECK(size > PROP_VALUE_MAX);
-  CHECK(&__system_property_get);
-  __system_property_get("asan.options", buf);
+  register int (*__fn)(void *) __asm__("r3") = fn;
+  register void *__cstack      __asm__("r4") = child_stack;
+  register int __flags         __asm__("r5") = flags;
+  register void * __arg        __asm__("r6") = arg;
+  register int * __ptidptr     __asm__("r7") = parent_tidptr;
+  register void * __newtls     __asm__("r8") = newtls;
+  register int * __ctidptr     __asm__("r9") = child_tidptr;
+
+ __asm__ __volatile__(
+           /* fn, arg, child_stack are saved acrVoss the syscall */
+           "mr 28, %5\n\t"
+           "mr 29, %6\n\t"
+           "mr 27, %8\n\t"
+
+           /* syscall
+             r3 == flags
+             r4 == child_stack
+             r5 == parent_tidptr
+             r6 == newtls
+             r7 == child_tidptr */
+           "mr 3, %7\n\t"
+           "mr 5, %9\n\t"
+           "mr 6, %10\n\t"
+           "mr 7, %11\n\t"
+           "li 0, %3\n\t"
+           "sc\n\t"
+
+           /* Test if syscall was successful */
+           "cmpdi  cr1, 3, 0\n\t"
+           "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
+           "bne-   cr1, 1f\n\t"
+
+           /* Do the function call */
+           "std   2, %13(1)\n\t"
+#if _CALL_ELF != 2
+           "ld    0, 0(28)\n\t"
+           "ld    2, 8(28)\n\t"
+           "mtctr 0\n\t"
+#else
+           "mr    12, 28\n\t"
+           "mtctr 12\n\t"
+#endif
+           "mr    3, 27\n\t"
+           "bctrl\n\t"
+           "ld    2, %13(1)\n\t"
+
+           /* Call _exit(r3) */
+           "li 0, %4\n\t"
+           "sc\n\t"
+
+           /* Return to parent */
+           "1:\n\t"
+           "mr %0, 3\n\t"
+             : "=r" (res)
+             : "0" (-1), "i" (EINVAL),
+               "i" (__NR_clone), "i" (__NR_exit),
+               "r" (__fn), "r" (__cstack), "r" (__flags),
+               "r" (__arg), "r" (__ptidptr), "r" (__newtls),
+               "r" (__ctidptr), "i" (FRAME_MIN_SIZE), "i" (FRAME_TOC_SAVE)
+             : "cr0", "cr1", "memory", "ctr",
+               "r0", "r29", "r27", "r28");
+  return res;
 }
+#endif  // defined(__x86_64__) && SANITIZER_LINUX
 
+#if SANITIZER_ANDROID
 #if __ANDROID_API__ < 21
 extern "C" __attribute__((weak)) int dl_iterate_phdr(
     int (*)(struct dl_phdr_info *, size_t, void *), void *);
@@ -1040,6 +1114,8 @@ AndroidApiLevel AndroidGetApiLevel() {
 bool IsDeadlySignal(int signum) {
   if (common_flags()->handle_abort && signum == SIGABRT)
     return true;
+  if (common_flags()->handle_sigill && signum == SIGILL)
+    return true;
   if (common_flags()->handle_sigfpe && signum == SIGFPE)
     return true;
   return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv;
@@ -1143,6 +1219,14 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
 #endif
 }
 
+void DisableReexec() {
+  // No need to re-exec on Linux.
+}
+
+void MaybeReexec() {
+  // No need to re-exec on Linux.
+}
+
 } // namespace __sanitizer
 
 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX
index d996f8c322035dfd0753083cc3ffc1820f5d25e5..77bfbd156815f3ba8d440a2c6bb789c00ef038cb 100644 (file)
@@ -44,7 +44,8 @@ uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
 // internal_sigaction instead.
 int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
 void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
-#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__)
+#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) \
+  || defined(__powerpc64__)
 uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
                     int *parent_tidptr, void *newtls, int *child_tidptr);
 #endif
index 4b583bfa414865c78c06feb8ce1e2d979572ffdc..04031d25528c0a089a9ad980c2ca6e7afa4836a4 100644 (file)
@@ -166,11 +166,15 @@ static uptr g_tls_size;
 # define DL_INTERNAL_FUNCTION
 #endif
 
-#if defined(__mips__)
+#if defined(__mips__) || defined(__powerpc64__)
 // TlsPreTcbSize includes size of struct pthread_descr and size of tcb
 // head structure. It lies before the static tls blocks.
 static uptr TlsPreTcbSize() {
-  const uptr kTcbHead = 16;
+# if defined(__mips__)
+  const uptr kTcbHead = 16; // sizeof (tcbhead_t)
+# elif defined(__powerpc64__)
+  const uptr kTcbHead = 88; // sizeof (tcbhead_t)
+# endif
   const uptr kTlsAlign = 16;
   const uptr kTlsPreTcbSize =
     (ThreadDescriptorSize() + kTcbHead + kTlsAlign - 1) & ~(kTlsAlign - 1);
@@ -201,9 +205,9 @@ void InitTlsSize() {
 }
 
 #if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) \
-    || defined(__aarch64__)) \
+    || defined(__aarch64__) || defined(__powerpc64__)) \
     && SANITIZER_LINUX && !SANITIZER_ANDROID
-// sizeof(struct thread) from glibc.
+// sizeof(struct pthread) from glibc.
 static atomic_uintptr_t kThreadDescriptorSize;
 
 uptr ThreadDescriptorSize() {
@@ -218,7 +222,12 @@ uptr ThreadDescriptorSize() {
     char *end;
     int minor = internal_simple_strtoll(buf + 8, &end, 10);
     if (end != buf + 8 && (*end == '\0' || *end == '.')) {
-      /* sizeof(struct thread) values from various glibc versions.  */
+      int patch = 0;
+      if (*end == '.')
+        // strtoll will return 0 if no valid conversion could be performed
+        patch = internal_simple_strtoll(end + 1, nullptr, 10);
+
+      /* sizeof(struct pthread) values from various glibc versions.  */
       if (SANITIZER_X32)
         val = 1728;  // Assume only one particular version for x32.
       else if (minor <= 3)
@@ -231,9 +240,9 @@ uptr ThreadDescriptorSize() {
         val = FIRST_32_SECOND_64(1136, 1712);
       else if (minor == 10)
         val = FIRST_32_SECOND_64(1168, 1776);
-      else if (minor <= 12)
+      else if (minor == 11 || (minor == 12 && patch == 1))
         val = FIRST_32_SECOND_64(1168, 2288);
-      else if (minor == 13)
+      else if (minor <= 13)
         val = FIRST_32_SECOND_64(1168, 2304);
       else
         val = FIRST_32_SECOND_64(1216, 2304);
@@ -254,6 +263,10 @@ uptr ThreadDescriptorSize() {
   val = 1776;
   atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
   return val;
+#elif defined(__powerpc64__)
+  val = 1776; // from glibc.ppc64le 2.20-8.fc21
+  atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+  return val;
 #endif
   return 0;
 }
@@ -285,6 +298,15 @@ uptr ThreadSelf() {
   descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
 # elif defined(__aarch64__)
   descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer());
+# elif defined(__powerpc64__)
+  // PPC64LE uses TLS variant I. The thread pointer (in GPR 13)
+  // points to the end of the TCB + 0x7000. The pthread_descr structure is
+  // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
+  // TCB and the size of pthread_descr.
+  const uptr kTlsTcbOffset = 0x7000;
+  uptr thread_pointer;
+  asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset));
+  descr_addr = thread_pointer - TlsPreTcbSize();
 # else
 #  error "unsupported CPU arch"
 # endif
@@ -320,7 +342,7 @@ static void GetTls(uptr *addr, uptr *size) {
   *size = GetTlsSize();
   *addr -= *size;
   *addr += ThreadDescriptorSize();
-# elif defined(__mips__) || defined(__aarch64__)
+# elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__)
   *addr = ThreadSelf();
   *size = GetTlsSize();
 # else
@@ -507,16 +529,16 @@ void AndroidLogInit() {
   atomic_store(&android_log_initialized, 1, memory_order_release);
 }
 
-static bool IsSyslogAvailable() {
+static bool ShouldLogAfterPrintf() {
   return atomic_load(&android_log_initialized, memory_order_acquire);
 }
 #else
 void AndroidLogInit() {}
 
-static bool IsSyslogAvailable() { return true; }
+static bool ShouldLogAfterPrintf() { return true; }
 #endif  // SANITIZER_ANDROID
 
-static void WriteOneLineToSyslog(const char *s) {
+void WriteOneLineToSyslog(const char *s) {
 #if SANITIZER_ANDROID &&__ANDROID_API__ < 21
   __android_log_write(ANDROID_LOG_INFO, NULL, s);
 #else
@@ -524,24 +546,11 @@ static void WriteOneLineToSyslog(const char *s) {
 #endif
 }
 
-void WriteToSyslog(const char *buffer) {
-  if (!IsSyslogAvailable())
-    return;
-  char *copy = internal_strdup(buffer);
-  char *p = copy;
-  char *q;
-  // syslog, at least on Android, has an implicit message length limit.
-  // Print one line at a time.
-  do {
-    q = internal_strchr(p, '\n');
-    if (q)
-      *q = '\0';
-    WriteOneLineToSyslog(p);
-    if (q)
-      p = q + 1;
-  } while (q);
-  InternalFree(copy);
+void LogMessageOnPrintf(const char *str) {
+  if (common_flags()->log_to_syslog && ShouldLogAfterPrintf())
+    WriteToSyslog(str);
 }
+
 #endif // SANITIZER_LINUX
 
 } // namespace __sanitizer
index 3151cf8b9112c0b0e333f124bce606c23ae38bce..715509d30746985b4e77c8c1ab3e4b39f75905f3 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "sanitizer_platform.h"
 #if SANITIZER_MAC
+#include "sanitizer_mac.h"
 
 // Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so
 // the clients will most certainly use 64-bit ones as well.
@@ -25,7 +26,6 @@
 #include "sanitizer_flags.h"
 #include "sanitizer_internal_defs.h"
 #include "sanitizer_libc.h"
-#include "sanitizer_mac.h"
 #include "sanitizer_placement_new.h"
 #include "sanitizer_platform_limits_posix.h"
 #include "sanitizer_procmaps.h"
 extern char **environ;
 #endif
 
+#if defined(__has_include) && __has_include(<os/trace.h>)
+#define SANITIZER_OS_TRACE 1
+#include <os/trace.h>
+#else
+#define SANITIZER_OS_TRACE 0
+#endif
+
+#if !SANITIZER_IOS
+#include <crt_externs.h>  // for _NSGetArgv and _NSGetEnviron
+#else
+extern "C" {
+  extern char ***_NSGetArgv(void);
+}
+#endif
+
+#include <asl.h>
+#include <dlfcn.h>  // for dladdr()
 #include <errno.h>
 #include <fcntl.h>
 #include <libkern/OSAtomic.h>
@@ -52,6 +69,7 @@ extern char **environ;
 #include <sys/sysctl.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <util.h>
 
 namespace __sanitizer {
 
@@ -147,9 +165,30 @@ uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
   return sigprocmask(how, set, oldset);
 }
 
+// Doesn't call pthread_atfork() handlers.
+extern "C" pid_t __fork(void);
+
 int internal_fork() {
-  // TODO(glider): this may call user's pthread_atfork() handlers which is bad.
-  return fork();
+  return __fork();
+}
+
+int internal_forkpty(int *amaster) {
+  int master, slave;
+  if (openpty(&master, &slave, nullptr, nullptr, nullptr) == -1) return -1;
+  int pid = __fork();
+  if (pid == -1) {
+    close(master);
+    close(slave);
+    return -1;
+  }
+  if (pid == 0) {
+    close(master);
+    CHECK_EQ(login_tty(slave), 0);
+  } else {
+    *amaster = master;
+    close(slave);
+  }
+  return pid;
 }
 
 uptr internal_rename(const char *oldpath, const char *newpath) {
@@ -180,7 +219,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
   uptr stacksize = pthread_get_stacksize_np(pthread_self());
   // pthread_get_stacksize_np() returns an incorrect stack size for the main
   // thread on Mavericks. See
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=261
+  // https://github.com/google/sanitizers/issues/261
   if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization &&
       stacksize == (1 << 19))  {
     struct rlimit rl;
@@ -371,8 +410,67 @@ uptr GetRSS() {
   return info.resident_size;
 }
 
-void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
-void internal_join_thread(void *th) { }
+void *internal_start_thread(void(*func)(void *arg), void *arg) {
+  // Start the thread with signals blocked, otherwise it can steal user signals.
+  __sanitizer_sigset_t set, old;
+  internal_sigfillset(&set);
+  internal_sigprocmask(SIG_SETMASK, &set, &old);
+  pthread_t th;
+  pthread_create(&th, 0, (void*(*)(void *arg))func, arg);
+  internal_sigprocmask(SIG_SETMASK, &old, 0);
+  return th;
+}
+
+void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); }
+
+static BlockingMutex syslog_lock(LINKER_INITIALIZED);
+
+void WriteOneLineToSyslog(const char *s) {
+  syslog_lock.CheckLocked();
+  asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s);
+}
+
+void LogMessageOnPrintf(const char *str) {
+  // Log all printf output to CrashLog.
+  if (common_flags()->abort_on_error)
+    CRAppendCrashLogMessage(str);
+}
+
+void LogFullErrorReport(const char *buffer) {
+  // Log with os_trace. This will make it into the crash log.
+#if SANITIZER_OS_TRACE
+  if (GetMacosVersion() >= MACOS_VERSION_YOSEMITE) {
+    // os_trace requires the message (format parameter) to be a string literal.
+    if (internal_strncmp(SanitizerToolName, "AddressSanitizer",
+                         sizeof("AddressSanitizer") - 1) == 0)
+      os_trace("Address Sanitizer reported a failure.");
+    else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer",
+                              sizeof("UndefinedBehaviorSanitizer") - 1) == 0)
+      os_trace("Undefined Behavior Sanitizer reported a failure.");
+    else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer",
+                              sizeof("ThreadSanitizer") - 1) == 0)
+      os_trace("Thread Sanitizer reported a failure.");
+    else
+      os_trace("Sanitizer tool reported a failure.");
+
+    if (common_flags()->log_to_syslog)
+      os_trace("Consult syslog for more information.");
+  }
+#endif
+
+  // Log to syslog.
+  // The logging on OS X may call pthread_create so we need the threading
+  // environment to be fully initialized. Also, this should never be called when
+  // holding the thread registry lock since that may result in a deadlock. If
+  // the reporting thread holds the thread registry mutex, and asl_log waits
+  // for GCD to dispatch a new thread, the process will deadlock, because the
+  // pthread_create wrapper needs to acquire the lock as well.
+  BlockingMutexLock l(&syslog_lock);
+  if (common_flags()->log_to_syslog)
+    WriteToSyslog(buffer);
+
+  // The report is added to CrashLog as part of logging all of Printf output.
+}
 
 void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
   ucontext_t *ucontext = (ucontext_t*)context;
@@ -401,6 +499,173 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
 # endif
 }
 
+static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
+LowLevelAllocator allocator_for_env;
+
+// Change the value of the env var |name|, leaking the original value.
+// If |name_value| is NULL, the variable is deleted from the environment,
+// otherwise the corresponding "NAME=value" string is replaced with
+// |name_value|.
+void LeakyResetEnv(const char *name, const char *name_value) {
+  char **env = GetEnviron();
+  uptr name_len = internal_strlen(name);
+  while (*env != 0) {
+    uptr len = internal_strlen(*env);
+    if (len > name_len) {
+      const char *p = *env;
+      if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
+        // Match.
+        if (name_value) {
+          // Replace the old value with the new one.
+          *env = const_cast<char*>(name_value);
+        } else {
+          // Shift the subsequent pointers back.
+          char **del = env;
+          do {
+            del[0] = del[1];
+          } while (*del++);
+        }
+      }
+    }
+    env++;
+  }
+}
+
+static bool reexec_disabled = false;
+
+void DisableReexec() {
+  reexec_disabled = true;
+}
+
+extern "C" double dyldVersionNumber;
+static const double kMinDyldVersionWithAutoInterposition = 360.0;
+
+bool DyldNeedsEnvVariable() {
+  // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
+  // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via
+  // GetMacosVersion() doesn't work for the simulator. Let's instead check
+  // `dyldVersionNumber`, which is exported by dyld, against a known version
+  // number from the first OS release where this appeared.
+  return dyldVersionNumber < kMinDyldVersionWithAutoInterposition;
+}
+
+void MaybeReexec() {
+  if (reexec_disabled) return;
+
+  // Make sure the dynamic runtime library is preloaded so that the
+  // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
+  // ourselves.
+  Dl_info info;
+  CHECK(dladdr((void*)((uptr)&__sanitizer_report_error_summary), &info));
+  char *dyld_insert_libraries =
+      const_cast<char*>(GetEnv(kDyldInsertLibraries));
+  uptr old_env_len = dyld_insert_libraries ?
+      internal_strlen(dyld_insert_libraries) : 0;
+  uptr fname_len = internal_strlen(info.dli_fname);
+  const char *dylib_name = StripModuleName(info.dli_fname);
+  uptr dylib_name_len = internal_strlen(dylib_name);
+
+  bool lib_is_in_env = dyld_insert_libraries &&
+                       internal_strstr(dyld_insert_libraries, dylib_name);
+  if (DyldNeedsEnvVariable() && !lib_is_in_env) {
+    // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
+    // library.
+    InternalScopedString program_name(1024);
+    uint32_t buf_size = program_name.size();
+    _NSGetExecutablePath(program_name.data(), &buf_size);
+    char *new_env = const_cast<char*>(info.dli_fname);
+    if (dyld_insert_libraries) {
+      // Append the runtime dylib name to the existing value of
+      // DYLD_INSERT_LIBRARIES.
+      new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
+      internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
+      new_env[old_env_len] = ':';
+      // Copy fname_len and add a trailing zero.
+      internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
+                       fname_len + 1);
+      // Ok to use setenv() since the wrappers don't depend on the value of
+      // asan_inited.
+      setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
+    } else {
+      // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
+      setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
+    }
+    VReport(1, "exec()-ing the program with\n");
+    VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env);
+    VReport(1, "to enable wrappers.\n");
+    execv(program_name.data(), *_NSGetArgv());
+
+    // We get here only if execv() failed.
+    Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, "
+           "which is required for the sanitizer to work. We tried to set the "
+           "environment variable and re-execute itself, but execv() failed, "
+           "possibly because of sandbox restrictions. Make sure to launch the "
+           "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env);
+    CHECK("execv failed" && 0);
+  }
+
+  if (!lib_is_in_env)
+    return;
+
+  // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
+  // the dylib from the environment variable, because interceptors are installed
+  // and we don't want our children to inherit the variable.
+
+  uptr env_name_len = internal_strlen(kDyldInsertLibraries);
+  // Allocate memory to hold the previous env var name, its value, the '='
+  // sign and the '\0' char.
+  char *new_env = (char*)allocator_for_env.Allocate(
+      old_env_len + 2 + env_name_len);
+  CHECK(new_env);
+  internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
+  internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
+  new_env[env_name_len] = '=';
+  char *new_env_pos = new_env + env_name_len + 1;
+
+  // Iterate over colon-separated pieces of |dyld_insert_libraries|.
+  char *piece_start = dyld_insert_libraries;
+  char *piece_end = NULL;
+  char *old_env_end = dyld_insert_libraries + old_env_len;
+  do {
+    if (piece_start[0] == ':') piece_start++;
+    piece_end = internal_strchr(piece_start, ':');
+    if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
+    if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
+    uptr piece_len = piece_end - piece_start;
+
+    char *filename_start =
+        (char *)internal_memrchr(piece_start, '/', piece_len);
+    uptr filename_len = piece_len;
+    if (filename_start) {
+      filename_start += 1;
+      filename_len = piece_len - (filename_start - piece_start);
+    } else {
+      filename_start = piece_start;
+    }
+
+    // If the current piece isn't the runtime library name,
+    // append it to new_env.
+    if ((dylib_name_len != filename_len) ||
+        (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
+      if (new_env_pos != new_env + env_name_len + 1) {
+        new_env_pos[0] = ':';
+        new_env_pos++;
+      }
+      internal_strncpy(new_env_pos, piece_start, piece_len);
+      new_env_pos += piece_len;
+    }
+    // Move on to the next piece.
+    piece_start = piece_end;
+  } while (piece_start < old_env_end);
+
+  // Can't use setenv() here, because it requires the allocator to be
+  // initialized.
+  // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
+  // a separate function called after InitializeAllocator().
+  if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
+  LeakyResetEnv(kDyldInsertLibraries, new_env);
+}
+
 }  // namespace __sanitizer
 
 #endif  // SANITIZER_MAC
index 50dbe93226c271790f87d7527e6d9f94a9f53f54..6e2b84f432e5aa8e33423cc4ffa1afb88ac183d1 100644 (file)
@@ -13,6 +13,7 @@
 #ifndef SANITIZER_MAC_H
 #define SANITIZER_MAC_H
 
+#include "sanitizer_common.h"
 #include "sanitizer_platform.h"
 #if SANITIZER_MAC
 #include "sanitizer_posix.h"
@@ -37,5 +38,18 @@ char **GetEnviron();
 
 }  // namespace __sanitizer
 
+extern "C" {
+static char __crashreporter_info_buff__[kErrorMessageBufferSize] = {};
+static const char *__crashreporter_info__ __attribute__((__used__)) =
+  &__crashreporter_info_buff__[0];
+asm(".desc ___crashreporter_info__, 0x10");
+} // extern "C"
+static BlockingMutex crashreporter_info_mutex(LINKER_INITIALIZED);
+
+INLINE void CRAppendCrashLogMessage(const char *msg) {
+  BlockingMutexLock l(&crashreporter_info_mutex);
+  internal_strlcat(__crashreporter_info_buff__, msg,
+                   sizeof(__crashreporter_info_buff__)); }
+
 #endif  // SANITIZER_MAC
 #endif  // SANITIZER_MAC_H
index 33e9d96b8ce0e4c032791e556ee286542e531719..149857c168c673b1cd4f136ce059377bd35b0195 100644 (file)
 #include "sanitizer_common/sanitizer_mac.h"
 
 // Similar code is used in Google Perftools,
-// http://code.google.com/p/google-perftools.
+// https://github.com/gperftools/gperftools.
 
-// TODO(glider): do we need both zones?
-static malloc_zone_t *system_malloc_zone = 0;
 static malloc_zone_t sanitizer_zone;
 
 INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
@@ -58,7 +56,7 @@ INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
 
 INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
   // FIXME: ASan should support purgeable allocations.
-  // https://code.google.com/p/address-sanitizer/issues/detail?id=139
+  // https://github.com/google/sanitizers/issues/139
   COMMON_MALLOC_ENTER();
   return &sanitizer_zone;
 }
@@ -155,10 +153,7 @@ size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
 extern "C"
 SANITIZER_INTERFACE_ATTRIBUTE
 void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
-  if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
-    CHECK(system_malloc_zone);
-    return malloc_zone_malloc(system_malloc_zone, size);
-  }
+  COMMON_MALLOC_ENTER();
   COMMON_MALLOC_MALLOC(size);
   return p;
 }
@@ -184,36 +179,23 @@ void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
 extern "C"
 SANITIZER_INTERFACE_ATTRIBUTE
 void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
-  if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
-    CHECK(system_malloc_zone);
-    return malloc_zone_valloc(system_malloc_zone, size);
-  }
+  COMMON_MALLOC_ENTER();
   COMMON_MALLOC_VALLOC(size);
   return p;
 }
 
-#define GET_ZONE_FOR_PTR(ptr) \
-  malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
-  const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
-
-void ALWAYS_INLINE free_common(void *context, void *ptr) {
-  if (!ptr) return;
-  // FIXME: need to retire this flag.
-  if (!COMMON_MALLOC_IGNORE_INVALID_FREE) {
-    COMMON_MALLOC_FREE(ptr);
-  } else {
-    GET_ZONE_FOR_PTR(ptr);
-    COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name);
-  }
-}
-
 // TODO(glider): the allocation callbacks need to be refactored.
 extern "C"
 SANITIZER_INTERFACE_ATTRIBUTE
 void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
-  free_common(zone, ptr);
+  if (!ptr) return;
+  COMMON_MALLOC_FREE(ptr);
 }
 
+#define GET_ZONE_FOR_PTR(ptr) \
+  malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
+  const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
+
 extern "C"
 SANITIZER_INTERFACE_ATTRIBUTE
 void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
@@ -246,10 +228,7 @@ void __sanitizer_mz_destroy(malloc_zone_t* zone) {
 extern "C"
 SANITIZER_INTERFACE_ATTRIBUTE
 void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
-  if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
-    CHECK(system_malloc_zone);
-    return malloc_zone_memalign(system_malloc_zone, align, size);
-  }
+  COMMON_MALLOC_ENTER();
   COMMON_MALLOC_MEMALIGN(align, size);
   return p;
 }
index a1da6150fbeb29dd5dfe7cf8d14a9841fea8b25f..841cceb510a8b46dce8f3afc23a2e556822c542c 100644 (file)
 # define SANITIZER_X32 0
 #endif
 
-// VMA size definition for architecture that support multiple sizes.
-// AArch64 has 3 VMA sizes: 39, 42 and 48.
-#if !defined(SANITIZER_AARCH64_VMA)
-# define SANITIZER_AARCH64_VMA 39
-#else
-# if SANITIZER_AARCH64_VMA != 39 && SANITIZER_AARCH64_VMA != 42
-#  error "invalid SANITIZER_AARCH64_VMA size"
-# endif
-#endif
-
 // By default we allow to use SizeClassAllocator64 on 64-bit platform.
 // But in some cases (e.g. AArch64's 39-bit address space) SizeClassAllocator64
 // does not work well and we need to fallback to SizeClassAllocator32.
 // For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
 // change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
 #ifndef SANITIZER_CAN_USE_ALLOCATOR64
-# if defined(__mips64) || (defined(__aarch64__) && SANITIZER_AARCH64_VMA == 39)
+# if defined(__mips64) || defined(__aarch64__)
 #  define SANITIZER_CAN_USE_ALLOCATOR64 0
 # else
 #  define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64)
 #endif
 
 // The range of addresses which can be returned my mmap.
-// FIXME: this value should be different on different platforms,
-// e.g. on AArch64 it is most likely (1ULL << 39). Larger values will still work
-// but will consume more memory for TwoLevelByteMap.
-#if defined(__aarch64__)
-# if SANITIZER_AARCH64_VMA == 39
-#  define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 39)
-# elif SANITIZER_AARCH64_VMA == 42
-#  define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 42)
-# endif
-#elif defined(__mips__)
+// FIXME: this value should be different on different platforms.  Larger values
+// will still work but will consume more memory for TwoLevelByteMap.
+#if defined(__mips__)
 # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
 #else
 # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
 #define SANITIZER_USES_UID16_SYSCALLS 0
 #endif
 
-#if defined(__mips__) || (defined(__aarch64__) && SANITIZER_AARCH64_VMA == 39)
+#if defined(__mips__)
 # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10)
-#elif defined(__aarch64__) && SANITIZER_AARCH64_VMA == 42
-# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 11)
 #else
 # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
 #endif
index 89bfe98897910d6a09e3d8a10eb6386ce000b737..430ad4839809095f75e50e89443e58a2b4472cea 100644 (file)
 #define SANITIZER_INTERCEPT_PTHREAD_SETCANCEL SI_NOT_WINDOWS
 #define SANITIZER_INTERCEPT_MINCORE SI_LINUX
 #define SANITIZER_INTERCEPT_PROCESS_VM_READV SI_LINUX
+#define SANITIZER_INTERCEPT_CTERMID SI_LINUX || SI_MAC || SI_FREEBSD
+#define SANITIZER_INTERCEPT_CTERMID_R SI_MAC || SI_FREEBSD
 
 #define SANITIZER_INTERCEPTOR_HOOKS SI_LINUX
 
index 7aebc9c00bbef793ebeed693282c33f833bb626f..b642cba0fede007069ef482380ac5029067d5733 100644 (file)
 #  include <asm/ptrace.h>
 #  ifdef __arm__
 typedef struct user_fpregs elf_fpregset_t;
+#   define ARM_VFPREGS_SIZE_ASAN (32 * 8 /*fpregs*/ + 4 /*fpscr*/)
+#   if !defined(ARM_VFPREGS_SIZE)
+#     define ARM_VFPREGS_SIZE ARM_VFPREGS_SIZE_ASAN
+#   endif
 #  endif
 # endif
 # include <semaphore.h>
@@ -333,21 +337,24 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
   int ptrace_peektext = PTRACE_PEEKTEXT;
   int ptrace_peekdata = PTRACE_PEEKDATA;
   int ptrace_peekuser = PTRACE_PEEKUSER;
-#if defined(PT_GETREGS) && defined(PT_SETREGS)
+#if (defined(PTRACE_GETREGS) && defined(PTRACE_SETREGS)) || \
+    (defined(PT_GETREGS) && defined(PT_SETREGS))
   int ptrace_getregs = PTRACE_GETREGS;
   int ptrace_setregs = PTRACE_SETREGS;
 #else
   int ptrace_getregs = -1;
   int ptrace_setregs = -1;
 #endif
-#if defined(PT_GETFPREGS) && defined(PT_SETFPREGS)
+#if (defined(PTRACE_GETFPREGS) && defined(PTRACE_SETFPREGS)) || \
+    (defined(PT_GETFPREGS) && defined(PT_SETFPREGS))
   int ptrace_getfpregs = PTRACE_GETFPREGS;
   int ptrace_setfpregs = PTRACE_SETFPREGS;
 #else
   int ptrace_getfpregs = -1;
   int ptrace_setfpregs = -1;
 #endif
-#if defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS)
+#if (defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS)) || \
+    (defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS))
   int ptrace_getfpxregs = PTRACE_GETFPXREGS;
   int ptrace_setfpxregs = PTRACE_SETFPXREGS;
 #else
@@ -1261,4 +1268,8 @@ CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close);
 CHECK_TYPE_SIZE(sem_t);
 #endif
 
+#if SANITIZER_LINUX && defined(__arm__)
+COMPILER_CHECK(ARM_VFPREGS_SIZE == ARM_VFPREGS_SIZE_ASAN);
+#endif
+
 #endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
index 9ad66bec2ad3d3def22c85b1fba582b1d2b4a80a..5ae68663df0eb68ee940a79e2d2d8d45086ab449 100644 (file)
@@ -112,14 +112,14 @@ uptr GetMaxVirtualAddress() {
 #endif  // SANITIZER_WORDSIZE
 }
 
-void *MmapOrDie(uptr size, const char *mem_type) {
+void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
   size = RoundUpTo(size, GetPageSizeCached());
   uptr res = internal_mmap(nullptr, size,
                            PROT_READ | PROT_WRITE,
                            MAP_PRIVATE | MAP_ANON, -1, 0);
   int reserrno;
   if (internal_iserror(res, &reserrno))
-    ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
+    ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno, raw_report);
   IncreaseTotalMmap(size);
   return (void *)res;
 }
@@ -321,22 +321,6 @@ SignalContext SignalContext::Create(void *siginfo, void *context) {
   return SignalContext(context, addr, pc, sp, bp);
 }
 
-// This function check is the built VMA matches the runtime one for
-// architectures with multiple VMA size.
-void CheckVMASize() {
-#ifdef __aarch64__
-  static const unsigned kBuiltVMA = SANITIZER_AARCH64_VMA;
-  unsigned maxRuntimeVMA =
-    (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
-  if (kBuiltVMA != maxRuntimeVMA) {
-    Printf("WARNING: %s runtime VMA is not the one built for.\n",
-      SanitizerToolName);
-    Printf("\tBuilt VMA:   %u bits\n", kBuiltVMA);
-    Printf("\tRuntime VMA: %u bits\n", maxRuntimeVMA);
-  }
-#endif
-}
-
 } // namespace __sanitizer
 
 #endif // SANITIZER_POSIX
index 19fa505c5253b2c76bf62a069cd024eac9592604..c0426a0b23fa3bc8f0774927327a2e05ec58125b 100644 (file)
@@ -54,6 +54,7 @@ uptr internal_ptrace(int request, int pid, void *addr, void *data);
 uptr internal_waitpid(int pid, int *status, int options);
 
 int internal_fork();
+int internal_forkpty(int *amaster);
 
 // These functions call appropriate pthread_ functions directly, bypassing
 // the interceptor. They are weak and may not be present in some tools.
index d7b9f20414cfdb44749acdf7d4f0e0baaa0f92c0..c158eedae0e3380af5bd7fdb9946cbe10f8fb454 100644 (file)
@@ -190,6 +190,7 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) {
   MaybeInstallSigaction(SIGBUS, handler);
   MaybeInstallSigaction(SIGABRT, handler);
   MaybeInstallSigaction(SIGFPE, handler);
+  MaybeInstallSigaction(SIGILL, handler);
 }
 #endif  // SANITIZER_GO
 
@@ -228,7 +229,7 @@ void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
 #endif
 }
 
-#if SANITIZER_ANDROID
+#if SANITIZER_ANDROID || SANITIZER_GO
 int GetNamedMappingFd(const char *name, uptr size) {
   return -1;
 }
index 5f4725ec1d2c9b13e30cf307c5fbc4b4ef450920..434ebb93dffa5b2aa7467a38733aaa36881cb3c0 100644 (file)
@@ -278,9 +278,12 @@ static void SharedPrintfCode(bool append_pid, const char *format,
 #   undef CHECK_NEEDED_LENGTH
   }
   RawWrite(buffer);
-  if (common_flags()->log_to_syslog)
-    WriteToSyslog(buffer);
+
+  // Remove color sequences from the message.
+  RemoveANSIEscapeSequencesFromString(buffer);
   CallPrintfAndReportCallback(buffer);
+  LogMessageOnPrintf(buffer);
+
   // If we had mapped any memory, clean up.
   if (buffer != local_buffer)
     UnmapOrDie((void *)buffer, buffer_size);
index 0e97f96331e5d9724cebcaec0aa8da018581687b..b6fb7034ded4e537f8031d592248db7bc25b144b 100644 (file)
@@ -67,7 +67,7 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
   while (IsDecimal(*current_))
     current_++;
   // Qemu may lack the trailing space.
-  // http://code.google.com/p/address-sanitizer/issues/detail?id=160
+  // https://github.com/google/sanitizers/issues/160
   // CHECK_EQ(*current_++, ' ');
   // Skip spaces.
   while (current_ < next_line && *current_ == ' ')
index 29d699609a6ee1a7803f963e02daff6cbe929ae3..d10881e8a7f8e6c0c5e2563f80f60e8ba95e69aa 100644 (file)
@@ -65,7 +65,7 @@ void MemoryMappingLayout::LoadFromCache() {
 }
 
 // Next and NextSegmentLoad were inspired by base/sysinfo.cc in
-// Google Perftools, http://code.google.com/p/google-perftools.
+// Google Perftools, https://github.com/gperftools/gperftools.
 
 // NextSegmentLoad scans the current image for the next segment load command
 // and returns the start and end addresses and file offset of the corresponding
index 80c053164a94732b2cf9ad6b23a07b8daa2befba..7862575b37bbd22d548d279e902688f02a2f8584 100644 (file)
@@ -118,7 +118,7 @@ void BufferedStackTrace::PopStackFrames(uptr count) {
 uptr BufferedStackTrace::LocatePcInTrace(uptr pc) {
   // Use threshold to find PC in stack trace, as PC we want to unwind from may
   // slightly differ from return address in the actual unwinded stack trace.
-  const int kPcThreshold = 304;
+  const int kPcThreshold = 320;
   for (uptr i = 0; i < size; ++i) {
     if (MatchPc(pc, trace[i], kPcThreshold))
       return i;
index bf5aa0e7bb3edd4bf1976d3403f077c9ece326a0..969cedb165c858f7211ca43de5f9a181cac8a06f 100644 (file)
@@ -29,7 +29,7 @@ static const u32 kStackTraceMax = 256;
 
 // Fast unwind is the only option on Mac for now; we will need to
 // revisit this macro when slow unwind works on Mac, see
-// https://code.google.com/p/address-sanitizer/issues/detail?id=137
+// https://github.com/google/sanitizers/issues/137
 #if SANITIZER_MAC
 # define SANITIZER_CAN_SLOW_UNWIND 0
 #else
index 6fd63126f0e865fd19ac4d1befa9db0d22ad3b8a..2376ee56e1d703f1bef8d71a595a9d83297669ce 100644 (file)
@@ -15,7 +15,7 @@
 #include "sanitizer_platform.h"
 
 #if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \
-                        defined(__aarch64__))
+                        defined(__aarch64__) || defined(__powerpc64__))
 
 #include "sanitizer_stoptheworld.h"
 
@@ -511,5 +511,5 @@ uptr SuspendedThreadsList::RegisterCount() {
 }
 } // namespace __sanitizer
 
-#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
-       // || defined(__aarch64__)
+#endif  // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
+        // || defined(__aarch64__) || defined(__powerpc64__)
index 46d46381331f8d4eeb07b3f2d50a4299067e855d..f0f2c9c725a1303940ca273c4e92f74c91f90abc 100644 (file)
@@ -127,7 +127,7 @@ void SuppressionContext::Parse(const char *str) {
         Printf("%s: failed to parse suppressions\n", SanitizerToolName);
         Die();
       }
-      Suppression s = {};
+      Suppression s;
       s.type = suppression_types_[type];
       s.templ = (char*)InternalAlloc(end2 - line + 1);
       internal_memcpy(s.templ, line, end2 - line);
index 03f6c87261e6bf58bc15704bf93265c43e8ab319..0ca875a2dde6518e166f6d52f5267bde3ddaf299 100644 (file)
@@ -20,6 +20,7 @@
 namespace __sanitizer {
 
 struct Suppression {
+  Suppression() { internal_memset(this, 0, sizeof(*this)); }
   const char *type;
   char *templ;
   atomic_uint32_t hit_count;
@@ -42,7 +43,7 @@ class SuppressionContext {
   void GetMatched(InternalMmapVector<Suppression *> *matched);
 
  private:
-  static const int kMaxSuppressionTypes = 16;
+  static const int kMaxSuppressionTypes = 32;
   const char **const suppression_types_;
   const int suppression_types_num_;
 
index aeaa09b0bdbc0784df68271967603bf480c2c32f..64048fa7e58ea4c575390c87183caab41eaba406 100644 (file)
@@ -33,12 +33,18 @@ bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
   int result = dladdr((const void *)addr, &info);
   if (!result) return false;
   const char *demangled = DemangleCXXABI(info.dli_sname);
-  stack->info.function = internal_strdup(demangled);
+  stack->info.function = demangled ? internal_strdup(demangled) : nullptr;
   return true;
 }
 
-bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
-  return false;
+bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
+  Dl_info info;
+  int result = dladdr((const void *)addr, &info);
+  if (!result) return false;
+  const char *demangled = DemangleCXXABI(info.dli_sname);
+  datainfo->name = internal_strdup(demangled);
+  datainfo->start = (uptr)info.dli_saddr;
+  return true;
 }
 
 class AtosSymbolizerProcess : public SymbolizerProcess {
@@ -90,7 +96,9 @@ static bool IsAtosErrorMessage(const char *str) {
   return false;
 }
 
-static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
+static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
+                               char **out_module, char **out_file, uptr *line,
+                               uptr *start_address) {
   // Trim ending newlines.
   char *trim;
   ExtractTokenUpToDelimiter(str, "\n", &trim);
@@ -98,7 +106,9 @@ static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
   // The line from `atos` is in one of these formats:
   //   myfunction (in library.dylib) (sourcefile.c:17)
   //   myfunction (in library.dylib) + 0x1fe
+  //   myfunction (in library.dylib) + 15
   //   0xdeadbeef (in library.dylib) + 0x1fe
+  //   0xdeadbeef (in library.dylib) + 15
   //   0xdeadbeef (in library.dylib)
   //   0xdeadbeef
 
@@ -109,21 +119,33 @@ static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
   }
 
   const char *rest = trim;
-  char *function_name;
-  rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name);
-  if (internal_strncmp(function_name, "0x", 2) != 0)
-    res->info.function = function_name;
+  char *symbol_name;
+  rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
+  if (rest[0] == '\0') {
+    InternalFree(symbol_name);
+    InternalFree(trim);
+    return false;
+  }
+
+  if (internal_strncmp(symbol_name, "0x", 2) != 0)
+    *out_name = symbol_name;
   else
-    InternalFree(function_name);
-  rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module);
+    InternalFree(symbol_name);
+  rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
 
   if (rest[0] == '(') {
-    rest++;
-    rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file);
-    char *extracted_line_number;
-    rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
-    res->info.line = internal_atoll(extracted_line_number);
-    InternalFree(extracted_line_number);
+    if (out_file) {
+      rest++;
+      rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
+      char *extracted_line_number;
+      rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
+      if (line) *line = (uptr)internal_atoll(extracted_line_number);
+      InternalFree(extracted_line_number);
+    }
+  } else if (rest[0] == '+') {
+    rest += 2;
+    uptr offset = internal_atoll(rest);
+    if (start_address) *start_address = addr - offset;
   }
 
   InternalFree(trim);
@@ -139,14 +161,29 @@ bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
   internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
   const char *buf = process_->SendCommand(command);
   if (!buf) return false;
-  if (!ParseCommandOutput(buf, stack)) {
+  uptr line;
+  if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
+                          &stack->info.file, &line, nullptr)) {
     process_ = nullptr;
     return false;
   }
+  stack->info.line = (int)line;
   return true;
 }
 
-bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; }
+bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+  if (!process_) return false;
+  char command[32];
+  internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+  const char *buf = process_->SendCommand(command);
+  if (!buf) return false;
+  if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
+                          nullptr, &info->start)) {
+    process_ = nullptr;
+    return false;
+  }
+  return true;
+}
 
 }  // namespace __sanitizer
 
index a19b0a678629dbd4da5c9ffb6886d9ba805b7871..fc8a7d91ac73a7b362e8bf511fc6828eec7bd8d6 100644 (file)
@@ -75,7 +75,7 @@ bool SymbolizerProcess::StartSymbolizerSubprocess() {
 #if SANITIZER_MAC
     fd_t fd = kInvalidFd;
     // Use forkpty to disable buffering in the new terminal.
-    pid = forkpty(&fd, 0, 0, 0);
+    pid = internal_forkpty(&fd);
     if (pid == -1) {
       // forkpty() failed.
       Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
@@ -452,10 +452,6 @@ static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
   VReport(2, "Using dladdr symbolizer.\n");
   list->push_back(new(*allocator) DlAddrSymbolizer());
 #endif  // SANITIZER_MAC
-
-  if (list->size() == 0) {
-    Report("WARNING: no internal or external symbolizer found.\n");
-  }
 }
 
 Symbolizer *Symbolizer::PlatformInit() {
index ea037159d00befe0e7ddb4383c43e85fcaf00961..213aced89da706b4ccf88ca6e7b6fe2109ebc496 100644 (file)
@@ -78,6 +78,15 @@ void DTLS_Destroy() {
   DTLS_Deallocate(dtls.dtv, s);
 }
 
+#if defined(__powerpc64__)
+// This is glibc's TLS_DTV_OFFSET:
+// "Dynamic thread vector pointers point 0x8000 past the start of each
+//  TLS block."
+static const uptr kDtvOffset = 0x8000;
+#else
+static const uptr kDtvOffset = 0;
+#endif
+
 DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
                                 uptr static_tls_begin, uptr static_tls_end) {
   if (!common_flags()->intercept_tls_get_addr) return 0;
@@ -87,7 +96,7 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
   DTLS_Resize(dso_id + 1);
   if (dtls.dtv[dso_id].beg) return 0;
   uptr tls_size = 0;
-  uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset;
+  uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset;
   VPrintf(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p "
              "num_live_dtls %zd\n",
           arg, arg->dso_id, arg->offset, res, tls_beg, &tls_beg,
index bcebce0b29468c10be8f952f65869fc7dc078192..861261d8402c3f5b3608551a325800fe187b7f0a 100644 (file)
@@ -83,10 +83,11 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
 }
 #endif  // #if !SANITIZER_GO
 
-void *MmapOrDie(uptr size, const char *mem_type) {
+void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
   void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
   if (rv == 0)
-    ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError());
+    ReportMmapFailureAndDie(size, mem_type, "allocate",
+                            GetLastError(), raw_report);
   return rv;
 }
 
@@ -220,12 +221,14 @@ struct ModuleInfo {
   uptr end_address;
 };
 
+#ifndef SANITIZER_GO
 int CompareModulesBase(const void *pl, const void *pr) {
   const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr;
   if (l->base_address < r->base_address)
     return -1;
   return l->base_address > r->base_address;
 }
+#endif
 }  // namespace
 
 #ifndef SANITIZER_GO
@@ -366,6 +369,7 @@ static uptr GetPreferredBase(const char *modname) {
   return (uptr)pe_header->ImageBase;
 }
 
+#ifndef SANITIZER_GO
 uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
                       string_predicate_t filter) {
   HANDLE cur_process = GetCurrentProcess();
@@ -434,7 +438,6 @@ uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
   return count;
 };
 
-#ifndef SANITIZER_GO
 // We can't use atexit() directly at __asan_init time as the CRT is not fully
 // initialized at this point.  Place the functions into a vector and use
 // atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers).
@@ -759,6 +762,14 @@ void CheckVMASize() {
   // Do nothing.
 }
 
+void DisableReexec() {
+  // No need to re-exec on Windows.
+}
+
+void MaybeReexec() {
+  // No need to re-exec on Windows.
+}
+
 }  // namespace __sanitizer
 
 #endif  // _WIN32
index c1bb797db203cf711429ff0230d634a8abf66b91..6fc308ad14d455a1dd8f66108393a7b7fd1db7af 100644 (file)
@@ -208,6 +208,30 @@ TEST(SanitizerCommon, StripPathPrefix) {
   EXPECT_STREQ("file.h", StripPathPrefix("/usr/lib/./file.h", "/usr/lib/"));
 }
 
+TEST(SanitizerCommon, RemoveANSIEscapeSequencesFromString) {
+  RemoveANSIEscapeSequencesFromString(nullptr);
+  const char *buffs[22] = {
+    "Default",                                "Default",
+    "\033[95mLight magenta",                  "Light magenta",
+    "\033[30mBlack\033[32mGreen\033[90mGray", "BlackGreenGray",
+    "\033[106mLight cyan \033[107mWhite ",    "Light cyan White ",
+    "\033[31mHello\033[0m World",             "Hello World",
+    "\033[38;5;82mHello \033[38;5;198mWorld", "Hello World",
+    "123[653456789012",                       "123[653456789012",
+    "Normal \033[5mBlink \033[25mNormal",     "Normal Blink Normal",
+    "\033[106m\033[107m",                     "",
+    "",                                       "",
+    " ",                                      " ",
+  };
+
+  for (size_t i = 0; i < ARRAY_SIZE(buffs); i+=2) {
+    char *buffer_copy = internal_strdup(buffs[i]);
+    RemoveANSIEscapeSequencesFromString(buffer_copy);
+    EXPECT_STREQ(buffer_copy, buffs[i+1]);
+    InternalFree(buffer_copy);
+  }
+}
+
 TEST(SanitizerCommon, InternalScopedString) {
   InternalScopedString str(10);
   EXPECT_EQ(0U, str.length());
index c0230ec6a70b32707e5c5cda6448b5e612c7c8a7..015e32a09e3733c44cc7578267b84ffa45ad9988 100644 (file)
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 // Tests for sanitizer_libc.h.
 //===----------------------------------------------------------------------===//
+#include <algorithm>
 
 #include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_libc.h"
@@ -15,7 +16,9 @@
 #include "gtest/gtest.h"
 
 #if SANITIZER_WINDOWS
+#define NOMINMAX
 #include <windows.h>
+#undef NOMINMAX
 #endif
 #if SANITIZER_POSIX
 # include <sys/stat.h>
@@ -145,12 +148,65 @@ TEST(SanitizerCommon, FileOps) {
 #endif
 }
 
+static const size_t kStrlcpyBufSize = 8;
+void test_internal_strlcpy(char *dbuf, const char *sbuf) {
+  uptr retval = 0;
+  retval = internal_strlcpy(dbuf, sbuf, kStrlcpyBufSize);
+  EXPECT_EQ(internal_strncmp(dbuf, sbuf, kStrlcpyBufSize - 1), 0);
+  EXPECT_EQ(internal_strlen(dbuf),
+            std::min(internal_strlen(sbuf), (uptr)(kStrlcpyBufSize - 1)));
+  EXPECT_EQ(retval, internal_strlen(sbuf));
+
+  // Test with shorter maxlen.
+  uptr maxlen = 2;
+  if (internal_strlen(sbuf) > maxlen) {
+    retval = internal_strlcpy(dbuf, sbuf, maxlen);
+    EXPECT_EQ(internal_strncmp(dbuf, sbuf, maxlen - 1), 0);
+    EXPECT_EQ(internal_strlen(dbuf), maxlen - 1);
+  }
+}
+
 TEST(SanitizerCommon, InternalStrFunctions) {
   const char *haystack = "haystack";
   EXPECT_EQ(haystack + 2, internal_strchr(haystack, 'y'));
   EXPECT_EQ(haystack + 2, internal_strchrnul(haystack, 'y'));
   EXPECT_EQ(0, internal_strchr(haystack, 'z'));
   EXPECT_EQ(haystack + 8, internal_strchrnul(haystack, 'z'));
+
+  char dbuf[kStrlcpyBufSize] = {};
+  const char *samesizestr = "1234567";
+  const char *shortstr = "123";
+  const char *longerstr = "123456789";
+
+  // Test internal_strlcpy.
+  internal_strlcpy(dbuf, shortstr, 0);
+  EXPECT_EQ(dbuf[0], 0);
+  EXPECT_EQ(dbuf[0], 0);
+  test_internal_strlcpy(dbuf, samesizestr);
+  test_internal_strlcpy(dbuf, shortstr);
+  test_internal_strlcpy(dbuf, longerstr);
+
+  // Test internal_strlcat.
+  char dcatbuf[kStrlcpyBufSize] = {};
+  uptr retval = 0;
+  retval = internal_strlcat(dcatbuf, "aaa", 0);
+  EXPECT_EQ(internal_strlen(dcatbuf), (uptr)0);
+  EXPECT_EQ(retval, (uptr)3);
+
+  retval = internal_strlcat(dcatbuf, "123", kStrlcpyBufSize);
+  EXPECT_EQ(internal_strcmp(dcatbuf, "123"), 0);
+  EXPECT_EQ(internal_strlen(dcatbuf), (uptr)3);
+  EXPECT_EQ(retval, (uptr)3);
+
+  retval = internal_strlcat(dcatbuf, "123", kStrlcpyBufSize);
+  EXPECT_EQ(internal_strcmp(dcatbuf, "123123"), 0);
+  EXPECT_EQ(internal_strlen(dcatbuf), (uptr)6);
+  EXPECT_EQ(retval, (uptr)6);
+
+  retval = internal_strlcat(dcatbuf, "123", kStrlcpyBufSize);
+  EXPECT_EQ(internal_strcmp(dcatbuf, "1231231"), 0);
+  EXPECT_EQ(internal_strlen(dcatbuf), (uptr)7);
+  EXPECT_EQ(retval, (uptr)9);
 }
 
 // FIXME: File manipulations are not yet supported on Windows
index 654ea1db82fb3ffef89be8d2cb551d77375195b6..3d57eded948ffe054622bc4c825abaa48a873d45 100644 (file)
@@ -82,7 +82,7 @@ TEST_F(FastUnwindTest, Basic) {
   }
 }
 
-// From: http://code.google.com/p/address-sanitizer/issues/detail?id=162
+// From: https://github.com/google/sanitizers/issues/162
 TEST_F(FastUnwindTest, FramePointerLoop) {
   // Make one fp point to itself.
   fake_stack[4] = (uhwptr)&fake_stack[4];
diff --git a/src/compiler-rt/lib/tsan/.clang-format b/src/compiler-rt/lib/tsan/.clang-format
new file mode 100644 (file)
index 0000000..f6cb8ad
--- /dev/null
@@ -0,0 +1 @@
+BasedOnStyle: Google
index cbfca5ce281b1233321ab704b360557570749e3d..c185cfa164077612bb58c183bdf33328fe96e159 100644 (file)
@@ -8,13 +8,19 @@ set(TSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
 append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE TSAN_CFLAGS)
 append_no_rtti_flag(TSAN_CFLAGS)
 
+if(COMPILER_RT_TSAN_DEBUG_OUTPUT)
+  # Add extra debug information to TSan runtime. This configuration is rarely
+  # used, but we need to support it so that debug output will not bitrot.
+  list(APPEND TSAN_CFLAGS -DTSAN_COLLECT_STATS=1
+                          -DTSAN_DEBUG_OUTPUT=2)
+endif()
+
 set(TSAN_RTL_CFLAGS ${TSAN_CFLAGS})
 append_list_if(COMPILER_RT_HAS_MSSE3_FLAG -msse3 TSAN_RTL_CFLAGS)
 append_list_if(SANITIZER_LIMIT_FRAME_SIZE -Wframe-larger-than=512
                TSAN_RTL_CFLAGS)
 append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors
                TSAN_RTL_CFLAGS)
-# FIXME: Add support for --sysroot=. compile flag:
 
 set(TSAN_SOURCES
   rtl/tsan_clock.cc
@@ -47,6 +53,8 @@ set(TSAN_CXX_SOURCES
 
 if(APPLE)
   list(APPEND TSAN_SOURCES
+    rtl/tsan_interceptors_mac.cc
+    rtl/tsan_libdispatch_mac.cc
     rtl/tsan_platform_mac.cc
     rtl/tsan_platform_posix.cc)
 elseif(UNIX)
@@ -88,17 +96,40 @@ set(TSAN_RUNTIME_LIBRARIES)
 add_custom_target(tsan)
 
 if(APPLE)
+  set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S)
+  # Xcode will try to compile this file as C ('clang -x c'), and that will fail.
+  if (${CMAKE_GENERATOR} STREQUAL "Xcode")
+    enable_language(ASM)
+  else()
+    # Pass ASM file directly to the C++ compiler.
+    set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES LANGUAGE C)
+  endif()
   add_compiler_rt_runtime(clang_rt.tsan
     SHARED
     OS ${TSAN_SUPPORTED_OS}
     ARCHS ${TSAN_SUPPORTED_ARCH}
-    SOURCES ${TSAN_SOURCES} ${TSAN_CXX_SOURCES}
+    SOURCES ${TSAN_SOURCES} ${TSAN_CXX_SOURCES} ${TSAN_ASM_SOURCES}
     OBJECT_LIBS RTInterception
                 RTSanitizerCommon
                 RTSanitizerCommonLibc
                 RTUbsan
     CFLAGS ${TSAN_RTL_CFLAGS}
     PARENT_TARGET tsan)
+  add_compiler_rt_object_libraries(RTTsan_dynamic 
+    OS ${TSAN_SUPPORTED_OS}
+    ARCHS ${TSAN_SUPPORTED_ARCH}
+    SOURCES ${TSAN_SOURCES} ${TSAN_CXX_SOURCES} ${TSAN_ASM_SOURCES}
+    CFLAGS ${TSAN_RTL_CFLAGS})
+
+  # Build and check Go runtime.
+  set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh)
+  add_custom_target(GotsanRuntimeCheck
+    COMMAND env "CC=${CMAKE_C_COMPILER} ${OSX_SYSROOT_FLAG}"
+            IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT}
+    DEPENDS tsan ${BUILDGO_SCRIPT}
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go
+    COMMENT "Checking TSan Go runtime..."
+    VERBATIM)
 else()
   foreach(arch ${TSAN_SUPPORTED_ARCH})
     if(arch STREQUAL "x86_64")
@@ -115,6 +146,16 @@ else()
         WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go
         COMMENT "Checking TSan Go runtime..."
         VERBATIM)
+    elseif(arch STREQUAL "aarch64")
+      set(TSAN_ASM_SOURCES rtl/tsan_rtl_aarch64.S)
+      # Pass ASM file directly to the C++ compiler.
+      set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES
+        LANGUAGE C)
+   elseif(arch MATCHES "powerpc64|powerpc64le")
+     set(TSAN_ASM_SOURCES rtl/tsan_rtl_ppc64.S)
+     # Pass ASM file directly to the C++ compiler.
+     set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES
+       LANGUAGE C)
     else()
       set(TSAN_ASM_SOURCES)
     endif()
@@ -150,13 +191,30 @@ endif()
 
 add_dependencies(compiler-rt tsan)
 
+# Make sure that non-platform-specific files don't include any system headers.
+if(COMPILER_RT_HAS_SYSROOT_FLAG)
+  file(GLOB _tsan_generic_sources rtl/tsan*)
+  file(GLOB _tsan_platform_sources rtl/tsan*posix* rtl/tsan*mac*
+                                   rtl/tsan*linux*)
+  list(REMOVE_ITEM _tsan_generic_sources ${_tsan_platform_sources})
+  set_source_files_properties(${_tsan_generic_sources}
+    PROPERTIES COMPILE_FLAGS "--sysroot=.")
+endif()
+
 # Build libcxx instrumented with TSan.
 if(COMPILER_RT_HAS_LIBCXX_SOURCES AND
    COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang")
-  set(LIBCXX_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_tsan)
-  add_custom_libcxx(libcxx_tsan ${LIBCXX_PREFIX}
-    DEPS ${TSAN_RUNTIME_LIBRARIES}
-    CFLAGS -fsanitize=thread)
+  set(libcxx_tsan_deps)
+  foreach(arch ${TSAN_SUPPORTED_ARCH})
+    get_target_flags_for_arch(${arch} TARGET_CFLAGS)
+    set(LIBCXX_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_tsan_${arch})
+    add_custom_libcxx(libcxx_tsan_${arch} ${LIBCXX_PREFIX}
+      DEPS ${TSAN_RUNTIME_LIBRARIES}
+      CFLAGS ${TARGET_CFLAGS} -fsanitize=thread)
+    list(APPEND libcxx_tsan_deps libcxx_tsan_${arch})
+  endforeach()
+
+  add_custom_target(libcxx_tsan DEPENDS ${libcxx_tsan_deps})
 endif()
 
 if(COMPILER_RT_INCLUDE_TESTS)
diff --git a/src/compiler-rt/lib/tsan/Makefile.old b/src/compiler-rt/lib/tsan/Makefile.old
deleted file mode 100644 (file)
index b2ac912..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-DEBUG=0
-LDFLAGS=-ldl -lrt -lpthread -pie
-CXXFLAGS = -std=c++11 -fPIE -fno-rtti -g -Wall -Werror \
-                                        -DGTEST_HAS_RTTI=0 -DSANITIZER_DEBUG=$(DEBUG) \
-                                        -DTSAN_CONTAINS_UBSAN=0
-CLANG=clang
-FILECHECK=FileCheck
-# Silence warnings that Clang produces for gtest code.
-# Use -Wno-attributes so that gcc doesn't complain about unknown warning types.
-CXXFLAGS += -Wno-attributes
-ifeq ($(DEBUG), 0)
-       CXXFLAGS += -O3
-endif
-ifeq ($(CXX), $(CLANG)++)
-  CXXFLAGS += -Wno-unused-private-field -Wno-static-in-inline -Wgnu
-else
-  CXXFLAGS += -Wno-maybe-uninitialized
-endif
-
-LIBTSAN=rtl/libtsan.a
-GTEST_ROOT=third_party/googletest
-GTEST_INCLUDE=-I$(GTEST_ROOT)/include
-GTEST_BUILD_DIR=$(GTEST_ROOT)/build
-GTEST_LIB_NAME=gtest-all.o
-GTEST_LIB=$(GTEST_BUILD_DIR)/$(GTEST_LIB_NAME)
-
-SANITIZER_TESTS_PATH=../sanitizer_common/tests
-SANITIZER_COMMON_TESTS_SRC=$(wildcard $(SANITIZER_TESTS_PATH)/*_test.cc)
-SANITIZER_COMMON_EXCLUDED_TESTS=$(SANITIZER_TESTS_PATH)/sanitizer_nolibc_test.cc
-SANITIZER_COMMON_GOOD_TESTS=$(filter-out $(SANITIZER_COMMON_EXCLUDED_TESTS), $(SANITIZER_COMMON_TESTS_SRC))
-SANITIZER_COMMON_TESTS_OBJ=$(patsubst %.cc,%.o,$(SANITIZER_COMMON_GOOD_TESTS))
-RTL_TEST_SRC=$(wildcard tests/rtl/*.cc)
-RTL_TEST_OBJ=$(patsubst %.cc,%.o,$(RTL_TEST_SRC))
-UNIT_TEST_SRC=$(wildcard tests/unit/*_test.cc)
-UNIT_TEST_OBJ=$(patsubst %.cc,%.o,$(UNIT_TEST_SRC))
-UNIT_TEST_HDR=$(wildcard rtl/*.h) $(wildcard ../sanitizer_common/*.h)
-LIT_TESTS_PATH=../../test/tsan
-
-INCLUDES=-Irtl -I.. -I../../include $(GTEST_INCLUDE)
-
-all: libtsan test
-
-help:
-       @ echo "A little help is always welcome!"
-       @ echo "The most useful targets are:"
-       @ echo "  make install_deps  # Install third-party dependencies required for building"
-       @ echo "  make presubmit     # Run it every time before committing"
-       @ echo
-       @ echo "For more info, see http://code.google.com/p/thread-sanitizer/wiki/Development"
-
-$(LIBTSAN): libtsan
-
-libtsan:
-       $(MAKE) -C rtl -f Makefile.old DEBUG=$(DEBUG)
-
-%.o: %.cc $(UNIT_TEST_HDR) $(LIBTSAN)
-       $(CXX) $(CXXFLAGS) $(CFLAGS) $(INCLUDES) -o $@ -c $<
-
-tsan_test: $(UNIT_TEST_OBJ) $(RTL_TEST_OBJ) \
-           $(SANITIZER_COMMON_TESTS_OBJ) $(LIBTSAN) $(GTEST_LIB)
-       $(CXX) -Wl,--whole-archive $^ -Wl,--no-whole-archive -o $@ $(LDFLAGS)
-
-test: libtsan tsan_test
-
-run: all
-       (ulimit -s 8192; ./tsan_test)
-       CC=$(CLANG) CXX=$(CLANG)++ FILECHECK=$(FILECHECK) $(LIT_TESTS_PATH)/test_output.sh
-
-presubmit:
-       ../sanitizer_common/scripts/check_lint.sh
-       # Debug build with clang.
-       $(MAKE) -f Makefile.old clean
-       $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=$(CLANG) CXX=$(CLANG)++
-       # Release build with clang.
-       $(MAKE) -f Makefile.old clean
-       $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=$(CLANG) CXX=$(CLANG)++
-       ./check_memcpy.sh
-       # Debug build with gcc
-       $(MAKE) -f Makefile.old clean
-       $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=gcc CXX=g++
-       # Release build with gcc
-       $(MAKE) -f Makefile.old clean
-       $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=gcc CXX=g++
-       ./check_memcpy.sh
-       ./check_analyze.sh
-       # Sanity check for Go runtime
-       (cd go && ./buildgo.sh)
-       # Check cmake build
-       ./check_cmake.sh
-       @ echo PRESUBMIT PASSED
-
-install_deps:
-       rm -rf third_party
-       mkdir third_party
-       (cd third_party && \
-       svn co -r613 http://googletest.googlecode.com/svn/trunk googletest \
-        )
-
-$(GTEST_LIB):
-       mkdir -p $(GTEST_BUILD_DIR) && \
-       cd $(GTEST_BUILD_DIR) && \
-       $(MAKE) -f ../make/Makefile CXXFLAGS="$(CXXFLAGS)" CFLAGS="$(CFLAGS)" CC=$(CC) CXX=$(CXX) $(GTEST_LIB_NAME)
-
-clean:
-       rm -f asm_*.s libtsan.nm libtsan.objdump */*.o tsan_test
-       rm -rf $(GTEST_BUILD_DIR)
-       $(MAKE) clean -C rtl -f Makefile.old
-       rm -f go/*.s
-       rm -rf build
index 705e4c5460f2ee70da2f1c678d880051a28c2924..ae29f1b5b05a12092a49c3822524f20e7acee0c3 100755 (executable)
@@ -1,10 +1,17 @@
 #!/bin/bash
+#
+# Script that prints information about generated code in TSan runtime.
 
 set -e
 set -u
 
+if [[ "$#" != 1 ]]; then
+  echo "Usage: $0 /path/to/binary/built/with/tsan"
+  exit 1
+fi
+
 get_asm() {
-  grep __tsan_$1.: -A 10000 libtsan.objdump | \
+  grep __tsan_$1.: -A 10000 ${OBJDUMP_CONTENTS} | \
     awk "/[^:]$/ {print;} />:/ {c++; if (c == 2) {exit}}"
 }
 
@@ -19,15 +26,19 @@ list="write1 \
       func_entry \
       func_exit"
 
-BIN=`dirname $0`/tsan_test
-objdump -d $BIN  > libtsan.objdump
-nm -S $BIN | grep "__tsan_" > libtsan.nm
+BIN=$1
+OUTPUT_DIR=$(mktemp -t -d analyze_libtsan_out.XXXXXXXX)
+OBJDUMP_CONTENTS=${OUTPUT_DIR}/libtsan_objdump
+NM_CONTENTS=${OUTPUT_DIR}/libtsan_nm
+
+objdump -d $BIN  > ${OBJDUMP_CONTENTS}
+nm -S $BIN | grep "__tsan_" > ${NM_CONTENTS}
 
 for f in $list; do
-  file=asm_$f.s
+  file=${OUTPUT_DIR}/asm_$f.s
   get_asm $f > $file
   tot=$(wc -l < $file)
-  size=$(grep __tsan_$f$ libtsan.nm | awk --non-decimal-data '{print ("0x"$2)+0}')
+  size=$(grep __tsan_$f$ ${NM_CONTENTS} | awk --non-decimal-data '{print ("0x"$2)+0}')
   rsp=$(grep '(%rsp)' $file | wc -l)
   push=$(grep 'push' $file | wc -l)
   pop=$(grep 'pop' $file | wc -l)
index 4b33393ef648fefb3a66fe5ce8d279ad017e7aef..0f6cc0698471e34e968cc50d0965999928d7c350 100755 (executable)
@@ -1,7 +1,17 @@
 #!/bin/bash
+#
+# Script that checks that critical functions in TSan runtime have correct number
+# of push/pop/rsp instructions to verify that runtime is efficient enough.
+
 set -u
 
-RES=$(./analyze_libtsan.sh)
+if [[ "$#" != 1 ]]; then
+  echo "Usage: $0 /path/to/binary/built/with/tsan"
+  exit 1
+fi
+
+SCRIPTDIR=$(dirname $0)
+RES=$(${SCRIPTDIR}/analyze_libtsan.sh $1)
 PrintRes() {
   printf "%s\n" "$RES"
 }
@@ -22,7 +32,13 @@ for f in write1; do
   check $f pop 2
 done
 
-for f in write2 write4 write8; do
+for f in write2 write4; do
+  check $f rsp 1
+  check $f push 4
+  check $f pop 4
+done
+
+for f in write8; do
   check $f rsp 1
   check $f push 3
   check $f pop 3
diff --git a/src/compiler-rt/lib/tsan/check_memcpy.sh b/src/compiler-rt/lib/tsan/check_memcpy.sh
deleted file mode 100755 (executable)
index 101df11..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-
-# Ensure that tsan runtime does not contain compiler-emitted memcpy and memset calls.
-
-set -eu
-
-ROOTDIR=$(dirname $0)
-TEST_DIR=$ROOTDIR/../../test/tsan
-
-: ${CXX:=clang++}
-CFLAGS="-fsanitize=thread -fPIE -O1 -g"
-LDFLAGS="-pie -lpthread -ldl -lrt -lm -Wl,--whole-archive $ROOTDIR/rtl/libtsan.a -Wl,--no-whole-archive"
-
-SRC=$TEST_DIR/simple_race.cc
-OBJ=$SRC.o
-EXE=$SRC.exe
-$CXX $SRC $CFLAGS -c -o $OBJ
-$CXX $OBJ $LDFLAGS -o $EXE
-
-NCALL=$(objdump -d $EXE | egrep "callq .*<__interceptor_mem(cpy|set)>" | wc -l)
-if [ "$NCALL" != "0" ]; then
-  echo FAIL: found $NCALL memcpy/memset calls
-  exit 1
-fi
-
-# tail calls
-NCALL=$(objdump -d $EXE | egrep "jmpq .*<__interceptor_mem(cpy|set)>" | wc -l)
-if [ "$NCALL" != "0" ]; then
-  echo FAIL: found $NCALL memcpy/memset calls
-  exit 1
-fi
index a7e12f18c3ce8cffc0c9446400d317dc36c42ef3..668530c5f40efc0c2782145bf3f7a9b16baaa40d 100755 (executable)
@@ -20,6 +20,7 @@ SRCS="
        ../rtl/tsan_sync.cc
        ../../sanitizer_common/sanitizer_allocator.cc
        ../../sanitizer_common/sanitizer_common.cc
+       ../../sanitizer_common/sanitizer_common_libcdep.cc
        ../../sanitizer_common/sanitizer_deadlock_detector2.cc
        ../../sanitizer_common/sanitizer_flag_parser.cc
        ../../sanitizer_common/sanitizer_flags.cc
@@ -36,7 +37,7 @@ SRCS="
 if [ "`uname -a | grep Linux`" != "" ]; then
        SUFFIX="linux_amd64"
        OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Wno-unused-const-variable -Werror -Wno-unknown-warning-option"
-       OSLDFLAGS="-lpthread -lrt -fPIC -fpie"
+       OSLDFLAGS="-lpthread -fPIC -fpie"
        SRCS="
                $SRCS
                ../rtl/tsan_platform_linux.cc
@@ -49,19 +50,20 @@ if [ "`uname -a | grep Linux`" != "" ]; then
                ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
        "
 elif [ "`uname -a | grep FreeBSD`" != "" ]; then
-        SUFFIX="freebsd_amd64"
-        OSCFLAGS="-fno-strict-aliasing -fPIC -Werror"
-        OSLDFLAGS="-lpthread -fPIC -fpie"
-        SRCS="
-                $SRCS
-                ../rtl/tsan_platform_linux.cc
-                ../../sanitizer_common/sanitizer_posix.cc
-                ../../sanitizer_common/sanitizer_posix_libcdep.cc
-                ../../sanitizer_common/sanitizer_procmaps_common.cc
-                ../../sanitizer_common/sanitizer_procmaps_freebsd.cc
-                ../../sanitizer_common/sanitizer_linux.cc
-                ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
-        "
+       SUFFIX="freebsd_amd64"
+       OSCFLAGS="-fno-strict-aliasing -fPIC -Werror"
+       OSLDFLAGS="-lpthread -fPIC -fpie"
+       SRCS="
+               $SRCS
+               ../rtl/tsan_platform_linux.cc
+               ../../sanitizer_common/sanitizer_posix.cc
+               ../../sanitizer_common/sanitizer_posix_libcdep.cc
+               ../../sanitizer_common/sanitizer_procmaps_common.cc
+               ../../sanitizer_common/sanitizer_procmaps_freebsd.cc
+               ../../sanitizer_common/sanitizer_linux.cc
+               ../../sanitizer_common/sanitizer_linux_libcdep.cc
+               ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
+       "
 elif [ "`uname -a | grep Darwin`" != "" ]; then
        SUFFIX="darwin_amd64"
        OSCFLAGS="-fPIC -Wno-unused-const-variable -Wno-unknown-warning-option"
diff --git a/src/compiler-rt/lib/tsan/rtl/Makefile.old b/src/compiler-rt/lib/tsan/rtl/Makefile.old
deleted file mode 100644 (file)
index 0ef6fed..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-CXXFLAGS = -std=c++11 -fPIE -g -Wall -Werror -fno-builtin -msse3 -DSANITIZER_DEBUG=$(DEBUG) -DTSAN_CONTAINS_UBSAN=0
-CLANG=clang
-ifeq ($(DEBUG), 0)
-  CXXFLAGS += -O3
-endif
-
-# For interception. FIXME: move interception one level higher.
-INTERCEPTION=../../interception
-COMMON=../../sanitizer_common
-INCLUDES= -I../.. -I../../../include
-EXTRA_CXXFLAGS=-fno-exceptions -fno-rtti
-NO_SYSROOT=--sysroot=.
-CXXFLAGS+=$(EXTRA_CXXFLAGS)
-CXXFLAGS+=$(CFLAGS)
-ifeq ($(DEBUG), 0)
-  CXXFLAGS+=-fomit-frame-pointer
-ifeq ($(CXX), g++)
-  CXXFLAGS+=-Wno-maybe-uninitialized
-  CXXFLAGS+=-Wframe-larger-than=512
-endif  # CXX=g++
-endif  # DEBUG=0
-
-ifeq ($(CXX), $(CLANG)++)
-  # Global constructors are banned.
-  CXXFLAGS+=-Wglobal-constructors
-endif
-
-
-
-all: libtsan.a
-
-LIBTSAN_HEADERS=$(wildcard *.h) \
-               $(wildcard $(INTERCEPTION)/*.h) \
-               $(wildcard $(COMMON)/*.h)
-LIBTSAN_SRC=$(wildcard *.cc)
-LIBTSAN_ASM_SRC=$(wildcard *.S)
-INTERCEPTION_SRC=$(wildcard $(INTERCEPTION)/*.cc)
-COMMON_SRC=$(filter-out $(wildcard $(COMMON)/*_nolibc.cc),$(wildcard $(COMMON)/*.cc))
-
-LIBTSAN_OBJ=$(patsubst %.cc,%.o,$(LIBTSAN_SRC)) \
-           $(patsubst %.S,%.o,$(LIBTSAN_ASM_SRC)) \
-           $(patsubst $(INTERCEPTION)/%.cc,%.o,$(INTERCEPTION_SRC)) \
-           $(patsubst $(COMMON)/%.cc,%.o,$(COMMON_SRC))
-
-%_linux.o: %_linux.cc Makefile.old $(LIBTSAN_HEADERS)
-       $(CXX) $(CXXFLAGS) $(INCLUDES) -c $<
-%.o: %.cc Makefile.old $(LIBTSAN_HEADERS)
-       $(CXX) $(CXXFLAGS) $(INCLUDES) $(NO_SYSROOT) -c $<
-%.o: $(INTERCEPTION)/%.cc Makefile.old $(LIBTSAN_HEADERS)
-       $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
-%.o: $(COMMON)/%.cc Makefile.old $(LIBTSAN_HEADERS)
-       $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
-%.o: %.S
-       $(CXX) $(INCLUDES) -o $@ -c $<
-
-libtsan.a: $(LIBTSAN_OBJ)
-       ar ru $@ $(LIBTSAN_OBJ)
-
-libtsan_dummy.a: tsan_dummy_rtl.o
-       ar ru $@ $<
-
-clean:
-       rm -f *.o *.a
index 385e366c39bb59243fcc3462c55f16886ba285b4..9c7b329dcf000d7baff16e93d2f863fae59241b5 100644 (file)
@@ -38,13 +38,10 @@ namespace __tsan {
 const bool kGoMode = true;
 const bool kCppMode = false;
 const char *const kTsanOptionsEnv = "GORACE";
-// Go linker does not support weak symbols.
-#define CPP_WEAK
 #else
 const bool kGoMode = false;
 const bool kCppMode = true;
 const char *const kTsanOptionsEnv = "TSAN_OPTIONS";
-#define CPP_WEAK WEAK
 #endif
 
 const int kTidBits = 13;
index a1cf84b8f16614806b426be89cdf3ba5dc391c54..e9815c90a9536782a8e5dcc4eb45be938db830cc 100644 (file)
@@ -108,7 +108,7 @@ class DenseSlabAlloc {
       // Reserve 0 as invalid index.
       IndexT start = fillpos_ == 0 ? 1 : 0;
       for (IndexT i = start; i < kL2Size; i++) {
-        new(batch + i) T();
+        new(batch + i) T;
         *(IndexT*)(batch + i) = i + 1 + fillpos_ * kL2Size;
       }
       *(IndexT*)(batch + kL2Size - 1) = 0;
index b8bee70b7f0f7c5fe849f16d619b27e61c0b1865..761523171c7708b9e34dcabb6a64ccfc5f54ea6e 100644 (file)
@@ -29,8 +29,8 @@ Flags *flags() {
 #ifdef TSAN_EXTERNAL_HOOKS
 extern "C" const char* __tsan_default_options();
 #else
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-const char *WEAK __tsan_default_options() {
+SANITIZER_WEAK_DEFAULT_IMPL
+const char *__tsan_default_options() {
   return "";
 }
 #endif
@@ -61,9 +61,13 @@ void InitializeFlags(Flags *f, const char *env) {
     CommonFlags cf;
     cf.CopyFrom(*common_flags());
     cf.allow_addr2line = true;
-#ifndef SANITIZER_GO
-    cf.detect_deadlocks = true;
-#endif
+    if (kGoMode) {
+      // Does not work as expected for Go: runtime handles SIGABRT and crashes.
+      cf.abort_on_error = false;
+      // Go does not have mutexes.
+    } else {
+      cf.detect_deadlocks = true;
+    }
     cf.print_suppressions = false;
     cf.stack_trace_format = "    #%n %f %S %M";
     cf.exitcode = 66;
index 800c7bd98b75a0fbd3124d760dc0b6390a392a07..7c835c6dc7df8d6075d17681992887c36a134372 100644 (file)
@@ -48,6 +48,13 @@ using namespace __tsan;  // NOLINT
 #define __libc_realloc REAL(realloc)
 #define __libc_calloc REAL(calloc)
 #define __libc_free REAL(free)
+#elif SANITIZER_ANDROID
+#define __errno_location __errno
+#define __libc_malloc REAL(malloc)
+#define __libc_realloc REAL(realloc)
+#define __libc_calloc REAL(calloc)
+#define __libc_free REAL(free)
+#define mallopt(a, b)
 #endif
 
 #if SANITIZER_LINUX || SANITIZER_FREEBSD
@@ -79,9 +86,11 @@ struct ucontext_t {
 };
 #endif
 
-#if defined(__x86_64__) || defined(__mips__)
+#if defined(__x86_64__) || defined(__mips__) \
+  || (defined(__powerpc64__) && defined(__BIG_ENDIAN__))
 #define PTHREAD_ABI_BASE  "GLIBC_2.3.2"
-#elif defined(__aarch64__)
+#elif defined(__aarch64__) || (defined(__powerpc64__) \
+  && defined(__LITTLE_ENDIAN__))
 #define PTHREAD_ABI_BASE  "GLIBC_2.17"
 #endif
 
@@ -103,10 +112,12 @@ extern "C" void *pthread_self();
 extern "C" void _exit(int status);
 extern "C" int *__errno_location();
 extern "C" int fileno_unlocked(void *stream);
+#if !SANITIZER_ANDROID
 extern "C" void *__libc_calloc(uptr size, uptr n);
 extern "C" void *__libc_realloc(void *ptr, uptr size);
+#endif
 extern "C" int dirfd(void *dirp);
-#if !SANITIZER_FREEBSD
+#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID
 extern "C" int mallopt(int param, int value);
 #endif
 extern __sanitizer_FILE *stdout, *stderr;
@@ -124,7 +135,7 @@ const int SIGFPE = 8;
 const int SIGSEGV = 11;
 const int SIGPIPE = 13;
 const int SIGTERM = 15;
-#ifdef __mips__
+#if defined(__mips__) || SANITIZER_MAC
 const int SIGBUS = 10;
 const int SIGSYS = 12;
 #else
@@ -149,6 +160,17 @@ typedef long long_t;  // NOLINT
 typedef void (*sighandler_t)(int sig);
 typedef void (*sigactionhandler_t)(int sig, my_siginfo_t *siginfo, void *uctx);
 
+#if SANITIZER_ANDROID
+struct sigaction_t {
+  u32 sa_flags;
+  union {
+    sighandler_t sa_handler;
+    sigactionhandler_t sa_sgiaction;
+  };
+  __sanitizer_sigset_t sa_mask;
+  void (*sa_restorer)();
+};
+#else
 struct sigaction_t {
 #ifdef __mips__
   u32 sa_flags;
@@ -160,6 +182,9 @@ struct sigaction_t {
 #if SANITIZER_FREEBSD
   int sa_flags;
   __sanitizer_sigset_t sa_mask;
+#elif SANITIZER_MAC
+  __sanitizer_sigset_t sa_mask;
+  int sa_flags;
 #else
   __sanitizer_sigset_t sa_mask;
 #ifndef __mips__
@@ -168,11 +193,12 @@ struct sigaction_t {
   void (*sa_restorer)();
 #endif
 };
+#endif
 
 const sighandler_t SIG_DFL = (sighandler_t)0;
 const sighandler_t SIG_IGN = (sighandler_t)1;
 const sighandler_t SIG_ERR = (sighandler_t)-1;
-#if SANITIZER_FREEBSD
+#if SANITIZER_FREEBSD || SANITIZER_MAC
 const int SA_SIGINFO = 0x40;
 const int SIG_SETMASK = 3;
 #elif defined(__mips__)
@@ -201,6 +227,9 @@ struct ThreadSignalContext {
   atomic_uintptr_t in_blocking_func;
   atomic_uintptr_t have_pending_signals;
   SignalDesc pending_signals[kSigCount];
+  // emptyset and oldset are too big for stack.
+  __sanitizer_sigset_t emptyset;
+  __sanitizer_sigset_t oldset;
 };
 
 // The object is 64-byte aligned, because we want hot data to be located in
@@ -233,7 +262,9 @@ static ThreadSignalContext *SigCtx(ThreadState *thr) {
   return ctx;
 }
 
+#if !SANITIZER_MAC
 static unsigned g_thread_finalize_key;
+#endif
 
 ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
                                      uptr pc)
@@ -264,17 +295,20 @@ ScopedInterceptor::~ScopedInterceptor() {
   }
 }
 
-#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
-    SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
-    if (REAL(func) == 0) { \
-      Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
-      Die(); \
-    }                                                    \
-    if (thr->ignore_interceptors || thr->in_ignored_lib) \
-      return REAL(func)(__VA_ARGS__); \
-/**/
+void ScopedInterceptor::UserCallbackStart() {
+  if (in_ignored_lib_) {
+    thr_->in_ignored_lib = false;
+    ThreadIgnoreEnd(thr_, pc_);
+  }
+}
+
+void ScopedInterceptor::UserCallbackEnd() {
+  if (in_ignored_lib_) {
+    thr_->in_ignored_lib = true;
+    ThreadIgnoreBegin(thr_, pc_);
+  }
+}
 
-#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
 #define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
 #if SANITIZER_FREEBSD
 # define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
@@ -359,6 +393,7 @@ static void at_exit_wrapper(void *arg) {
 static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
       void *arg, void *dso);
 
+#if !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
   if (cur_thread()->in_symbolizer)
     return 0;
@@ -367,6 +402,7 @@ TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
   SCOPED_INTERCEPTOR_RAW(atexit, f);
   return setup_at_exit_wrapper(thr, pc, (void(*)())f, 0, 0);
 }
+#endif
 
 TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) {
   if (cur_thread()->in_symbolizer)
@@ -422,7 +458,7 @@ static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) {
     JmpBuf *buf = &thr->jmp_bufs[i];
     if (buf->sp <= sp) {
       uptr sz = thr->jmp_bufs.Size();
-      thr->jmp_bufs[i] = thr->jmp_bufs[sz - 1];
+      internal_memcpy(buf, &thr->jmp_bufs[sz - 1], sizeof(*buf));
       thr->jmp_bufs.PopBack();
       i--;
     }
@@ -449,11 +485,17 @@ static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) {
 }
 
 static void LongJmp(ThreadState *thr, uptr *env) {
-#if SANITIZER_FREEBSD
+#ifdef __powerpc__
+  uptr mangled_sp = env[0];
+#elif SANITIZER_FREEBSD || SANITIZER_MAC
   uptr mangled_sp = env[2];
-#else
+#elif defined(SANITIZER_LINUX)
+# ifdef __aarch64__
+  uptr mangled_sp = env[13];
+# else
   uptr mangled_sp = env[6];
-#endif  // SANITIZER_FREEBSD
+# endif
+#endif
   // Find the saved buf by mangled_sp.
   for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
     JmpBuf *buf = &thr->jmp_bufs[i];
@@ -483,6 +525,11 @@ extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) {
   SetJmp(cur_thread(), sp, mangled_sp);
 }
 
+#if SANITIZER_MAC
+TSAN_INTERCEPTOR(int, setjmp, void *env);
+TSAN_INTERCEPTOR(int, _setjmp, void *env);
+TSAN_INTERCEPTOR(int, sigsetjmp, void *env);
+#else  // SANITIZER_MAC
 // Not called.  Merely to satisfy TSAN_INTERCEPT().
 extern "C" SANITIZER_INTERFACE_ATTRIBUTE
 int __interceptor_setjmp(void *env);
@@ -521,6 +568,7 @@ DEFINE_REAL(int, setjmp, void *env)
 DEFINE_REAL(int, _setjmp, void *env)
 DEFINE_REAL(int, sigsetjmp, void *env)
 DEFINE_REAL(int, __sigsetjmp, void *env)
+#endif  // SANITIZER_MAC
 
 TSAN_INTERCEPTOR(void, longjmp, uptr *env, int val) {
   {
@@ -791,8 +839,25 @@ TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
 }
 #endif
 
+// __cxa_guard_acquire and friends need to be intercepted in a special way -
+// regular interceptors will break statically-linked libstdc++. Linux
+// interceptors are especially defined as weak functions (so that they don't
+// cause link errors when user defines them as well). So they silently
+// auto-disable themselves when such symbol is already present in the binary. If
+// we link libstdc++ statically, it will bring own __cxa_guard_acquire which
+// will silently replace our interceptor.  That's why on Linux we simply export
+// these interceptors with INTERFACE_ATTRIBUTE.
+// On OS X, we don't support statically linking, so we just use a regular
+// interceptor.
+#if SANITIZER_MAC
+#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
+#else
+#define STDCXX_INTERCEPTOR(rettype, name, ...) \
+  extern "C" rettype INTERFACE_ATTRIBUTE name(__VA_ARGS__)
+#endif
+
 // Used in thread-safe function static initialization.
-extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) {
+STDCXX_INTERCEPTOR(int, __cxa_guard_acquire, atomic_uint32_t *g) {
   SCOPED_INTERCEPTOR_RAW(__cxa_guard_acquire, g);
   for (;;) {
     u32 cmp = atomic_load(g, memory_order_acquire);
@@ -808,13 +873,13 @@ extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) {
   }
 }
 
-extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_release(atomic_uint32_t *g) {
+STDCXX_INTERCEPTOR(void, __cxa_guard_release, atomic_uint32_t *g) {
   SCOPED_INTERCEPTOR_RAW(__cxa_guard_release, g);
   Release(thr, pc, (uptr)g);
   atomic_store(g, 1, memory_order_release);
 }
 
-extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_abort(atomic_uint32_t *g) {
+STDCXX_INTERCEPTOR(void, __cxa_guard_abort, atomic_uint32_t *g) {
   SCOPED_INTERCEPTOR_RAW(__cxa_guard_abort, g);
   atomic_store(g, 0, memory_order_relaxed);
 }
@@ -832,6 +897,7 @@ void DestroyThreadState() {
 }
 }  // namespace __tsan
 
+#if !SANITIZER_MAC
 static void thread_finalize(void *v) {
   uptr iter = (uptr)v;
   if (iter > 1) {
@@ -843,6 +909,7 @@ static void thread_finalize(void *v) {
   }
   DestroyThreadState();
 }
+#endif
 
 
 struct ThreadParam {
@@ -860,6 +927,7 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
     ThreadState *thr = cur_thread();
     // Thread-local state is not initialized yet.
     ScopedIgnoreInterceptors ignore;
+#if !SANITIZER_MAC
     ThreadIgnoreBegin(thr, 0);
     if (pthread_setspecific(g_thread_finalize_key,
                             (void *)GetPthreadDestructorIterations())) {
@@ -867,6 +935,7 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
       Die();
     }
     ThreadIgnoreEnd(thr, 0);
+#endif
     while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
       internal_sched_yield();
     ThreadStart(thr, tid, GetTid());
@@ -1325,7 +1394,7 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
   return 0;
 }
 
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
   SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf);
   READ_STRING(thr, pc, path, 0);
@@ -1337,7 +1406,7 @@ TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
 #endif
 
 TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_MAC
+#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_ANDROID
   SCOPED_TSAN_INTERCEPTOR(stat, path, buf);
   READ_STRING(thr, pc, path, 0);
   return REAL(stat)(path, buf);
@@ -1348,7 +1417,7 @@ TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
 #endif
 }
 
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
   SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf);
   READ_STRING(thr, pc, path, 0);
@@ -1359,7 +1428,7 @@ TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
 #define TSAN_MAYBE_INTERCEPT___XSTAT64
 #endif
 
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
   SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf);
   READ_STRING(thr, pc, path, 0);
@@ -1370,7 +1439,7 @@ TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
 #define TSAN_MAYBE_INTERCEPT_STAT64
 #endif
 
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
   SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf);
   READ_STRING(thr, pc, path, 0);
@@ -1382,7 +1451,7 @@ TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
 #endif
 
 TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_MAC
+#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_ANDROID
   SCOPED_TSAN_INTERCEPTOR(lstat, path, buf);
   READ_STRING(thr, pc, path, 0);
   return REAL(lstat)(path, buf);
@@ -1393,7 +1462,7 @@ TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
 #endif
 }
 
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
   SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf);
   READ_STRING(thr, pc, path, 0);
@@ -1404,7 +1473,7 @@ TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
 #define TSAN_MAYBE_INTERCEPT___LXSTAT64
 #endif
 
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
   SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf);
   READ_STRING(thr, pc, path, 0);
@@ -1415,7 +1484,7 @@ TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
 #define TSAN_MAYBE_INTERCEPT_LSTAT64
 #endif
 
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
   SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf);
   if (fd > 0)
@@ -1428,7 +1497,7 @@ TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
 #endif
 
 TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_MAC
+#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_ANDROID
   SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf);
   if (fd > 0)
     FdAccess(thr, pc, fd);
@@ -1441,7 +1510,7 @@ TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
 #endif
 }
 
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
   SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
   if (fd > 0)
@@ -1453,7 +1522,7 @@ TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
 #define TSAN_MAYBE_INTERCEPT___FXSTAT64
 #endif
 
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
   SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf);
   if (fd > 0)
@@ -1678,7 +1747,7 @@ TSAN_INTERCEPTOR(int, __close, int fd) {
 #endif
 
 // glibc guts
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
 TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) {
   SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr);
   int fds[64];
@@ -1823,8 +1892,10 @@ TSAN_INTERCEPTOR(int, rmdir, char *path) {
 
 TSAN_INTERCEPTOR(int, closedir, void *dirp) {
   SCOPED_TSAN_INTERCEPTOR(closedir, dirp);
-  int fd = dirfd(dirp);
-  FdClose(thr, pc, fd);
+  if (dirp) {
+    int fd = dirfd(dirp);
+    FdClose(thr, pc, fd);
+  }
   return REAL(closedir)(dirp);
 }
 
@@ -1910,10 +1981,8 @@ void ProcessPendingSignals(ThreadState *thr) {
     return;
   atomic_store(&sctx->have_pending_signals, 0, memory_order_relaxed);
   atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
-  // These are too big for stack.
-  static THREADLOCAL __sanitizer_sigset_t emptyset, oldset;
-  CHECK_EQ(0, REAL(sigfillset)(&emptyset));
-  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &emptyset, &oldset));
+  CHECK_EQ(0, REAL(sigfillset)(&sctx->emptyset));
+  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sctx->emptyset, &sctx->oldset));
   for (int sig = 0; sig < kSigCount; sig++) {
     SignalDesc *signal = &sctx->pending_signals[sig];
     if (signal->armed) {
@@ -1922,7 +1991,7 @@ void ProcessPendingSignals(ThreadState *thr) {
           &signal->siginfo, &signal->ctx);
     }
   }
-  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &oldset, 0));
+  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sctx->oldset, 0));
   atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
 }
 
@@ -2011,7 +2080,7 @@ TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) {
   sigactions[sig].sa_flags = *(volatile int*)&act->sa_flags;
   internal_memcpy(&sigactions[sig].sa_mask, &act->sa_mask,
       sizeof(sigactions[sig].sa_mask));
-#if !SANITIZER_FREEBSD
+#if !SANITIZER_FREEBSD && !SANITIZER_MAC
   sigactions[sig].sa_restorer = act->sa_restorer;
 #endif
   sigaction_t newact;
@@ -2144,7 +2213,7 @@ TSAN_INTERCEPTOR(int, vfork, int fake) {
   return WRAP(fork)(fake);
 }
 
-#if !SANITIZER_MAC
+#if !SANITIZER_MAC && !SANITIZER_ANDROID
 typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size,
                                     void *data);
 struct dl_iterate_phdr_data {
@@ -2466,7 +2535,7 @@ static void finalize(void *arg) {
     Die();
 }
 
-#if !SANITIZER_MAC
+#if !SANITIZER_MAC && !SANITIZER_ANDROID
 static void unreachable() {
   Report("FATAL: ThreadSanitizer: unreachable called\n");
   Die();
@@ -2631,12 +2700,14 @@ void InitializeInterceptors() {
 
   TSAN_INTERCEPT(fork);
   TSAN_INTERCEPT(vfork);
+#if !SANITIZER_ANDROID
   TSAN_INTERCEPT(dl_iterate_phdr);
+#endif
   TSAN_INTERCEPT(on_exit);
   TSAN_INTERCEPT(__cxa_atexit);
   TSAN_INTERCEPT(_exit);
 
-#if !SANITIZER_MAC
+#if !SANITIZER_MAC && !SANITIZER_ANDROID
   // Need to setup it, because interceptors check that the function is resolved.
   // But atexit is emitted directly into the module, so can't be resolved.
   REAL(atexit) = (int(*)(void(*)()))unreachable;
@@ -2647,12 +2718,50 @@ void InitializeInterceptors() {
     Die();
   }
 
+#if !SANITIZER_MAC
   if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) {
     Printf("ThreadSanitizer: failed to create thread key\n");
     Die();
   }
+#endif
 
   FdInit();
 }
 
 }  // namespace __tsan
+
+// Invisible barrier for tests.
+// There were several unsuccessful iterations for this functionality:
+// 1. Initially it was implemented in user code using
+//    REAL(pthread_barrier_wait). But pthread_barrier_wait is not supported on
+//    MacOS. Futexes are linux-specific for this matter.
+// 2. Then we switched to atomics+usleep(10). But usleep produced parasitic
+//    "as-if synchronized via sleep" messages in reports which failed some
+//    output tests.
+// 3. Then we switched to atomics+sched_yield. But this produced tons of tsan-
+//    visible events, which lead to "failed to restore stack trace" failures.
+// Note that no_sanitize_thread attribute does not turn off atomic interception
+// so attaching it to the function defined in user code does not help.
+// That's why we now have what we have.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_testonly_barrier_init(u64 *barrier, u32 count) {
+  if (count >= (1 << 8)) {
+      Printf("barrier_init: count is too large (%d)\n", count);
+      Die();
+  }
+  // 8 lsb is thread count, the remaining are count of entered threads.
+  *barrier = count;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_testonly_barrier_wait(u64 *barrier) {
+  unsigned old = __atomic_fetch_add(barrier, 1 << 8, __ATOMIC_RELAXED);
+  unsigned old_epoch = (old >> 8) / (old & 0xff);
+  for (;;) {
+    unsigned cur = __atomic_load_n(barrier, __ATOMIC_RELAXED);
+    unsigned cur_epoch = (cur >> 8) / (cur & 0xff);
+    if (cur_epoch != old_epoch)
+      return;
+    internal_sched_yield();
+  }
+}
index 49b79a7c5f9e60cf52a5de6c6a8370b8881e270e..d831620cfafe7ccd7ba941e7aee2915ee914da31 100644 (file)
@@ -10,6 +10,8 @@ class ScopedInterceptor {
  public:
   ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc);
   ~ScopedInterceptor();
+  void UserCallbackStart();
+  void UserCallbackEnd();
  private:
   ThreadState *const thr_;
   const uptr pc_;
@@ -26,6 +28,24 @@ class ScopedInterceptor {
     (void)pc; \
 /**/
 
+#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
+    SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
+    if (REAL(func) == 0) { \
+      Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
+      Die(); \
+    }                                                    \
+    if (thr->ignore_interceptors || thr->in_ignored_lib) \
+      return REAL(func)(__VA_ARGS__); \
+/**/
+
+#define SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START() \
+    si.UserCallbackStart();
+
+#define SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END() \
+    si.UserCallbackEnd();
+
+#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
+
 #if SANITIZER_FREEBSD
 #define __libc_free __free
 #define __libc_malloc __malloc
diff --git a/src/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc b/src/compiler-rt/lib/tsan/rtl/tsan_interceptors_mac.cc
new file mode 100644 (file)
index 0000000..2bf7ad9
--- /dev/null
@@ -0,0 +1,91 @@
+//===-- tsan_interceptors_mac.cc ------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific interceptors.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "interception/interception.h"
+#include "tsan_interceptors.h"
+
+#include <libkern/OSAtomic.h>
+
+namespace __tsan {
+
+TSAN_INTERCEPTOR(void, OSSpinLockLock, volatile OSSpinLock *lock) {
+  CHECK(!cur_thread()->is_dead);
+  if (!cur_thread()->is_inited) {
+    return REAL(OSSpinLockLock)(lock);
+  }
+  SCOPED_TSAN_INTERCEPTOR(OSSpinLockLock, lock);
+  REAL(OSSpinLockLock)(lock);
+  Acquire(thr, pc, (uptr)lock);
+}
+
+TSAN_INTERCEPTOR(bool, OSSpinLockTry, volatile OSSpinLock *lock) {
+  CHECK(!cur_thread()->is_dead);
+  if (!cur_thread()->is_inited) {
+    return REAL(OSSpinLockTry)(lock);
+  }
+  SCOPED_TSAN_INTERCEPTOR(OSSpinLockTry, lock);
+  bool result = REAL(OSSpinLockTry)(lock);
+  if (result)
+    Acquire(thr, pc, (uptr)lock);
+  return result;
+}
+
+TSAN_INTERCEPTOR(void, OSSpinLockUnlock, volatile OSSpinLock *lock) {
+  CHECK(!cur_thread()->is_dead);
+  if (!cur_thread()->is_inited) {
+    return REAL(OSSpinLockUnlock)(lock);
+  }
+  SCOPED_TSAN_INTERCEPTOR(OSSpinLockUnlock, lock);
+  Release(thr, pc, (uptr)lock);
+  REAL(OSSpinLockUnlock)(lock);
+}
+
+TSAN_INTERCEPTOR(void, os_lock_lock, void *lock) {
+  CHECK(!cur_thread()->is_dead);
+  if (!cur_thread()->is_inited) {
+    return REAL(os_lock_lock)(lock);
+  }
+  SCOPED_TSAN_INTERCEPTOR(os_lock_lock, lock);
+  REAL(os_lock_lock)(lock);
+  Acquire(thr, pc, (uptr)lock);
+}
+
+TSAN_INTERCEPTOR(bool, os_lock_trylock, void *lock) {
+  CHECK(!cur_thread()->is_dead);
+  if (!cur_thread()->is_inited) {
+    return REAL(os_lock_trylock)(lock);
+  }
+  SCOPED_TSAN_INTERCEPTOR(os_lock_trylock, lock);
+  bool result = REAL(os_lock_trylock)(lock);
+  if (result)
+    Acquire(thr, pc, (uptr)lock);
+  return result;
+}
+
+TSAN_INTERCEPTOR(void, os_lock_unlock, void *lock) {
+  CHECK(!cur_thread()->is_dead);
+  if (!cur_thread()->is_inited) {
+    return REAL(os_lock_unlock)(lock);
+  }
+  SCOPED_TSAN_INTERCEPTOR(os_lock_unlock, lock);
+  Release(thr, pc, (uptr)lock);
+  REAL(os_lock_unlock)(lock);
+}
+
+}  // namespace __tsan
+
+#endif  // SANITIZER_MAC
diff --git a/src/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc b/src/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
new file mode 100644 (file)
index 0000000..617dc91
--- /dev/null
@@ -0,0 +1,284 @@
+//===-- tsan_libdispatch_mac.cc -------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific libdispatch (GCD) support.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "interception/interception.h"
+#include "tsan_interceptors.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+#include <Block.h>
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+
+typedef long long_t;  // NOLINT
+
+namespace __tsan {
+
+typedef struct {
+  dispatch_queue_t queue;
+  void *orig_context;
+  dispatch_function_t orig_work;
+  uptr object_to_acquire;
+  dispatch_object_t object_to_release;
+} tsan_block_context_t;
+
+// The offsets of different fields of the dispatch_queue_t structure, exported
+// by libdispatch.dylib.
+extern "C" struct dispatch_queue_offsets_s {
+  const uint16_t dqo_version;
+  const uint16_t dqo_label;
+  const uint16_t dqo_label_size;
+  const uint16_t dqo_flags;
+  const uint16_t dqo_flags_size;
+  const uint16_t dqo_serialnum;
+  const uint16_t dqo_serialnum_size;
+  const uint16_t dqo_width;
+  const uint16_t dqo_width_size;
+  const uint16_t dqo_running;
+  const uint16_t dqo_running_size;
+  const uint16_t dqo_suspend_cnt;
+  const uint16_t dqo_suspend_cnt_size;
+  const uint16_t dqo_target_queue;
+  const uint16_t dqo_target_queue_size;
+  const uint16_t dqo_priority;
+  const uint16_t dqo_priority_size;
+} dispatch_queue_offsets;
+
+static bool IsQueueSerial(dispatch_queue_t q) {
+  CHECK_EQ(dispatch_queue_offsets.dqo_width_size, 2);
+  uptr width = *(uint16_t *)(((uptr)q) + dispatch_queue_offsets.dqo_width);
+  CHECK_NE(width, 0);
+  return width == 1;
+}
+
+static tsan_block_context_t *AllocContext(ThreadState *thr, uptr pc,
+                                          dispatch_queue_t queue,
+                                          void *orig_context,
+                                          dispatch_function_t orig_work) {
+  tsan_block_context_t *new_context =
+      (tsan_block_context_t *)user_alloc(thr, pc, sizeof(tsan_block_context_t));
+  new_context->queue = queue;
+  new_context->orig_context = orig_context;
+  new_context->orig_work = orig_work;
+  new_context->object_to_acquire = (uptr)new_context;
+  new_context->object_to_release = nullptr;
+  return new_context;
+}
+
+static void dispatch_callback_wrap_acquire(void *param) {
+  SCOPED_INTERCEPTOR_RAW(dispatch_async_f_callback_wrap);
+  tsan_block_context_t *context = (tsan_block_context_t *)param;
+  Acquire(thr, pc, context->object_to_acquire);
+
+  // Extra retain/release is required for dispatch groups. We use the group
+  // itself to synchronize, but in a notification (dispatch_group_notify
+  // callback), it may be disposed already. To solve this, we retain the group
+  // and release it here.
+  if (context->object_to_release) dispatch_release(context->object_to_release);
+
+  // In serial queues, work items can be executed on different threads, we need
+  // to explicitly synchronize on the queue itself.
+  if (IsQueueSerial(context->queue)) Acquire(thr, pc, (uptr)context->queue);
+  SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+  context->orig_work(context->orig_context);
+  SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+  if (IsQueueSerial(context->queue)) Release(thr, pc, (uptr)context->queue);
+  user_free(thr, pc, context);
+}
+
+static void invoke_and_release_block(void *param) {
+  dispatch_block_t block = (dispatch_block_t)param;
+  block();
+  Block_release(block);
+}
+
+#define DISPATCH_INTERCEPT_B(name)                                           \
+  TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, dispatch_block_t block) { \
+    SCOPED_TSAN_INTERCEPTOR(name, q, block);                                 \
+    SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
+    dispatch_block_t heap_block = Block_copy(block);                         \
+    SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
+    tsan_block_context_t *new_context =                                      \
+        AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);     \
+    Release(thr, pc, (uptr)new_context);                                     \
+    SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
+    REAL(name##_f)(q, new_context, dispatch_callback_wrap_acquire);          \
+    SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
+  }
+
+#define DISPATCH_INTERCEPT_F(name)                                \
+  TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \
+                   dispatch_function_t work) {                    \
+    SCOPED_TSAN_INTERCEPTOR(name, q, context, work);              \
+    tsan_block_context_t *new_context =                           \
+        AllocContext(thr, pc, q, context, work);                  \
+    Release(thr, pc, (uptr)new_context);                          \
+    SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
+    REAL(name)(q, new_context, dispatch_callback_wrap_acquire);   \
+    SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
+  }
+
+// We wrap dispatch_async, dispatch_sync and friends where we allocate a new
+// context, which is used to synchronize (we release the context before
+// submitting, and the callback acquires it before executing the original
+// callback).
+DISPATCH_INTERCEPT_B(dispatch_async)
+DISPATCH_INTERCEPT_B(dispatch_barrier_async)
+DISPATCH_INTERCEPT_F(dispatch_async_f)
+DISPATCH_INTERCEPT_F(dispatch_barrier_async_f)
+DISPATCH_INTERCEPT_B(dispatch_sync)
+DISPATCH_INTERCEPT_B(dispatch_barrier_sync)
+DISPATCH_INTERCEPT_F(dispatch_sync_f)
+DISPATCH_INTERCEPT_F(dispatch_barrier_sync_f)
+
+// GCD's dispatch_once implementation has a fast path that contains a racy read
+// and it's inlined into user's code. Furthermore, this fast path doesn't
+// establish a proper happens-before relations between the initialization and
+// code following the call to dispatch_once. We could deal with this in
+// instrumented code, but there's not much we can do about it in system
+// libraries. Let's disable the fast path (by never storing the value ~0 to
+// predicate), so the interceptor is always called, and let's add proper release
+// and acquire semantics. Since TSan does not see its own atomic stores, the
+// race on predicate won't be reported - the only accesses to it that TSan sees
+// are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
+// both a macro and a real function, we want to intercept the function, so we
+// need to undefine the macro.
+#undef dispatch_once
+TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
+                 dispatch_block_t block) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_once, predicate, block);
+  atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
+  u32 v = atomic_load(a, memory_order_acquire);
+  if (v == 0 &&
+      atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
+    SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+    block();
+    SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+    Release(thr, pc, (uptr)a);
+    atomic_store(a, 2, memory_order_release);
+  } else {
+    while (v != 2) {
+      internal_sched_yield();
+      v = atomic_load(a, memory_order_acquire);
+    }
+    Acquire(thr, pc, (uptr)a);
+  }
+}
+
+#undef dispatch_once_f
+TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
+                 void *context, dispatch_function_t function) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_once_f, predicate, context, function);
+  SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+  WRAP(dispatch_once)(predicate, ^(void) {
+    function(context);
+  });
+  SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+}
+
+TSAN_INTERCEPTOR(long_t, dispatch_semaphore_signal,
+                 dispatch_semaphore_t dsema) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_signal, dsema);
+  Release(thr, pc, (uptr)dsema);
+  return REAL(dispatch_semaphore_signal)(dsema);
+}
+
+TSAN_INTERCEPTOR(long_t, dispatch_semaphore_wait, dispatch_semaphore_t dsema,
+                 dispatch_time_t timeout) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_wait, dsema, timeout);
+  long_t result = REAL(dispatch_semaphore_wait)(dsema, timeout);
+  if (result == 0) Acquire(thr, pc, (uptr)dsema);
+  return result;
+}
+
+TSAN_INTERCEPTOR(long_t, dispatch_group_wait, dispatch_group_t group,
+                 dispatch_time_t timeout) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_group_wait, group, timeout);
+  long_t result = REAL(dispatch_group_wait)(group, timeout);
+  if (result == 0) Acquire(thr, pc, (uptr)group);
+  return result;
+}
+
+TSAN_INTERCEPTOR(void, dispatch_group_leave, dispatch_group_t group) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_group_leave, group);
+  Release(thr, pc, (uptr)group);
+  REAL(dispatch_group_leave)(group);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_group_async, dispatch_group_t group,
+                 dispatch_queue_t queue, dispatch_block_t block) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_group_async, group, queue, block);
+  dispatch_retain(group);
+  dispatch_group_enter(group);
+  WRAP(dispatch_async)(queue, ^(void) {
+    block();
+    WRAP(dispatch_group_leave)(group);
+    dispatch_release(group);
+  });
+}
+
+TSAN_INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
+                 dispatch_queue_t queue, void *context,
+                 dispatch_function_t work) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_group_async_f, group, queue, context, work);
+  dispatch_retain(group);
+  dispatch_group_enter(group);
+  WRAP(dispatch_async)(queue, ^(void) {
+    work(context);
+    WRAP(dispatch_group_leave)(group);
+    dispatch_release(group);
+  });
+}
+
+TSAN_INTERCEPTOR(void, dispatch_group_notify, dispatch_group_t group,
+                 dispatch_queue_t q, dispatch_block_t block) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify, group, q, block);
+  SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+  dispatch_block_t heap_block = Block_copy(block);
+  SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+  tsan_block_context_t *new_context =
+      AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
+  new_context->object_to_acquire = (uptr)group;
+
+  // Will be released in dispatch_callback_wrap_acquire.
+  new_context->object_to_release = group;
+  dispatch_retain(group);
+
+  Release(thr, pc, (uptr)group);
+  REAL(dispatch_group_notify_f)(group, q, new_context,
+                                dispatch_callback_wrap_acquire);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_group_notify_f, dispatch_group_t group,
+                 dispatch_queue_t q, void *context, dispatch_function_t work) {
+  SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify_f, group, q, context, work);
+  tsan_block_context_t *new_context = AllocContext(thr, pc, q, context, work);
+  new_context->object_to_acquire = (uptr)group;
+
+  // Will be released in dispatch_callback_wrap_acquire.
+  new_context->object_to_release = group;
+  dispatch_retain(group);
+
+  Release(thr, pc, (uptr)group);
+  REAL(dispatch_group_notify_f)(group, q, new_context,
+                                dispatch_callback_wrap_acquire);
+}
+
+}  // namespace __tsan
+
+#endif  // SANITIZER_MAC
index 6319fdf60301cc9c4ba478f1d0d818925b91237b..7fd94273c3149dff6624df472a8f04bc70318182 100644 (file)
@@ -28,30 +28,36 @@ using namespace __tsan;
   void *p =                                     \
       user_alloc(cur_thread(), StackTrace::GetCurrentPc(), size, alignment)
 #define COMMON_MALLOC_MALLOC(size)      \
+  if (cur_thread()->in_symbolizer)      \
+    return REAL(malloc)(size);          \
   SCOPED_INTERCEPTOR_RAW(malloc, size); \
   void *p = user_alloc(thr, pc, size)
-#define COMMON_MALLOC_REALLOC(ptr, size) \
+#define COMMON_MALLOC_REALLOC(ptr, size)      \
+  if (cur_thread()->in_symbolizer)            \
+    return REAL(realloc)(ptr, size);          \
   SCOPED_INTERCEPTOR_RAW(realloc, ptr, size); \
-  void *p = user_realloc(thr, pc, ptr, size);
-#define COMMON_MALLOC_CALLOC(count, size) \
+  void *p = user_realloc(thr, pc, ptr, size)
+#define COMMON_MALLOC_CALLOC(count, size)      \
+  if (cur_thread()->in_symbolizer)             \
+    return REAL(calloc)(count, size);          \
   SCOPED_INTERCEPTOR_RAW(calloc, size, count); \
-  void *p = user_calloc(thr, pc, size, count);
-#define COMMON_MALLOC_VALLOC(size) \
-  SCOPED_INTERCEPTOR_RAW(valloc, size); \
-  void *p = user_alloc(thr, pc, size, GetPageSizeCached());
-#define COMMON_MALLOC_FREE(ptr) \
+  void *p = user_calloc(thr, pc, size, count)
+#define COMMON_MALLOC_VALLOC(size)                          \
+  if (cur_thread()->in_symbolizer)                          \
+    return REAL(valloc)(size);                              \
+  SCOPED_INTERCEPTOR_RAW(valloc, size);                     \
+  void *p = user_alloc(thr, pc, size, GetPageSizeCached())
+#define COMMON_MALLOC_FREE(ptr)      \
+  if (cur_thread()->in_symbolizer)   \
+    return REAL(free)(ptr);          \
   SCOPED_INTERCEPTOR_RAW(free, ptr); \
-  user_free(thr, pc, ptr);
+  user_free(thr, pc, ptr)
 #define COMMON_MALLOC_SIZE(ptr) \
   uptr size = user_alloc_usable_size(ptr);
 #define COMMON_MALLOC_FILL_STATS(zone, stats)
 #define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
   (void)zone_name; \
   Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr);
-#define COMMON_MALLOC_IGNORE_INVALID_FREE false
-#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \
-  (void)zone_name; \
-  Report("free_common(%p) -- attempting to free unallocated memory.\n", ptr);
 #define COMMON_MALLOC_NAMESPACE __tsan
 
 #include "sanitizer_common/sanitizer_malloc_mac.inc"
index 3168226b9cbdbd610e9d48feb1001daee7964320..7247c6e00035b14ac0bd9b48df956544f9ecf1cb 100644 (file)
 #include "tsan_flags.h"
 
 // May be overriden by front-end.
-extern "C" void WEAK __sanitizer_malloc_hook(void *ptr, uptr size) {
+SANITIZER_WEAK_DEFAULT_IMPL
+void __sanitizer_malloc_hook(void *ptr, uptr size) {
   (void)ptr;
   (void)size;
 }
 
-extern "C" void WEAK __sanitizer_free_hook(void *ptr) {
+SANITIZER_WEAK_DEFAULT_IMPL
+void __sanitizer_free_hook(void *ptr) {
   (void)ptr;
 }
 
index 18ba9082c4ac9615a4c6b7059f85ea1a9778a786..ebb422cf20234927bbb560bb548e0b2e22be2ce7 100644 (file)
@@ -11,8 +11,8 @@
 //
 // Interceptors for operators new and delete.
 //===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_internal_defs.h"
 #include "interception/interception.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
 #include "tsan_interceptors.h"
 
 using namespace __tsan;  // NOLINT
@@ -23,7 +23,7 @@ struct nothrow_t {};
 
 DECLARE_REAL(void *, malloc, uptr size)
 DECLARE_REAL(void, free, void *ptr)
-#if SANITIZER_MAC
+#if SANITIZER_MAC || SANITIZER_ANDROID
 #define __libc_malloc REAL(malloc)
 #define __libc_free REAL(free)
 #endif
index 57cd86d83043015a776af1f12fd36b1771ca7bd7..c2b487155300b2095a2cc77136a12cf74612d45f 100644 (file)
@@ -41,21 +41,23 @@ C/C++ on linux/x86_64 and freebsd/x86_64
 7e00 0000 0000 - 7e80 0000 0000: -
 7e80 0000 0000 - 8000 0000 0000: modules and main thread stack
 */
-const uptr kMetaShadowBeg = 0x300000000000ull;
-const uptr kMetaShadowEnd = 0x400000000000ull;
-const uptr kTraceMemBeg   = 0x600000000000ull;
-const uptr kTraceMemEnd   = 0x620000000000ull;
-const uptr kShadowBeg     = 0x020000000000ull;
-const uptr kShadowEnd     = 0x100000000000ull;
-const uptr kHeapMemBeg    = 0x7d0000000000ull;
-const uptr kHeapMemEnd    = 0x7e0000000000ull;
-const uptr kLoAppMemBeg   = 0x000000001000ull;
-const uptr kLoAppMemEnd   = 0x010000000000ull;
-const uptr kHiAppMemBeg   = 0x7e8000000000ull;
-const uptr kHiAppMemEnd   = 0x800000000000ull;
-const uptr kAppMemMsk     = 0x7c0000000000ull;
-const uptr kAppMemXor     = 0x020000000000ull;
-const uptr kVdsoBeg       = 0xf000000000000000ull;
+struct Mapping {
+  static const uptr kMetaShadowBeg = 0x300000000000ull;
+  static const uptr kMetaShadowEnd = 0x400000000000ull;
+  static const uptr kTraceMemBeg   = 0x600000000000ull;
+  static const uptr kTraceMemEnd   = 0x620000000000ull;
+  static const uptr kShadowBeg     = 0x020000000000ull;
+  static const uptr kShadowEnd     = 0x100000000000ull;
+  static const uptr kHeapMemBeg    = 0x7d0000000000ull;
+  static const uptr kHeapMemEnd    = 0x7e0000000000ull;
+  static const uptr kLoAppMemBeg   = 0x000000001000ull;
+  static const uptr kLoAppMemEnd   = 0x010000000000ull;
+  static const uptr kHiAppMemBeg   = 0x7e8000000000ull;
+  static const uptr kHiAppMemEnd   = 0x800000000000ull;
+  static const uptr kAppMemMsk     = 0x7c0000000000ull;
+  static const uptr kAppMemXor     = 0x020000000000ull;
+  static const uptr kVdsoBeg       = 0xf000000000000000ull;
+};
 #elif defined(__mips64)
 /*
 C/C++ on linux/mips64
@@ -71,128 +73,181 @@ fe00 0000 00 - ff00 0000 00: heap
 ff00 0000 00 - ff80 0000 00: -
 ff80 0000 00 - ffff ffff ff: modules and main thread stack
 */
-const uptr kMetaShadowBeg = 0x3000000000ull;
-const uptr kMetaShadowEnd = 0x4000000000ull;
-const uptr kTraceMemBeg   = 0x6000000000ull;
-const uptr kTraceMemEnd   = 0x6200000000ull;
-const uptr kShadowBeg     = 0x1400000000ull;
-const uptr kShadowEnd     = 0x2400000000ull;
-const uptr kHeapMemBeg    = 0xfe00000000ull;
-const uptr kHeapMemEnd    = 0xff00000000ull;
-const uptr kLoAppMemBeg   = 0x0100000000ull;
-const uptr kLoAppMemEnd   = 0x0200000000ull;
-const uptr kHiAppMemBeg   = 0xff80000000ull;
-const uptr kHiAppMemEnd   = 0xffffffffffull;
-const uptr kAppMemMsk     = 0xfc00000000ull;
-const uptr kAppMemXor     = 0x0400000000ull;
-const uptr kVdsoBeg       = 0xfffff00000ull;
+struct Mapping {
+  static const uptr kMetaShadowBeg = 0x3000000000ull;
+  static const uptr kMetaShadowEnd = 0x4000000000ull;
+  static const uptr kTraceMemBeg   = 0x6000000000ull;
+  static const uptr kTraceMemEnd   = 0x6200000000ull;
+  static const uptr kShadowBeg     = 0x1400000000ull;
+  static const uptr kShadowEnd     = 0x2400000000ull;
+  static const uptr kHeapMemBeg    = 0xfe00000000ull;
+  static const uptr kHeapMemEnd    = 0xff00000000ull;
+  static const uptr kLoAppMemBeg   = 0x0100000000ull;
+  static const uptr kLoAppMemEnd   = 0x0200000000ull;
+  static const uptr kHiAppMemBeg   = 0xff80000000ull;
+  static const uptr kHiAppMemEnd   = 0xffffffffffull;
+  static const uptr kAppMemMsk     = 0xfc00000000ull;
+  static const uptr kAppMemXor     = 0x0400000000ull;
+  static const uptr kVdsoBeg       = 0xfffff00000ull;
+};
 #elif defined(__aarch64__)
-# if SANITIZER_AARCH64_VMA == 39
+// AArch64 supports multiple VMA which leads to multiple address transformation
+// functions.  To support these multiple VMAS transformations and mappings TSAN
+// runtime for AArch64 uses an external memory read (vmaSize) to select which
+// mapping to use.  Although slower, it make a same instrumented binary run on
+// multiple kernels.
+
 /*
 C/C++ on linux/aarch64 (39-bit VMA)
-0000 4000 00 - 0200 0000 00: main binary
-2000 0000 00 - 4000 0000 00: shadow memory
-4000 0000 00 - 5000 0000 00: metainfo
-5000 0000 00 - 6000 0000 00: -
+0000 0010 00 - 0100 0000 00: main binary
+0100 0000 00 - 0800 0000 00: -
+0800 0000 00 - 2000 0000 00: shadow memory
+2000 0000 00 - 3100 0000 00: -
+3100 0000 00 - 3400 0000 00: metainfo
+3400 0000 00 - 5500 0000 00: -
+5500 0000 00 - 5600 0000 00: main binary (PIE)
+5600 0000 00 - 6000 0000 00: -
 6000 0000 00 - 6200 0000 00: traces
 6200 0000 00 - 7d00 0000 00: -
-7d00 0000 00 - 7e00 0000 00: heap
-7e00 0000 00 - 7fff ffff ff: modules and main thread stack
+7c00 0000 00 - 7d00 0000 00: heap
+7d00 0000 00 - 7fff ffff ff: modules and main thread stack
 */
-const uptr kLoAppMemBeg   = 0x0000400000ull;
-const uptr kLoAppMemEnd   = 0x0200000000ull;
-const uptr kShadowBeg     = 0x2000000000ull;
-const uptr kShadowEnd     = 0x4000000000ull;
-const uptr kMetaShadowBeg = 0x4000000000ull;
-const uptr kMetaShadowEnd = 0x5000000000ull;
-const uptr kTraceMemBeg   = 0x6000000000ull;
-const uptr kTraceMemEnd   = 0x6200000000ull;
-const uptr kHeapMemBeg    = 0x7d00000000ull;
-const uptr kHeapMemEnd    = 0x7e00000000ull;
-const uptr kHiAppMemBeg   = 0x7e00000000ull;
-const uptr kHiAppMemEnd   = 0x7fffffffffull;
-const uptr kAppMemMsk     = 0x7800000000ull;
-const uptr kAppMemXor     = 0x0800000000ull;
-const uptr kVdsoBeg       = 0x7f00000000ull;
-# elif SANITIZER_AARCH64_VMA == 42
+struct Mapping39 {
+  static const uptr kLoAppMemBeg   = 0x0000001000ull;
+  static const uptr kLoAppMemEnd   = 0x0100000000ull;
+  static const uptr kShadowBeg     = 0x0800000000ull;
+  static const uptr kShadowEnd     = 0x2000000000ull;
+  static const uptr kMetaShadowBeg = 0x3100000000ull;
+  static const uptr kMetaShadowEnd = 0x3400000000ull;
+  static const uptr kMidAppMemBeg  = 0x5500000000ull;
+  static const uptr kMidAppMemEnd  = 0x5600000000ull;
+  static const uptr kMidShadowOff  = 0x5000000000ull;
+  static const uptr kTraceMemBeg   = 0x6000000000ull;
+  static const uptr kTraceMemEnd   = 0x6200000000ull;
+  static const uptr kHeapMemBeg    = 0x7c00000000ull;
+  static const uptr kHeapMemEnd    = 0x7d00000000ull;
+  static const uptr kHiAppMemBeg   = 0x7e00000000ull;
+  static const uptr kHiAppMemEnd   = 0x7fffffffffull;
+  static const uptr kAppMemMsk     = 0x7800000000ull;
+  static const uptr kAppMemXor     = 0x0200000000ull;
+  static const uptr kVdsoBeg       = 0x7f00000000ull;
+};
+
 /*
 C/C++ on linux/aarch64 (42-bit VMA)
-00000 4000 00 - 01000 0000 00: main binary
+00000 0010 00 - 01000 0000 00: main binary
 01000 0000 00 - 10000 0000 00: -
 10000 0000 00 - 20000 0000 00: shadow memory
 20000 0000 00 - 26000 0000 00: -
 26000 0000 00 - 28000 0000 00: metainfo
-28000 0000 00 - 36200 0000 00: -
+28000 0000 00 - 2aa00 0000 00: -
+2aa00 0000 00 - 2ab00 0000 00: main binary (PIE)
+2ab00 0000 00 - 36200 0000 00: -
 36200 0000 00 - 36240 0000 00: traces
 36240 0000 00 - 3e000 0000 00: -
 3e000 0000 00 - 3f000 0000 00: heap
-3c000 0000 00 - 3ff00 0000 00: -
-3ff00 0000 00 - 3ffff f000 00: modules and main thread stack
+3f000 0000 00 - 3ffff ffff ff: modules and main thread stack
 */
-const uptr kLoAppMemBeg   = 0x00000400000ull;
-const uptr kLoAppMemEnd   = 0x01000000000ull;
-const uptr kShadowBeg     = 0x10000000000ull;
-const uptr kShadowEnd     = 0x20000000000ull;
-const uptr kMetaShadowBeg = 0x26000000000ull;
-const uptr kMetaShadowEnd = 0x28000000000ull;
-const uptr kTraceMemBeg   = 0x36200000000ull;
-const uptr kTraceMemEnd   = 0x36400000000ull;
-const uptr kHeapMemBeg    = 0x3e000000000ull;
-const uptr kHeapMemEnd    = 0x3f000000000ull;
-const uptr kHiAppMemBeg   = 0x3ff00000000ull;
-const uptr kHiAppMemEnd   = 0x3fffff00000ull;
-const uptr kAppMemMsk     = 0x3c000000000ull;
-const uptr kAppMemXor     = 0x04000000000ull;
-const uptr kVdsoBeg       = 0x37f00000000ull;
-# endif
-#endif
-
-ALWAYS_INLINE
-bool IsAppMem(uptr mem) {
-  return (mem >= kHeapMemBeg && mem < kHeapMemEnd) ||
-         (mem >= kLoAppMemBeg && mem < kLoAppMemEnd) ||
-         (mem >= kHiAppMemBeg && mem < kHiAppMemEnd);
-}
-
-ALWAYS_INLINE
-bool IsShadowMem(uptr mem) {
-  return mem >= kShadowBeg && mem <= kShadowEnd;
-}
+struct Mapping42 {
+  static const uptr kLoAppMemBeg   = 0x00000001000ull;
+  static const uptr kLoAppMemEnd   = 0x01000000000ull;
+  static const uptr kShadowBeg     = 0x10000000000ull;
+  static const uptr kShadowEnd     = 0x20000000000ull;
+  static const uptr kMetaShadowBeg = 0x26000000000ull;
+  static const uptr kMetaShadowEnd = 0x28000000000ull;
+  static const uptr kMidAppMemBeg  = 0x2aa00000000ull;
+  static const uptr kMidAppMemEnd  = 0x2ab00000000ull;
+  static const uptr kMidShadowOff  = 0x28000000000ull;
+  static const uptr kTraceMemBeg   = 0x36200000000ull;
+  static const uptr kTraceMemEnd   = 0x36400000000ull;
+  static const uptr kHeapMemBeg    = 0x3e000000000ull;
+  static const uptr kHeapMemEnd    = 0x3f000000000ull;
+  static const uptr kHiAppMemBeg   = 0x3f000000000ull;
+  static const uptr kHiAppMemEnd   = 0x3ffffffffffull;
+  static const uptr kAppMemMsk     = 0x3c000000000ull;
+  static const uptr kAppMemXor     = 0x04000000000ull;
+  static const uptr kVdsoBeg       = 0x37f00000000ull;
+};
 
-ALWAYS_INLINE
-bool IsMetaMem(uptr mem) {
-  return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
-}
+// Indicates the runtime will define the memory regions at runtime.
+#define TSAN_RUNTIME_VMA 1
+// Indicates that mapping defines a mid range memory segment.
+#define TSAN_MID_APP_RANGE 1
+#elif defined(__powerpc64__)
+// PPC64 supports multiple VMA which leads to multiple address transformation
+// functions.  To support these multiple VMAS transformations and mappings TSAN
+// runtime for PPC64 uses an external memory read (vmaSize) to select which
+// mapping to use.  Although slower, it make a same instrumented binary run on
+// multiple kernels.
 
-ALWAYS_INLINE
-uptr MemToShadow(uptr x) {
-  DCHECK(IsAppMem(x));
-  return (((x) & ~(kAppMemMsk | (kShadowCell - 1)))
-      ^ kAppMemXor) * kShadowCnt;
-}
-
-ALWAYS_INLINE
-u32 *MemToMeta(uptr x) {
-  DCHECK(IsAppMem(x));
-  return (u32*)(((((x) & ~(kAppMemMsk | (kMetaShadowCell - 1)))
-      ^ kAppMemXor) / kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
-}
-
-ALWAYS_INLINE
-uptr ShadowToMem(uptr s) {
-  CHECK(IsShadowMem(s));
-  if (s >= MemToShadow(kLoAppMemBeg) && s <= MemToShadow(kLoAppMemEnd - 1))
-    return (s / kShadowCnt) ^ kAppMemXor;
-  else
-    return ((s / kShadowCnt) ^ kAppMemXor) | kAppMemMsk;
-}
+/*
+C/C++ on linux/powerpc64 (44-bit VMA)
+0000 0000 0100 - 0001 0000 0000: main binary
+0001 0000 0000 - 0001 0000 0000: -
+0001 0000 0000 - 0b00 0000 0000: shadow
+0b00 0000 0000 - 0b00 0000 0000: -
+0b00 0000 0000 - 0d00 0000 0000: metainfo (memory blocks and sync objects)
+0d00 0000 0000 - 0d00 0000 0000: -
+0d00 0000 0000 - 0f00 0000 0000: traces
+0f00 0000 0000 - 0f00 0000 0000: -
+0f00 0000 0000 - 0f50 0000 0000: heap
+0f50 0000 0000 - 0f60 0000 0000: -
+0f60 0000 0000 - 1000 0000 0000: modules and main thread stack
+*/
+struct Mapping44 {
+  static const uptr kMetaShadowBeg = 0x0b0000000000ull;
+  static const uptr kMetaShadowEnd = 0x0d0000000000ull;
+  static const uptr kTraceMemBeg   = 0x0d0000000000ull;
+  static const uptr kTraceMemEnd   = 0x0f0000000000ull;
+  static const uptr kShadowBeg     = 0x000100000000ull;
+  static const uptr kShadowEnd     = 0x0b0000000000ull;
+  static const uptr kLoAppMemBeg   = 0x000000000100ull;
+  static const uptr kLoAppMemEnd   = 0x000100000000ull;
+  static const uptr kHeapMemBeg    = 0x0f0000000000ull;
+  static const uptr kHeapMemEnd    = 0x0f5000000000ull;
+  static const uptr kHiAppMemBeg   = 0x0f6000000000ull;
+  static const uptr kHiAppMemEnd   = 0x100000000000ull; // 44 bits
+  static const uptr kAppMemMsk     = 0x0f0000000000ull;
+  static const uptr kAppMemXor     = 0x002100000000ull;
+  static const uptr kVdsoBeg       = 0x3c0000000000000ull;
+};
 
-static USED uptr UserRegions[] = {
-  kLoAppMemBeg, kLoAppMemEnd,
-  kHiAppMemBeg, kHiAppMemEnd,
-  kHeapMemBeg,  kHeapMemEnd,
+/*
+C/C++ on linux/powerpc64 (46-bit VMA)
+0000 0000 1000 - 0100 0000 0000: main binary
+0100 0000 0000 - 0200 0000 0000: -
+0100 0000 0000 - 1000 0000 0000: shadow
+1000 0000 0000 - 1000 0000 0000: -
+1000 0000 0000 - 2000 0000 0000: metainfo (memory blocks and sync objects)
+2000 0000 0000 - 2000 0000 0000: -
+2000 0000 0000 - 2200 0000 0000: traces
+2200 0000 0000 - 3d00 0000 0000: -
+3d00 0000 0000 - 3e00 0000 0000: heap
+3e00 0000 0000 - 3e80 0000 0000: -
+3e80 0000 0000 - 4000 0000 0000: modules and main thread stack
+*/
+struct Mapping46 {
+  static const uptr kMetaShadowBeg = 0x100000000000ull;
+  static const uptr kMetaShadowEnd = 0x200000000000ull;
+  static const uptr kTraceMemBeg   = 0x200000000000ull;
+  static const uptr kTraceMemEnd   = 0x220000000000ull;
+  static const uptr kShadowBeg     = 0x010000000000ull;
+  static const uptr kShadowEnd     = 0x100000000000ull;
+  static const uptr kHeapMemBeg    = 0x3d0000000000ull;
+  static const uptr kHeapMemEnd    = 0x3e0000000000ull;
+  static const uptr kLoAppMemBeg   = 0x000000001000ull;
+  static const uptr kLoAppMemEnd   = 0x010000000000ull;
+  static const uptr kHiAppMemBeg   = 0x3e8000000000ull;
+  static const uptr kHiAppMemEnd   = 0x400000000000ull; // 46 bits
+  static const uptr kAppMemMsk     = 0x3c0000000000ull;
+  static const uptr kAppMemXor     = 0x020000000000ull;
+  static const uptr kVdsoBeg       = 0x7800000000000000ull;
 };
 
+// Indicates the runtime will define the memory regions at runtime.
+#define TSAN_RUNTIME_VMA 1
+#endif
+
 #elif defined(SANITIZER_GO) && !SANITIZER_WINDOWS
 
 /* Go on linux, darwin and freebsd
@@ -208,138 +263,493 @@ static USED uptr UserRegions[] = {
 6200 0000 0000 - 8000 0000 0000: -
 */
 
-const uptr kMetaShadowBeg = 0x300000000000ull;
-const uptr kMetaShadowEnd = 0x400000000000ull;
-const uptr kTraceMemBeg   = 0x600000000000ull;
-const uptr kTraceMemEnd   = 0x620000000000ull;
-const uptr kShadowBeg     = 0x200000000000ull;
-const uptr kShadowEnd     = 0x238000000000ull;
-const uptr kAppMemBeg     = 0x000000001000ull;
-const uptr kAppMemEnd     = 0x00e000000000ull;
+struct Mapping {
+  static const uptr kMetaShadowBeg = 0x300000000000ull;
+  static const uptr kMetaShadowEnd = 0x400000000000ull;
+  static const uptr kTraceMemBeg   = 0x600000000000ull;
+  static const uptr kTraceMemEnd   = 0x620000000000ull;
+  static const uptr kShadowBeg     = 0x200000000000ull;
+  static const uptr kShadowEnd     = 0x238000000000ull;
+  static const uptr kAppMemBeg     = 0x000000001000ull;
+  static const uptr kAppMemEnd     = 0x00e000000000ull;
+};
+
+#elif defined(SANITIZER_GO) && SANITIZER_WINDOWS
+
+/* Go on windows
+0000 0000 1000 - 0000 1000 0000: executable
+0000 1000 0000 - 00f8 0000 0000: -
+00c0 0000 0000 - 00e0 0000 0000: heap
+00e0 0000 0000 - 0100 0000 0000: -
+0100 0000 0000 - 0500 0000 0000: shadow
+0500 0000 0000 - 0560 0000 0000: -
+0560 0000 0000 - 0760 0000 0000: traces
+0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects)
+07d0 0000 0000 - 8000 0000 0000: -
+*/
+
+struct Mapping {
+  static const uptr kMetaShadowBeg = 0x076000000000ull;
+  static const uptr kMetaShadowEnd = 0x07d000000000ull;
+  static const uptr kTraceMemBeg   = 0x056000000000ull;
+  static const uptr kTraceMemEnd   = 0x076000000000ull;
+  static const uptr kShadowBeg     = 0x010000000000ull;
+  static const uptr kShadowEnd     = 0x050000000000ull;
+  static const uptr kAppMemBeg     = 0x000000001000ull;
+  static const uptr kAppMemEnd     = 0x00e000000000ull;
+}
+
+#else
+# error "Unknown platform"
+#endif
+
+
+#ifdef TSAN_RUNTIME_VMA
+extern uptr vmaSize;
+#endif
+
+
+enum MappingType {
+  MAPPING_LO_APP_BEG,
+  MAPPING_LO_APP_END,
+  MAPPING_HI_APP_BEG,
+  MAPPING_HI_APP_END,
+#ifdef TSAN_MID_APP_RANGE
+  MAPPING_MID_APP_BEG,
+  MAPPING_MID_APP_END,
+#endif
+  MAPPING_HEAP_BEG,
+  MAPPING_HEAP_END,
+  MAPPING_APP_BEG,
+  MAPPING_APP_END,
+  MAPPING_SHADOW_BEG,
+  MAPPING_SHADOW_END,
+  MAPPING_META_SHADOW_BEG,
+  MAPPING_META_SHADOW_END,
+  MAPPING_TRACE_BEG,
+  MAPPING_TRACE_END,
+  MAPPING_VDSO_BEG,
+};
+
+template<typename Mapping, int Type>
+uptr MappingImpl(void) {
+  switch (Type) {
+#ifndef SANITIZER_GO
+    case MAPPING_LO_APP_BEG: return Mapping::kLoAppMemBeg;
+    case MAPPING_LO_APP_END: return Mapping::kLoAppMemEnd;
+# ifdef TSAN_MID_APP_RANGE
+    case MAPPING_MID_APP_BEG: return Mapping::kMidAppMemBeg;
+    case MAPPING_MID_APP_END: return Mapping::kMidAppMemEnd;
+# endif
+    case MAPPING_HI_APP_BEG: return Mapping::kHiAppMemBeg;
+    case MAPPING_HI_APP_END: return Mapping::kHiAppMemEnd;
+    case MAPPING_HEAP_BEG: return Mapping::kHeapMemBeg;
+    case MAPPING_HEAP_END: return Mapping::kHeapMemEnd;
+    case MAPPING_VDSO_BEG: return Mapping::kVdsoBeg;
+#else
+    case MAPPING_APP_BEG: return Mapping::kAppMemBeg;
+    case MAPPING_APP_END: return Mapping::kAppMemEnd;
+#endif
+    case MAPPING_SHADOW_BEG: return Mapping::kShadowBeg;
+    case MAPPING_SHADOW_END: return Mapping::kShadowEnd;
+    case MAPPING_META_SHADOW_BEG: return Mapping::kMetaShadowBeg;
+    case MAPPING_META_SHADOW_END: return Mapping::kMetaShadowEnd;
+    case MAPPING_TRACE_BEG: return Mapping::kTraceMemBeg;
+    case MAPPING_TRACE_END: return Mapping::kTraceMemEnd;
+  }
+}
+
+template<int Type>
+uptr MappingArchImpl(void) {
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return MappingImpl<Mapping39, Type>();
+  else
+    return MappingImpl<Mapping42, Type>();
+  DCHECK(0);
+#elif defined(__powerpc64__)
+  if (vmaSize == 44)
+    return MappingImpl<Mapping44, Type>();
+  else
+    return MappingImpl<Mapping46, Type>();
+  DCHECK(0);
+#else
+  return MappingImpl<Mapping, Type>();
+#endif
+}
 
+#ifndef SANITIZER_GO
 ALWAYS_INLINE
-bool IsAppMem(uptr mem) {
-  return mem >= kAppMemBeg && mem < kAppMemEnd;
+uptr LoAppMemBeg(void) {
+  return MappingArchImpl<MAPPING_LO_APP_BEG>();
+}
+ALWAYS_INLINE
+uptr LoAppMemEnd(void) {
+  return MappingArchImpl<MAPPING_LO_APP_END>();
 }
 
+#ifdef TSAN_MID_APP_RANGE
 ALWAYS_INLINE
-bool IsShadowMem(uptr mem) {
-  return mem >= kShadowBeg && mem <= kShadowEnd;
+uptr MidAppMemBeg(void) {
+  return MappingArchImpl<MAPPING_MID_APP_BEG>();
 }
+ALWAYS_INLINE
+uptr MidAppMemEnd(void) {
+  return MappingArchImpl<MAPPING_MID_APP_END>();
+}
+#endif
 
 ALWAYS_INLINE
-bool IsMetaMem(uptr mem) {
-  return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
+uptr HeapMemBeg(void) {
+  return MappingArchImpl<MAPPING_HEAP_BEG>();
+}
+ALWAYS_INLINE
+uptr HeapMemEnd(void) {
+  return MappingArchImpl<MAPPING_HEAP_END>();
 }
 
 ALWAYS_INLINE
-uptr MemToShadow(uptr x) {
-  DCHECK(IsAppMem(x));
-  return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg;
+uptr HiAppMemBeg(void) {
+  return MappingArchImpl<MAPPING_HI_APP_BEG>();
+}
+ALWAYS_INLINE
+uptr HiAppMemEnd(void) {
+  return MappingArchImpl<MAPPING_HI_APP_END>();
 }
 
 ALWAYS_INLINE
-u32 *MemToMeta(uptr x) {
-  DCHECK(IsAppMem(x));
-  return (u32*)(((x & ~(kMetaShadowCell - 1)) / \
-      kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
+uptr VdsoBeg(void) {
+  return MappingArchImpl<MAPPING_VDSO_BEG>();
 }
 
+#else
+
 ALWAYS_INLINE
-uptr ShadowToMem(uptr s) {
-  CHECK(IsShadowMem(s));
-  return (s & ~kShadowBeg) / kShadowCnt;
+uptr AppMemBeg(void) {
+  return MappingArchImpl<MAPPING_APP_BEG>();
+}
+ALWAYS_INLINE
+uptr AppMemEnd(void) {
+  return MappingArchImpl<MAPPING_APP_END>();
 }
 
-static USED uptr UserRegions[] = {
-  kAppMemBeg, kAppMemEnd,
-};
+#endif
 
-#elif defined(SANITIZER_GO) && SANITIZER_WINDOWS
+static inline
+bool GetUserRegion(int i, uptr *start, uptr *end) {
+  switch (i) {
+  default:
+    return false;
+#ifndef SANITIZER_GO
+  case 0:
+    *start = LoAppMemBeg();
+    *end = LoAppMemEnd();
+    return true;
+  case 1:
+    *start = HiAppMemBeg();
+    *end = HiAppMemEnd();
+    return true;
+  case 2:
+    *start = HeapMemBeg();
+    *end = HeapMemEnd();
+    return true;
+# ifdef TSAN_MID_APP_RANGE
+  case 3:
+    *start = MidAppMemBeg();
+    *end = MidAppMemEnd();
+    return true;
+# endif
+#else
+  case 0:
+    *start = AppMemBeg();
+    *end = AppMemEnd();
+    return true;
+#endif
+  }
+}
 
-/* Go on windows
-0000 0000 1000 - 0000 1000 0000: executable
-0000 1000 0000 - 00f8 0000 0000: -
-00c0 0000 0000 - 00e0 0000 0000: heap
-00e0 0000 0000 - 0100 0000 0000: -
-0100 0000 0000 - 0500 0000 0000: shadow
-0500 0000 0000 - 0560 0000 0000: -
-0560 0000 0000 - 0760 0000 0000: traces
-0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects)
-07d0 0000 0000 - 8000 0000 0000: -
-*/
+ALWAYS_INLINE
+uptr ShadowBeg(void) {
+  return MappingArchImpl<MAPPING_SHADOW_BEG>();
+}
+ALWAYS_INLINE
+uptr ShadowEnd(void) {
+  return MappingArchImpl<MAPPING_SHADOW_END>();
+}
 
-const uptr kMetaShadowBeg = 0x076000000000ull;
-const uptr kMetaShadowEnd = 0x07d000000000ull;
-const uptr kTraceMemBeg   = 0x056000000000ull;
-const uptr kTraceMemEnd   = 0x076000000000ull;
-const uptr kShadowBeg     = 0x010000000000ull;
-const uptr kShadowEnd     = 0x050000000000ull;
-const uptr kAppMemBeg     = 0x000000001000ull;
-const uptr kAppMemEnd     = 0x00e000000000ull;
+ALWAYS_INLINE
+uptr MetaShadowBeg(void) {
+  return MappingArchImpl<MAPPING_META_SHADOW_BEG>();
+}
+ALWAYS_INLINE
+uptr MetaShadowEnd(void) {
+  return MappingArchImpl<MAPPING_META_SHADOW_END>();
+}
+
+ALWAYS_INLINE
+uptr TraceMemBeg(void) {
+  return MappingArchImpl<MAPPING_TRACE_BEG>();
+}
+ALWAYS_INLINE
+uptr TraceMemEnd(void) {
+  return MappingArchImpl<MAPPING_TRACE_END>();
+}
+
+
+template<typename Mapping>
+bool IsAppMemImpl(uptr mem) {
+#ifndef SANITIZER_GO
+  return (mem >= Mapping::kHeapMemBeg && mem < Mapping::kHeapMemEnd) ||
+# ifdef TSAN_MID_APP_RANGE
+         (mem >= Mapping::kMidAppMemBeg && mem < Mapping::kMidAppMemEnd) ||
+# endif
+         (mem >= Mapping::kLoAppMemBeg && mem < Mapping::kLoAppMemEnd) ||
+         (mem >= Mapping::kHiAppMemBeg && mem < Mapping::kHiAppMemEnd);
+#else
+  return mem >= Mapping::kAppMemBeg && mem < Mapping::kAppMemEnd;
+#endif
+}
 
 ALWAYS_INLINE
 bool IsAppMem(uptr mem) {
-  return mem >= kAppMemBeg && mem < kAppMemEnd;
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return IsAppMemImpl<Mapping39>(mem);
+  else
+    return IsAppMemImpl<Mapping42>(mem);
+  DCHECK(0);
+#elif defined(__powerpc64__)
+  if (vmaSize == 44)
+    return IsAppMemImpl<Mapping44>(mem);
+  else
+    return IsAppMemImpl<Mapping46>(mem);
+  DCHECK(0);
+#else
+  return IsAppMemImpl<Mapping>(mem);
+#endif
+}
+
+
+template<typename Mapping>
+bool IsShadowMemImpl(uptr mem) {
+  return mem >= Mapping::kShadowBeg && mem <= Mapping::kShadowEnd;
 }
 
 ALWAYS_INLINE
 bool IsShadowMem(uptr mem) {
-  return mem >= kShadowBeg && mem <= kShadowEnd;
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return IsShadowMemImpl<Mapping39>(mem);
+  else
+    return IsShadowMemImpl<Mapping42>(mem);
+  DCHECK(0);
+#elif defined(__powerpc64__)
+  if (vmaSize == 44)
+    return IsShadowMemImpl<Mapping44>(mem);
+  else
+    return IsShadowMemImpl<Mapping46>(mem);
+  DCHECK(0);
+#else
+  return IsShadowMemImpl<Mapping>(mem);
+#endif
+}
+
+
+template<typename Mapping>
+bool IsMetaMemImpl(uptr mem) {
+  return mem >= Mapping::kMetaShadowBeg && mem <= Mapping::kMetaShadowEnd;
 }
 
 ALWAYS_INLINE
 bool IsMetaMem(uptr mem) {
-  return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return IsMetaMemImpl<Mapping39>(mem);
+  else
+    return IsMetaMemImpl<Mapping42>(mem);
+  DCHECK(0);
+#elif defined(__powerpc64__)
+  if (vmaSize == 44)
+    return IsMetaMemImpl<Mapping44>(mem);
+  else
+    return IsMetaMemImpl<Mapping46>(mem);
+  DCHECK(0);
+#else
+  return IsMetaMemImpl<Mapping>(mem);
+#endif
 }
 
-ALWAYS_INLINE
-uptr MemToShadow(uptr x) {
+
+template<typename Mapping>
+uptr MemToShadowImpl(uptr x) {
   DCHECK(IsAppMem(x));
-  return ((x & ~(kShadowCell - 1)) * kShadowCnt) + kShadowBeg;
+#ifndef SANITIZER_GO
+  return (((x) & ~(Mapping::kAppMemMsk | (kShadowCell - 1)))
+      ^ Mapping::kAppMemXor) * kShadowCnt;
+#else
+  return ((x & ~(kShadowCell - 1)) * kShadowCnt) | Mapping::kShadowBeg;
+#endif
 }
 
 ALWAYS_INLINE
-u32 *MemToMeta(uptr x) {
+uptr MemToShadow(uptr x) {
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return MemToShadowImpl<Mapping39>(x);
+  else
+    return MemToShadowImpl<Mapping42>(x);
+  DCHECK(0);
+#elif defined(__powerpc64__)
+  if (vmaSize == 44)
+    return MemToShadowImpl<Mapping44>(x);
+  else
+    return MemToShadowImpl<Mapping46>(x);
+  DCHECK(0);
+#else
+  return MemToShadowImpl<Mapping>(x);
+#endif
+}
+
+
+template<typename Mapping>
+u32 *MemToMetaImpl(uptr x) {
   DCHECK(IsAppMem(x));
+#ifndef SANITIZER_GO
+  return (u32*)(((((x) & ~(Mapping::kAppMemMsk | (kMetaShadowCell - 1)))
+        ^ Mapping::kAppMemXor) / kMetaShadowCell * kMetaShadowSize)
+          | Mapping::kMetaShadowBeg);
+#else
   return (u32*)(((x & ~(kMetaShadowCell - 1)) / \
-      kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
+      kMetaShadowCell * kMetaShadowSize) | Mapping::kMetaShadowBeg);
+#endif
 }
 
 ALWAYS_INLINE
-uptr ShadowToMem(uptr s) {
-  CHECK(IsShadowMem(s));
-  // FIXME(dvyukov): this is most likely wrong as the mapping is not bijection.
-  return (s - kShadowBeg) / kShadowCnt;
+u32 *MemToMeta(uptr x) {
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return MemToMetaImpl<Mapping39>(x);
+  else
+    return MemToMetaImpl<Mapping42>(x);
+  DCHECK(0);
+#elif defined(__powerpc64__)
+  if (vmaSize == 44)
+    return MemToMetaImpl<Mapping44>(x);
+  else
+    return MemToMetaImpl<Mapping46>(x);
+  DCHECK(0);
+#else
+  return MemToMetaImpl<Mapping>(x);
+#endif
 }
 
-static USED uptr UserRegions[] = {
-  kAppMemBeg, kAppMemEnd,
-};
 
+template<typename Mapping>
+uptr ShadowToMemImpl(uptr s) {
+  DCHECK(IsShadowMem(s));
+#ifndef SANITIZER_GO
+  if (s >= MemToShadow(Mapping::kLoAppMemBeg)
+      && s <= MemToShadow(Mapping::kLoAppMemEnd - 1))
+    return (s / kShadowCnt) ^ Mapping::kAppMemXor;
+# ifdef TSAN_MID_APP_RANGE
+  if (s >= MemToShadow(Mapping::kMidAppMemBeg)
+      && s <= MemToShadow(Mapping::kMidAppMemEnd - 1))
+    return ((s / kShadowCnt) ^ Mapping::kAppMemXor) + Mapping::kMidShadowOff;
+# endif
+  else
+    return ((s / kShadowCnt) ^ Mapping::kAppMemXor) | Mapping::kAppMemMsk;
 #else
-# error "Unknown platform"
+# ifndef SANITIZER_WINDOWS
+  return (s & ~Mapping::kShadowBeg) / kShadowCnt;
+# else
+  // FIXME(dvyukov): this is most likely wrong as the mapping is not bijection.
+  return (s - Mapping::kShadowBeg) / kShadowCnt;
+# endif // SANITIZER_WINDOWS
 #endif
+}
+
+ALWAYS_INLINE
+uptr ShadowToMem(uptr s) {
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return ShadowToMemImpl<Mapping39>(s);
+  else
+    return ShadowToMemImpl<Mapping42>(s);
+  DCHECK(0);
+#elif defined(__powerpc64__)
+  if (vmaSize == 44)
+    return ShadowToMemImpl<Mapping44>(s);
+  else
+    return ShadowToMemImpl<Mapping46>(s);
+  DCHECK(0);
+#else
+  return ShadowToMemImpl<Mapping>(s);
+#endif
+}
+
+
 
 // The additional page is to catch shadow stack overflow as paging fault.
 // Windows wants 64K alignment for mmaps.
 const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace)
     + (64 << 10) + (64 << 10) - 1) & ~((64 << 10) - 1);
 
-uptr ALWAYS_INLINE GetThreadTrace(int tid) {
-  uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize;
-  DCHECK_LT(p, kTraceMemEnd);
+template<typename Mapping>
+uptr GetThreadTraceImpl(int tid) {
+  uptr p = Mapping::kTraceMemBeg + (uptr)tid * kTotalTraceSize;
+  DCHECK_LT(p, Mapping::kTraceMemEnd);
   return p;
 }
 
-uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) {
-  uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize
+ALWAYS_INLINE
+uptr GetThreadTrace(int tid) {
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return GetThreadTraceImpl<Mapping39>(tid);
+  else
+    return GetThreadTraceImpl<Mapping42>(tid);
+  DCHECK(0);
+#elif defined(__powerpc64__)
+  if (vmaSize == 44)
+    return GetThreadTraceImpl<Mapping44>(tid);
+  else
+    return GetThreadTraceImpl<Mapping46>(tid);
+  DCHECK(0);
+#else
+  return GetThreadTraceImpl<Mapping>(tid);
+#endif
+}
+
+
+template<typename Mapping>
+uptr GetThreadTraceHeaderImpl(int tid) {
+  uptr p = Mapping::kTraceMemBeg + (uptr)tid * kTotalTraceSize
       + kTraceSize * sizeof(Event);
-  DCHECK_LT(p, kTraceMemEnd);
+  DCHECK_LT(p, Mapping::kTraceMemEnd);
   return p;
 }
 
+ALWAYS_INLINE
+uptr GetThreadTraceHeader(int tid) {
+#ifdef __aarch64__
+  if (vmaSize == 39)
+    return GetThreadTraceHeaderImpl<Mapping39>(tid);
+  else
+    return GetThreadTraceHeaderImpl<Mapping42>(tid);
+  DCHECK(0);
+#elif defined(__powerpc64__)
+  if (vmaSize == 44)
+    return GetThreadTraceHeaderImpl<Mapping44>(tid);
+  else
+    return GetThreadTraceHeaderImpl<Mapping46>(tid);
+  DCHECK(0);
+#else
+  return GetThreadTraceHeaderImpl<Mapping>(tid);
+#endif
+}
+
 void InitializePlatform();
+void InitializePlatformEarly();
 void CheckAndProtect();
 void InitializeShadowMemoryPlatform();
 void FlushShadowMemory();
index f6c704051c84298cfe948611d2328e0a0ab62f8e..6602561186ce0d795900d9d267252b3943ed7a94 100644 (file)
@@ -67,6 +67,11 @@ namespace __tsan {
 static uptr g_data_start;
 static uptr g_data_end;
 
+#ifdef TSAN_RUNTIME_VMA
+// Runtime detected VMA size.
+uptr vmaSize;
+#endif
+
 enum {
   MemTotal  = 0,
   MemShadow = 1,
@@ -82,29 +87,30 @@ enum {
 void FillProfileCallback(uptr p, uptr rss, bool file,
                          uptr *mem, uptr stats_size) {
   mem[MemTotal] += rss;
-  if (p >= kShadowBeg && p < kShadowEnd)
+  if (p >= ShadowBeg() && p < ShadowEnd())
     mem[MemShadow] += rss;
-  else if (p >= kMetaShadowBeg && p < kMetaShadowEnd)
+  else if (p >= MetaShadowBeg() && p < MetaShadowEnd())
     mem[MemMeta] += rss;
 #ifndef SANITIZER_GO
-  else if (p >= kHeapMemBeg && p < kHeapMemEnd)
+  else if (p >= HeapMemBeg() && p < HeapMemEnd())
     mem[MemHeap] += rss;
-  else if (p >= kLoAppMemBeg && p < kLoAppMemEnd)
+  else if (p >= LoAppMemBeg() && p < LoAppMemEnd())
     mem[file ? MemFile : MemMmap] += rss;
-  else if (p >= kHiAppMemBeg && p < kHiAppMemEnd)
+  else if (p >= HiAppMemBeg() && p < HiAppMemEnd())
     mem[file ? MemFile : MemMmap] += rss;
 #else
-  else if (p >= kAppMemBeg && p < kAppMemEnd)
+  else if (p >= AppMemBeg() && p < AppMemEnd())
     mem[file ? MemFile : MemMmap] += rss;
 #endif
-  else if (p >= kTraceMemBeg && p < kTraceMemEnd)
+  else if (p >= TraceMemBeg() && p < TraceMemEnd())
     mem[MemTrace] += rss;
   else
     mem[MemOther] += rss;
 }
 
 void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
-  uptr mem[MemCount] = {};
+  uptr mem[MemCount];
+  internal_memset(mem, 0, sizeof(mem[0]) * MemCount);
   __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
   StackDepotStats *stacks = StackDepotGetStats();
   internal_snprintf(buf, buf_size,
@@ -121,7 +127,7 @@ void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
 void FlushShadowMemoryCallback(
     const SuspendedThreadsList &suspended_threads_list,
     void *argument) {
-  FlushUnneededShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
+  FlushUnneededShadowMemory(ShadowBeg(), ShadowEnd() - ShadowBeg());
 }
 #endif
 
@@ -235,6 +241,26 @@ static void InitDataSeg() {
 
 #endif  // #ifndef SANITIZER_GO
 
+void InitializePlatformEarly() {
+#ifdef TSAN_RUNTIME_VMA
+  vmaSize =
+    (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+#if defined(__aarch64__)
+  if (vmaSize != 39 && vmaSize != 42) {
+    Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
+    Printf("FATAL: Found %d - Supported 39 and 42\n", vmaSize);
+    Die();
+  }
+#elif defined(__powerpc64__)
+  if (vmaSize != 44 && vmaSize != 46) {
+    Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
+    Printf("FATAL: Found %d - Supported 44 and 46\n", vmaSize);
+    Die();
+  }
+#endif
+#endif
+}
+
 void InitializePlatform() {
   DisableCoreDumperIfNecessary();
 
@@ -283,7 +309,7 @@ bool IsGlobalVar(uptr addr) {
 // This is required to properly "close" the fds, because we do not see internal
 // closes within glibc. The code is a pure hack.
 int ExtractResolvFDs(void *state, int *fds, int nfd) {
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
   int cnt = 0;
   __res_state *statp = (__res_state*)state;
   for (int i = 0; i < MAXNS && cnt < nfd; i++) {
index 446d79001990cd6558ac8dab5fa63a16cafd7c8f..31caf37dee5a8885e242f8b39817b25c8faf02db 100644 (file)
@@ -42,6 +42,7 @@
 
 namespace __tsan {
 
+#ifndef SANITIZER_GO
 static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) {
   atomic_uintptr_t *a = (atomic_uintptr_t *)dst;
   void *val = (void *)atomic_load_relaxed(a);
@@ -61,7 +62,6 @@ static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) {
   return val;
 }
 
-#ifndef SANITIZER_GO
 // On OS X, accessing TLVs via __thread or manually by using pthread_key_* is
 // problematic, because there are several places where interceptors are called
 // when TLVs are not accessible (early process startup, thread cleanup, ...).
@@ -110,7 +110,6 @@ void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
 
 #ifndef SANITIZER_GO
 void InitializeShadowMemoryPlatform() { }
-#endif
 
 // On OS X, GCD worker threads are created without a call to pthread_create. We
 // need to properly register these threads with ThreadCreate and ThreadStart.
@@ -125,7 +124,7 @@ typedef void (*pthread_introspection_hook_t)(unsigned int event,
 extern "C" pthread_introspection_hook_t pthread_introspection_hook_install(
     pthread_introspection_hook_t hook);
 static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1;
-static const uptr PTHREAD_INTROSPECTION_THREAD_DESTROY = 4;
+static const uptr PTHREAD_INTROSPECTION_THREAD_TERMINATE = 3;
 static pthread_introspection_hook_t prev_pthread_introspection_hook;
 static void my_pthread_introspection_hook(unsigned int event, pthread_t thread,
                                           void *addr, size_t size) {
@@ -138,16 +137,22 @@ static void my_pthread_introspection_hook(unsigned int event, pthread_t thread,
       ThreadState *thr = cur_thread();
       ThreadStart(thr, tid, GetTid());
     }
-  } else if (event == PTHREAD_INTROSPECTION_THREAD_DESTROY) {
-    ThreadState *thr = cur_thread();
-    if (thr->tctx->parent_tid == kInvalidTid) {
-      DestroyThreadState();
+  } else if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) {
+    if (thread == pthread_self()) {
+      ThreadState *thr = cur_thread();
+      if (thr->tctx) {
+        DestroyThreadState();
+      }
     }
   }
 
   if (prev_pthread_introspection_hook != nullptr)
     prev_pthread_introspection_hook(event, thread, addr, size);
 }
+#endif
+
+void InitializePlatformEarly() {
+}
 
 void InitializePlatform() {
   DisableCoreDumperIfNecessary();
@@ -156,10 +161,10 @@ void InitializePlatform() {
 
   CHECK_EQ(main_thread_identity, 0);
   main_thread_identity = (uptr)pthread_self();
-#endif
 
   prev_pthread_introspection_hook =
       pthread_introspection_hook_install(&my_pthread_introspection_hook);
+#endif
 }
 
 #ifndef SANITIZER_GO
index 1782bf1407ae5d6fafd8fe59d4828ea15f9d394c..90476cbc5fd58f85be206e09c996d056ee8b1e78 100644 (file)
@@ -27,11 +27,12 @@ namespace __tsan {
 void InitializeShadowMemory() {
   // Map memory shadow.
   uptr shadow =
-      (uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow");
-  if (shadow != kShadowBeg) {
+      (uptr)MmapFixedNoReserve(ShadowBeg(), ShadowEnd() - ShadowBeg(),
+                               "shadow");
+  if (shadow != ShadowBeg()) {
     Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
     Printf("FATAL: Make sure to compile with -fPIE and "
-               "to link with -pie (%p, %p).\n", shadow, kShadowBeg);
+               "to link with -pie (%p, %p).\n", shadow, ShadowBeg());
     Die();
   }
   // This memory range is used for thread stacks and large user mmaps.
@@ -46,29 +47,50 @@ void InitializeShadowMemory() {
   const uptr kMadviseRangeBeg  = 0xff00000000ull;
   const uptr kMadviseRangeSize = 0x0100000000ull;
 #elif defined(__aarch64__)
-  const uptr kMadviseRangeBeg  = 0x7e00000000ull;
-  const uptr kMadviseRangeSize = 0x0100000000ull;
+  uptr kMadviseRangeBeg = 0;
+  uptr kMadviseRangeSize = 0;
+  if (vmaSize == 39) {
+    kMadviseRangeBeg  = 0x7d00000000ull;
+    kMadviseRangeSize = 0x0300000000ull;
+  } else if (vmaSize == 42) {
+    kMadviseRangeBeg  = 0x3f000000000ull;
+    kMadviseRangeSize = 0x01000000000ull;
+  } else {
+    DCHECK(0);
+  }
+#elif defined(__powerpc64__)
+  uptr kMadviseRangeBeg = 0;
+  uptr kMadviseRangeSize = 0;
+  if (vmaSize == 44) {
+    kMadviseRangeBeg  = 0x0f60000000ull;
+    kMadviseRangeSize = 0x0010000000ull;
+  } else if (vmaSize == 46) {
+    kMadviseRangeBeg  = 0x3f0000000000ull;
+    kMadviseRangeSize = 0x010000000000ull;
+  } else {
+    DCHECK(0);
+  }
 #endif
   NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg),
                       kMadviseRangeSize * kShadowMultiplier);
   // Meta shadow is compressing and we don't flush it,
   // so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory.
   // On one program it reduces memory consumption from 5GB to 2.5GB.
-  NoHugePagesInRegion(kMetaShadowBeg, kMetaShadowEnd - kMetaShadowBeg);
+  NoHugePagesInRegion(MetaShadowBeg(), MetaShadowEnd() - MetaShadowBeg());
   if (common_flags()->use_madv_dontdump)
-    DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
+    DontDumpShadowMemory(ShadowBeg(), ShadowEnd() - ShadowBeg());
   DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
-      kShadowBeg, kShadowEnd,
-      (kShadowEnd - kShadowBeg) >> 30);
+      ShadowBeg(), ShadowEnd(),
+      (ShadowEnd() - ShadowBeg()) >> 30);
 
   // Map meta shadow.
-  uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
+  uptr meta_size = MetaShadowEnd() - MetaShadowBeg();
   uptr meta =
-      (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow");
-  if (meta != kMetaShadowBeg) {
+      (uptr)MmapFixedNoReserve(MetaShadowBeg(), meta_size, "meta shadow");
+  if (meta != MetaShadowBeg()) {
     Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
     Printf("FATAL: Make sure to compile with -fPIE and "
-               "to link with -pie (%p, %p).\n", meta, kMetaShadowBeg);
+               "to link with -pie (%p, %p).\n", meta, MetaShadowBeg());
     Die();
   }
   if (common_flags()->use_madv_dontdump)
@@ -97,25 +119,30 @@ void CheckAndProtect() {
   while (proc_maps.Next(&p, &end, 0, 0, 0, &prot)) {
     if (IsAppMem(p))
       continue;
-    if (p >= kHeapMemEnd &&
+    if (p >= HeapMemEnd() &&
         p < HeapEnd())
       continue;
     if (prot == 0)  // Zero page or mprotected.
       continue;
-    if (p >= kVdsoBeg)  // vdso
+    if (p >= VdsoBeg())  // vdso
       break;
     Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end);
     Die();
   }
 
-  ProtectRange(kLoAppMemEnd, kShadowBeg);
-  ProtectRange(kShadowEnd, kMetaShadowBeg);
-  ProtectRange(kMetaShadowEnd, kTraceMemBeg);
+  ProtectRange(LoAppMemEnd(), ShadowBeg());
+  ProtectRange(ShadowEnd(), MetaShadowBeg());
+#ifdef TSAN_MID_APP_RANGE
+  ProtectRange(MetaShadowEnd(), MidAppMemBeg());
+  ProtectRange(MidAppMemEnd(), TraceMemBeg());
+#else
+  ProtectRange(MetaShadowEnd(), TraceMemBeg());
+#endif
   // Memory for traces is mapped lazily in MapThreadTrace.
   // Protect the whole range for now, so that user does not map something here.
-  ProtectRange(kTraceMemBeg, kTraceMemEnd);
-  ProtectRange(kTraceMemEnd, kHeapMemBeg);
-  ProtectRange(HeapEnd(), kHiAppMemBeg);
+  ProtectRange(TraceMemBeg(), TraceMemEnd());
+  ProtectRange(TraceMemEnd(), HeapMemBeg());
+  ProtectRange(HeapEnd(), HiAppMemBeg());
 }
 #endif
 
index cfbe77da2c0751fa48f026e05513ad94ac6fd42a..c6d5058d96fc046269224969b4a408843f93a1f6 100644 (file)
@@ -31,6 +31,9 @@ void FlushShadowMemory() {
 void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
 }
 
+void InitializePlatformEarly() {
+}
+
 void InitializePlatform() {
 }
 
diff --git a/src/compiler-rt/lib/tsan/rtl/tsan_ppc_regs.h b/src/compiler-rt/lib/tsan/rtl/tsan_ppc_regs.h
new file mode 100644 (file)
index 0000000..5b43f3d
--- /dev/null
@@ -0,0 +1,96 @@
+#define r0 0
+#define r1 1
+#define r2 2
+#define r3 3
+#define r4 4
+#define r5 5
+#define r6 6
+#define r7 7
+#define r8 8
+#define r9 9
+#define r10 10
+#define r11 11
+#define r12 12
+#define r13 13
+#define r14 14
+#define r15 15
+#define r16 16
+#define r17 17
+#define r18 18
+#define r19 19
+#define r20 20
+#define r21 21
+#define r22 22
+#define r23 23
+#define r24 24
+#define r25 25
+#define r26 26
+#define r27 27
+#define r28 28
+#define r29 29
+#define r30 30
+#define r31 31
+#define f0 0
+#define f1 1
+#define f2 2
+#define f3 3
+#define f4 4
+#define f5 5
+#define f6 6
+#define f7 7
+#define f8 8
+#define f9 9
+#define f10 10
+#define f11 11
+#define f12 12
+#define f13 13
+#define f14 14
+#define f15 15
+#define f16 16
+#define f17 17
+#define f18 18
+#define f19 19
+#define f20 20
+#define f21 21
+#define f22 22
+#define f23 23
+#define f24 24
+#define f25 25
+#define f26 26
+#define f27 27
+#define f28 28
+#define f29 29
+#define f30 30
+#define f31 31
+#define v0 0
+#define v1 1
+#define v2 2
+#define v3 3
+#define v4 4
+#define v5 5
+#define v6 6
+#define v7 7
+#define v8 8
+#define v9 9
+#define v10 10
+#define v11 11
+#define v12 12
+#define v13 13
+#define v14 14
+#define v15 15
+#define v16 16
+#define v17 17
+#define v18 18
+#define v19 19
+#define v20 20
+#define v21 21
+#define v22 22
+#define v23 23
+#define v24 24
+#define v25 25
+#define v26 26
+#define v27 27
+#define v28 28
+#define v29 29
+#define v30 30
+#define v31 31
index f4b06878a58e4cebb9a55b1f7f2976f689031dc4..c1d2fd07c0d99747d6f21875f4e1df93362cac60 100644 (file)
@@ -111,6 +111,12 @@ static const char *ReportTypeString(ReportType typ) {
   return "";
 }
 
+#if SANITIZER_MAC
+static const char *const kInterposedFunctionPrefix = "wrap_";
+#else
+static const char *const kInterposedFunctionPrefix = "__interceptor_";
+#endif
+
 void PrintStack(const ReportStack *ent) {
   if (ent == 0 || ent->frames == 0) {
     Printf("    [failed to restore the stack]\n\n");
@@ -121,7 +127,7 @@ void PrintStack(const ReportStack *ent) {
     InternalScopedString res(2 * GetPageSizeCached());
     RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
                 common_flags()->symbolize_vs_style,
-                common_flags()->strip_path_prefix, "__interceptor_");
+                common_flags()->strip_path_prefix, kInterposedFunctionPrefix);
     Printf("%s\n", res.data());
   }
   Printf("\n");
@@ -165,9 +171,14 @@ static void PrintLocation(const ReportLocation *loc) {
   Printf("%s", d.Location());
   if (loc->type == ReportLocationGlobal) {
     const DataInfo &global = loc->global;
-    Printf("  Location is global '%s' of size %zu at %p (%s+%p)\n\n",
-           global.name, global.size, global.start,
-           StripModuleName(global.module), global.module_offset);
+    if (global.size != 0)
+      Printf("  Location is global '%s' of size %zu at %p (%s+%p)\n\n",
+             global.name, global.size, global.start,
+             StripModuleName(global.module), global.module_offset);
+    else
+      Printf("  Location is global '%s' at %p (%s+%p)\n\n", global.name,
+             global.start, StripModuleName(global.module),
+             global.module_offset);
   } else if (loc->type == ReportLocationHeap) {
     char thrbuf[kThreadBufSize];
     Printf("  Location is heap block of size %zu at %p allocated by %s:\n",
@@ -256,10 +267,15 @@ static bool FrameIsInternal(const SymbolizedStack *frame) {
   if (frame == 0)
     return false;
   const char *file = frame->info.file;
-  return file != 0 &&
-         (internal_strstr(file, "tsan_interceptors.cc") ||
-          internal_strstr(file, "sanitizer_common_interceptors.inc") ||
-          internal_strstr(file, "tsan_interface_"));
+  const char *module = frame->info.module;
+  if (file != 0 &&
+      (internal_strstr(file, "tsan_interceptors.cc") ||
+       internal_strstr(file, "sanitizer_common_interceptors.inc") ||
+       internal_strstr(file, "tsan_interface_")))
+    return true;
+  if (module != 0 && (internal_strstr(module, "libclang_rt.tsan_")))
+    return true;
+  return false;
 }
 
 static SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {
index 05d530e982b35f4e002bfa664bd979c321e1d18f..4df4db557a24648d23e3716db4508c87c295d88f 100644 (file)
@@ -55,12 +55,12 @@ Context *ctx;
 bool OnFinalize(bool failed);
 void OnInitialize();
 #else
-SANITIZER_INTERFACE_ATTRIBUTE
-bool WEAK OnFinalize(bool failed) {
+SANITIZER_WEAK_CXX_DEFAULT_IMPL
+bool OnFinalize(bool failed) {
   return failed;
 }
-SANITIZER_INTERFACE_ATTRIBUTE
-void WEAK OnInitialize() {}
+SANITIZER_WEAK_CXX_DEFAULT_IMPL
+void OnInitialize() {}
 #endif
 
 static char thread_registry_placeholder[sizeof(ThreadRegistry)];
@@ -273,8 +273,8 @@ void MapShadow(uptr addr, uptr size) {
 
 void MapThreadTrace(uptr addr, uptr size, const char *name) {
   DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
-  CHECK_GE(addr, kTraceMemBeg);
-  CHECK_LE(addr + size, kTraceMemEnd);
+  CHECK_GE(addr, TraceMemBeg());
+  CHECK_LE(addr + size, TraceMemEnd());
   CHECK_EQ(addr, addr & ~((64 << 10) - 1));  // windows wants 64K alignment
   uptr addr1 = (uptr)MmapFixedNoReserve(addr, size, name);
   if (addr1 != addr) {
@@ -285,9 +285,8 @@ void MapThreadTrace(uptr addr, uptr size, const char *name) {
 }
 
 static void CheckShadowMapping() {
-  for (uptr i = 0; i < ARRAY_SIZE(UserRegions); i += 2) {
-    const uptr beg = UserRegions[i];
-    const uptr end = UserRegions[i + 1];
+  uptr beg, end;
+  for (int i = 0; GetUserRegion(i, &beg, &end); i++) {
     VPrintf(3, "checking shadow region %p-%p\n", beg, end);
     for (uptr p0 = beg; p0 <= end; p0 += (end - beg) / 4) {
       for (int x = -1; x <= 1; x++) {
@@ -322,8 +321,11 @@ void Initialize(ThreadState *thr) {
   const char *options = GetEnv(kTsanOptionsEnv);
   CacheBinaryName();
   InitializeFlags(&ctx->flags, options);
-  CheckVMASize();
+  InitializePlatformEarly();
 #ifndef SANITIZER_GO
+  // Re-exec ourselves if we need to set additional env or command line args.
+  MaybeReexec();
+
   InitializeAllocator();
   ReplaceSystemMalloc();
 #endif
index 2e6be3ff06520f88dcdb64df4e479ff984c0a051..04104b162f98b057c4adb0c3f7d87f65b15b563f 100644 (file)
@@ -54,7 +54,7 @@ namespace __tsan {
 
 #ifndef SANITIZER_GO
 struct MapUnmapCallback;
-#if defined(__mips64) || defined(__aarch64__)
+#if defined(__mips64) || defined(__aarch64__) || defined(__powerpc__)
 static const uptr kAllocatorSpace = 0;
 static const uptr kAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
 static const uptr kAllocatorRegionSizeLog = 20;
@@ -66,7 +66,8 @@ typedef SizeClassAllocator32<kAllocatorSpace, kAllocatorSize, 0,
     CompactSizeClassMap, kAllocatorRegionSizeLog, ByteMap,
     MapUnmapCallback> PrimaryAllocator;
 #else
-typedef SizeClassAllocator64<kHeapMemBeg, kHeapMemEnd - kHeapMemBeg, 0,
+typedef SizeClassAllocator64<Mapping::kHeapMemBeg,
+    Mapping::kHeapMemEnd - Mapping::kHeapMemBeg, 0,
     DefaultSizeClassMap, MapUnmapCallback> PrimaryAllocator;
 #endif
 typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
@@ -761,7 +762,7 @@ void ALWAYS_INLINE TraceAddEvent(ThreadState *thr, FastState fs,
 
 #ifndef SANITIZER_GO
 uptr ALWAYS_INLINE HeapEnd() {
-  return kHeapMemEnd + PrimaryAllocator::AdditionalSize();
+  return HeapMemEnd() + PrimaryAllocator::AdditionalSize();
 }
 #endif
 
diff --git a/src/compiler-rt/lib/tsan/rtl/tsan_rtl_aarch64.S b/src/compiler-rt/lib/tsan/rtl/tsan_rtl_aarch64.S
new file mode 100644 (file)
index 0000000..9cea3cf
--- /dev/null
@@ -0,0 +1,206 @@
+#include "sanitizer_common/sanitizer_asm.h"
+.section .text
+
+.hidden __tsan_setjmp
+.comm _ZN14__interception11real_setjmpE,8,8
+.type setjmp, @function
+setjmp:
+  CFI_STARTPROC
+
+  // save env parameters for function call
+  stp     x29, x30, [sp, -32]!
+  CFI_DEF_CFA_OFFSET (32)
+  CFI_OFFSET (29, -32)
+  CFI_OFFSET (30, -24)
+
+  // Adjust the SP for previous frame
+  add     x29, sp, 0
+  CFI_DEF_CFA_REGISTER (29)
+
+  // Save jmp_buf
+  str     x19, [sp, 16]
+  CFI_OFFSET (19, -16)
+  mov     x19, x0
+
+  // SP pointer mangling (see glibc setjmp)
+  adrp    x2, :got:__pointer_chk_guard
+  ldr     x2, [x2, #:got_lo12:__pointer_chk_guard]
+  add     x0, x29, 32
+  ldr     x2, [x2]
+  eor     x1, x2, x0
+
+  // call tsan interceptor
+  bl      __tsan_setjmp
+
+  // restore env parameter
+  mov     x0, x19
+  ldr     x19, [sp, 16]
+  ldp     x29, x30, [sp], 32
+  CFI_RESTORE (30)
+  CFI_RESTORE (19)
+  CFI_DEF_CFA (31, 0)
+
+  // tail jump to libc setjmp
+  adrp    x1, :got:_ZN14__interception11real_setjmpE
+  ldr     x1, [x1, #:got_lo12:_ZN14__interception11real_setjmpE]
+  ldr     x1, [x1]
+  br      x1
+
+  CFI_ENDPROC
+.size setjmp, .-setjmp
+
+.comm _ZN14__interception12real__setjmpE,8,8
+.globl _setjmp
+.type _setjmp, @function
+_setjmp:
+  CFI_STARTPROC
+
+  // save env parameters for function call
+  stp     x29, x30, [sp, -32]!
+  CFI_DEF_CFA_OFFSET (32)
+  CFI_OFFSET (29, -32)
+  CFI_OFFSET (30, -24)
+
+  // Adjust the SP for previous frame
+  add     x29, sp, 0
+  CFI_DEF_CFA_REGISTER (29)
+
+  // Save jmp_buf
+  str     x19, [sp, 16]
+  CFI_OFFSET (19, -16)
+  mov     x19, x0
+
+  // SP pointer mangling (see glibc setjmp)
+  adrp    x2, :got:__pointer_chk_guard
+  ldr     x2, [x2, #:got_lo12:__pointer_chk_guard]
+  add     x0, x29, 32
+  ldr     x2, [x2]
+  eor     x1, x2, x0
+
+  // call tsan interceptor
+  bl      __tsan_setjmp
+
+  // Restore jmp_buf parameter
+  mov     x0, x19
+  ldr     x19, [sp, 16]
+  ldp     x29, x30, [sp], 32
+  CFI_RESTORE (30)
+  CFI_RESTORE (19)
+  CFI_DEF_CFA (31, 0)
+
+  // tail jump to libc setjmp
+  adrp    x1, :got:_ZN14__interception12real__setjmpE
+  ldr     x1, [x1, #:got_lo12:_ZN14__interception12real__setjmpE]
+  ldr     x1, [x1]
+  br      x1
+
+  CFI_ENDPROC
+.size _setjmp, .-_setjmp
+
+.comm _ZN14__interception14real_sigsetjmpE,8,8
+.globl sigsetjmp
+.type sigsetjmp, @function
+sigsetjmp:
+  CFI_STARTPROC
+
+  // save env parameters for function call
+  stp     x29, x30, [sp, -32]!
+  CFI_DEF_CFA_OFFSET (32)
+  CFI_OFFSET (29, -32)
+  CFI_OFFSET (30, -24)
+
+  // Adjust the SP for previous frame
+  add     x29, sp, 0
+  CFI_DEF_CFA_REGISTER (29)
+
+  // Save jmp_buf and savesigs
+  stp     x19, x20, [sp, 16]
+  CFI_OFFSET (19, -16)
+  CFI_OFFSET (20, -8)
+  mov     w20, w1
+  mov     x19, x0
+
+  // SP pointer mangling (see glibc setjmp)
+  adrp    x2, :got:__pointer_chk_guard
+  ldr     x2, [x2, #:got_lo12:__pointer_chk_guard]
+  add     x0, x29, 32
+  ldr     x2, [x2]
+  eor     x1, x2, x0
+
+  // call tsan interceptor
+  bl      __tsan_setjmp
+
+  // restore env parameter
+  mov     w1, w20
+  mov     x0, x19
+  ldp     x19, x20, [sp, 16]
+  ldp     x29, x30, [sp], 32
+  CFI_RESTORE (30)
+  CFI_RESTORE (29)
+  CFI_RESTORE (19)
+  CFI_RESTORE (20)
+  CFI_DEF_CFA (31, 0)
+
+  // tail jump to libc sigsetjmp
+  adrp    x2, :got:_ZN14__interception14real_sigsetjmpE
+  ldr     x2, [x2, #:got_lo12:_ZN14__interception14real_sigsetjmpE]
+  ldr     x2, [x2]
+  br      x2
+  CFI_ENDPROC
+.size sigsetjmp, .-sigsetjmp
+
+.comm _ZN14__interception16real___sigsetjmpE,8,8
+.globl __sigsetjmp
+.type __sigsetjmp, @function
+__sigsetjmp:
+  CFI_STARTPROC
+
+  // save env parameters for function call
+  stp     x29, x30, [sp, -32]!
+  CFI_DEF_CFA_OFFSET (32)
+  CFI_OFFSET (29, -32)
+  CFI_OFFSET (30, -24)
+
+  // Adjust the SP for previous frame
+  add     x29, sp, 0
+  CFI_DEF_CFA_REGISTER (29)
+
+  // Save jmp_buf and savesigs
+  stp     x19, x20, [sp, 16]
+  CFI_OFFSET (19, -16)
+  CFI_OFFSET (20, -8)
+  mov     w20, w1
+  mov     x19, x0
+
+  // SP pointer mangling (see glibc setjmp)
+  adrp    x2, :got:__pointer_chk_guard
+  ldr     x2, [x2, #:got_lo12:__pointer_chk_guard]
+  add     x0, x29, 32
+  ldr     x2, [x2]
+  eor     x1, x2, x0
+
+  // call tsan interceptor
+  bl      __tsan_setjmp
+
+  mov     w1, w20
+  mov     x0, x19
+  ldp     x19, x20, [sp, 16]
+  ldp     x29, x30, [sp], 32
+  CFI_RESTORE (30)
+  CFI_RESTORE (29)
+  CFI_RESTORE (19)
+  CFI_RESTORE (20)
+  CFI_DEF_CFA (31, 0)
+
+  // tail jump to libc __sigsetjmp
+  adrp    x2, :got:_ZN14__interception16real___sigsetjmpE
+  ldr     x2, [x2, #:got_lo12:_ZN14__interception16real___sigsetjmpE]
+  ldr     x2, [x2]
+  br      x2
+  CFI_ENDPROC
+.size __sigsetjmp, .-__sigsetjmp
+
+#if defined(__linux__)
+/* We do not need executable stack.  */
+.section        .note.GNU-stack,"",@progbits
+#endif
index 8db62f9013a3d7ff39e8cfc86ea5d82c42892eda..caa832375e52d9c1b23b00d4533b75da7ceeb43e 100644 (file)
@@ -1,9 +1,13 @@
 #include "sanitizer_common/sanitizer_asm.h"
+#if !defined(__APPLE__)
 .section .text
+#else
+.section __TEXT,__text
+#endif
 
-.hidden __tsan_trace_switch
-.globl __tsan_trace_switch_thunk
-__tsan_trace_switch_thunk:
+ASM_HIDDEN(__tsan_trace_switch)
+.globl ASM_TSAN_SYMBOL(__tsan_trace_switch_thunk)
+ASM_TSAN_SYMBOL(__tsan_trace_switch_thunk):
   CFI_STARTPROC
   # Save scratch registers.
   push %rax
@@ -42,7 +46,7 @@ __tsan_trace_switch_thunk:
   shr $4, %rsp  # clear 4 lsb, align to 16
   shl $4, %rsp
 
-  call __tsan_trace_switch
+  call ASM_TSAN_SYMBOL(__tsan_trace_switch)
 
   # Unalign stack frame back.
   mov %rbx, %rsp  # restore the original rsp
@@ -81,9 +85,9 @@ __tsan_trace_switch_thunk:
   ret
   CFI_ENDPROC
 
-.hidden __tsan_report_race
-.globl __tsan_report_race_thunk
-__tsan_report_race_thunk:
+ASM_HIDDEN(__tsan_report_race)
+.globl ASM_TSAN_SYMBOL(__tsan_report_race_thunk)
+ASM_TSAN_SYMBOL(__tsan_report_race_thunk):
   CFI_STARTPROC
   # Save scratch registers.
   push %rax
@@ -122,7 +126,7 @@ __tsan_report_race_thunk:
   shr $4, %rsp  # clear 4 lsb, align to 16
   shl $4, %rsp
 
-  call __tsan_report_race
+  call ASM_TSAN_SYMBOL(__tsan_report_race)
 
   # Unalign stack frame back.
   mov %rbx, %rsp  # restore the original rsp
@@ -161,11 +165,13 @@ __tsan_report_race_thunk:
   ret
   CFI_ENDPROC
 
-.hidden __tsan_setjmp
+ASM_HIDDEN(__tsan_setjmp)
+#if !defined(__APPLE__)
 .comm _ZN14__interception11real_setjmpE,8,8
-.globl setjmp
-.type setjmp, @function
-setjmp:
+#endif
+.globl ASM_TSAN_SYMBOL_INTERCEPTOR(setjmp)
+ASM_TYPE_FUNCTION(ASM_TSAN_SYMBOL_INTERCEPTOR(setjmp))
+ASM_TSAN_SYMBOL_INTERCEPTOR(setjmp):
   CFI_STARTPROC
   // save env parameter
   push %rdi
@@ -175,29 +181,38 @@ setjmp:
 #if defined(__FreeBSD__)
   lea 8(%rsp), %rdi
   mov %rdi, %rsi
-#else
+#elif defined(__APPLE__)
+  lea 16(%rsp), %rdi
+  mov %rdi, %rsi
+#elif defined(__linux__)
   lea 16(%rsp), %rdi
   mov %rdi, %rsi
   xor %fs:0x30, %rsi  // magic mangling of rsp (see libc setjmp)
   rol $0x11, %rsi
+#else
+# error "Unknown platform"
 #endif
   // call tsan interceptor
-  call __tsan_setjmp
+  call ASM_TSAN_SYMBOL(__tsan_setjmp)
   // restore env parameter
   pop %rdi
   CFI_ADJUST_CFA_OFFSET(-8)
   CFI_RESTORE(%rdi)
   // tail jump to libc setjmp
   movl $0, %eax
+#if !defined(__APPLE__)
   movq _ZN14__interception11real_setjmpE@GOTPCREL(%rip), %rdx
   jmp *(%rdx)
+#else
+  jmp ASM_TSAN_SYMBOL(setjmp)
+#endif
   CFI_ENDPROC
-.size setjmp, .-setjmp
+ASM_SIZE(ASM_TSAN_SYMBOL_INTERCEPTOR(setjmp))
 
 .comm _ZN14__interception12real__setjmpE,8,8
-.globl _setjmp
-.type _setjmp, @function
-_setjmp:
+.globl ASM_TSAN_SYMBOL_INTERCEPTOR(_setjmp)
+ASM_TYPE_FUNCTION(ASM_TSAN_SYMBOL_INTERCEPTOR(_setjmp))
+ASM_TSAN_SYMBOL_INTERCEPTOR(_setjmp):
   CFI_STARTPROC
   // save env parameter
   push %rdi
@@ -207,29 +222,38 @@ _setjmp:
 #if defined(__FreeBSD__)
   lea 8(%rsp), %rdi
   mov %rdi, %rsi
-#else
+#elif defined(__APPLE__)
+  lea 16(%rsp), %rdi
+  mov %rdi, %rsi
+#elif defined(__linux__)
   lea 16(%rsp), %rdi
   mov %rdi, %rsi
   xor %fs:0x30, %rsi  // magic mangling of rsp (see libc setjmp)
   rol $0x11, %rsi
+#else
+# error "Unknown platform"
 #endif
   // call tsan interceptor
-  call __tsan_setjmp
+  call ASM_TSAN_SYMBOL(__tsan_setjmp)
   // restore env parameter
   pop %rdi
   CFI_ADJUST_CFA_OFFSET(-8)
   CFI_RESTORE(%rdi)
   // tail jump to libc setjmp
   movl $0, %eax
+#if !defined(__APPLE__)
   movq _ZN14__interception12real__setjmpE@GOTPCREL(%rip), %rdx
   jmp *(%rdx)
+#else
+  jmp ASM_TSAN_SYMBOL(_setjmp)
+#endif
   CFI_ENDPROC
-.size _setjmp, .-_setjmp
+ASM_SIZE(ASM_TSAN_SYMBOL_INTERCEPTOR(_setjmp))
 
 .comm _ZN14__interception14real_sigsetjmpE,8,8
-.globl sigsetjmp
-.type sigsetjmp, @function
-sigsetjmp:
+.globl ASM_TSAN_SYMBOL_INTERCEPTOR(sigsetjmp)
+ASM_TYPE_FUNCTION(ASM_TSAN_SYMBOL_INTERCEPTOR(sigsetjmp))
+ASM_TSAN_SYMBOL_INTERCEPTOR(sigsetjmp):
   CFI_STARTPROC
   // save env parameter
   push %rdi
@@ -246,14 +270,19 @@ sigsetjmp:
 #if defined(__FreeBSD__)
   lea 24(%rsp), %rdi
   mov %rdi, %rsi
-#else
+#elif defined(__APPLE__)
+  lea 32(%rsp), %rdi
+  mov %rdi, %rsi
+#elif defined(__linux__)
   lea 32(%rsp), %rdi
   mov %rdi, %rsi
   xor %fs:0x30, %rsi  // magic mangling of rsp (see libc setjmp)
   rol $0x11, %rsi
+#else
+# error "Unknown platform"
 #endif
   // call tsan interceptor
-  call __tsan_setjmp
+  call ASM_TSAN_SYMBOL(__tsan_setjmp)
   // unalign stack frame
   add $8, %rsp
   CFI_ADJUST_CFA_OFFSET(-8)
@@ -267,15 +296,20 @@ sigsetjmp:
   CFI_RESTORE(%rdi)
   // tail jump to libc sigsetjmp
   movl $0, %eax
+#if !defined(__APPLE__)
   movq _ZN14__interception14real_sigsetjmpE@GOTPCREL(%rip), %rdx
   jmp *(%rdx)
+#else
+  jmp ASM_TSAN_SYMBOL(sigsetjmp)
+#endif
   CFI_ENDPROC
-.size sigsetjmp, .-sigsetjmp
+ASM_SIZE(ASM_TSAN_SYMBOL_INTERCEPTOR(sigsetjmp))
 
+#if !defined(__APPLE__)
 .comm _ZN14__interception16real___sigsetjmpE,8,8
-.globl __sigsetjmp
-.type __sigsetjmp, @function
-__sigsetjmp:
+.globl ASM_TSAN_SYMBOL_INTERCEPTOR(__sigsetjmp)
+ASM_TYPE_FUNCTION(ASM_TSAN_SYMBOL_INTERCEPTOR(__sigsetjmp))
+ASM_TSAN_SYMBOL_INTERCEPTOR(__sigsetjmp):
   CFI_STARTPROC
   // save env parameter
   push %rdi
@@ -299,7 +333,7 @@ __sigsetjmp:
   rol $0x11, %rsi
 #endif
   // call tsan interceptor
-  call __tsan_setjmp
+  call ASM_TSAN_SYMBOL(__tsan_setjmp)
   // unalign stack frame
   add $8, %rsp
   CFI_ADJUST_CFA_OFFSET(-8)
@@ -316,7 +350,8 @@ __sigsetjmp:
   movq _ZN14__interception16real___sigsetjmpE@GOTPCREL(%rip), %rdx
   jmp *(%rdx)
   CFI_ENDPROC
-.size __sigsetjmp, .-__sigsetjmp
+ASM_SIZE(ASM_TSAN_SYMBOL_INTERCEPTOR(__sigsetjmp))
+#endif  // !defined(__APPLE__)
 
 #if defined(__FreeBSD__) || defined(__linux__)
 /* We do not need executable stack.  */
diff --git a/src/compiler-rt/lib/tsan/rtl/tsan_rtl_ppc64.S b/src/compiler-rt/lib/tsan/rtl/tsan_rtl_ppc64.S
new file mode 100644 (file)
index 0000000..8285e21
--- /dev/null
@@ -0,0 +1,288 @@
+#include "tsan_ppc_regs.h"
+
+        .section .text
+        .hidden __tsan_setjmp
+        .globl _setjmp
+        .type _setjmp, @function
+        .align 4
+#if _CALL_ELF == 2
+_setjmp:
+#else
+       .section ".opd","aw"
+       .align 3
+_setjmp:
+       .quad   .L._setjmp,.TOC.@tocbase,0
+       .previous
+#endif
+.L._setjmp:
+        mflr    r0
+        stdu    r1,-48(r1)
+        std     r2,24(r1)
+        std     r3,32(r1)
+        std     r0,40(r1)
+        // r3 is the original stack pointer.
+        addi    r3,r1,48
+        // r4 is the mangled stack pointer (see glibc)
+        ld      r4,-28696(r13)
+        xor     r4,r3,r4
+        // Materialize a TOC in case we were called from libc.
+        // For big-endian, we load the TOC from the OPD.  For little-
+        // endian, we use the .TOC. symbol to find it.
+        nop
+        bcl     20,31,0f
+0:
+        mflr    r2
+#if _CALL_ELF == 2
+        addis   r2,r2,.TOC.-0b@ha
+        addi    r2,r2,.TOC.-0b@l
+#else
+        addis   r2,r2,_setjmp-0b@ha
+        addi    r2,r2,_setjmp-0b@l
+        ld      r2,8(r2)
+#endif
+        // Call the interceptor.
+        bl      __tsan_setjmp
+        nop
+        // Restore regs needed for setjmp.
+        ld      r3,32(r1)
+        ld      r0,40(r1)
+        // Emulate the real setjmp function.  We do this because we can't
+        // perform a sibcall:  The real setjmp function trashes the TOC
+        // pointer, and with a sibcall we have no way to restore it.
+        // This way we can make sure our caller's stack pointer and
+        // link register are saved correctly in the jmpbuf.
+        ld      r6,-28696(r13)
+        addi    r5,r1,48  // original stack ptr of caller
+        xor     r5,r6,r5
+        std     r5,0(r3)  // mangled stack ptr of caller
+        ld      r5,24(r1)
+        std     r5,8(r3)  // caller's saved TOC pointer
+        xor     r0,r6,r0
+        std     r0,16(r3) // caller's mangled return address
+        mfcr    r0
+        // Nonvolatiles.
+        std     r14,24(r3)
+        stfd    f14,176(r3)
+        stw     r0,172(r3) // CR
+        std     r15,32(r3)
+        stfd    f15,184(r3)
+        std     r16,40(r3)
+        stfd    f16,192(r3)
+        std     r17,48(r3)
+        stfd    f17,200(r3)
+        std     r18,56(r3)
+        stfd    f18,208(r3)
+        std     r19,64(r3)
+        stfd    f19,216(r3)
+        std     r20,72(r3)
+        stfd    f20,224(r3)
+        std     r21,80(r3)
+        stfd    f21,232(r3)
+        std     r22,88(r3)
+        stfd    f22,240(r3)
+        std     r23,96(r3)
+        stfd    f23,248(r3)
+        std     r24,104(r3)
+        stfd    f24,256(r3)
+        std     r25,112(r3)
+        stfd    f25,264(r3)
+        std     r26,120(r3)
+        stfd    f26,272(r3)
+        std     r27,128(r3)
+        stfd    f27,280(r3)
+        std     r28,136(r3)
+        stfd    f28,288(r3)
+        std     r29,144(r3)
+        stfd    f29,296(r3)
+        std     r30,152(r3)
+        stfd    f30,304(r3)
+        std     r31,160(r3)
+        stfd    f31,312(r3)
+        addi    r5,r3,320
+        mfspr   r0,256
+        stw     r0,168(r3)  // VRSAVE
+        addi    r6,r5,16
+        stvx    v20,0,r5
+        addi    r5,r5,32
+        stvx    v21,0,r6
+        addi    r6,r6,32
+        stvx    v22,0,r5
+        addi    r5,r5,32
+        stvx    v23,0,r6
+        addi    r6,r6,32
+        stvx    v24,0,r5
+        addi    r5,r5,32
+        stvx    v25,0,r6
+        addi    r6,r6,32
+        stvx    v26,0,r5
+        addi    r5,r5,32
+        stvx    v27,0,r6
+        addi    r6,r6,32
+        stvx    v28,0,r5
+        addi    r5,r5,32
+        stvx    v29,0,r6
+        addi    r6,r6,32
+        stvx    v30,0,r5
+        stvx    v31,0,r6
+        // Clear the "mask-saved" slot.
+        li      r4,0
+        stw     r4,512(r3)
+        // Restore TOC, LR, and stack and return to caller.
+        ld      r2,24(r1)
+        ld      r0,40(r1)
+        addi    r1,r1,48
+        li      r3,0  // This is the setjmp return path
+        mtlr    r0
+        blr
+        .size _setjmp, .-.L._setjmp
+
+        .globl setjmp
+        .type setjmp, @function
+        .align 4
+setjmp:
+        b       _setjmp
+        .size setjmp, .-setjmp
+
+        // sigsetjmp is like setjmp, except that the mask in r4 needs
+        // to be saved at offset 512 of the jump buffer.
+        .globl __sigsetjmp
+        .type __sigsetjmp, @function
+        .align 4
+#if _CALL_ELF == 2
+__sigsetjmp:
+#else
+       .section ".opd","aw"
+       .align 3
+__sigsetjmp:
+       .quad   .L.__sigsetjmp,.TOC.@tocbase,0
+       .previous
+#endif
+.L.__sigsetjmp:
+        mflr    r0
+        stdu    r1,-64(r1)
+        std     r2,24(r1)
+        std     r3,32(r1)
+        std     r4,40(r1)
+        std     r0,48(r1)
+        // r3 is the original stack pointer.
+        addi    r3,r1,64
+        // r4 is the mangled stack pointer (see glibc)
+        ld      r4,-28696(r13)
+        xor     r4,r3,r4
+        // Materialize a TOC in case we were called from libc.
+        // For big-endian, we load the TOC from the OPD.  For little-
+        // endian, we use the .TOC. symbol to find it.
+        nop
+        bcl     20,31,1f
+1:
+        mflr    r2
+#if _CALL_ELF == 2
+        addis   r2,r2,.TOC.-1b@ha
+        addi    r2,r2,.TOC.-1b@l
+#else
+        addis   r2,r2,_setjmp-1b@ha
+        addi    r2,r2,_setjmp-1b@l
+        ld      r2,8(r2)
+#endif
+        // Call the interceptor.
+        bl      __tsan_setjmp
+        nop
+        // Restore regs needed for __sigsetjmp.
+        ld      r3,32(r1)
+        ld      r4,40(r1)
+        ld      r0,48(r1)
+        // Emulate the real sigsetjmp function.  We do this because we can't
+        // perform a sibcall:  The real sigsetjmp function trashes the TOC
+        // pointer, and with a sibcall we have no way to restore it.
+        // This way we can make sure our caller's stack pointer and
+        // link register are saved correctly in the jmpbuf.
+        ld      r6,-28696(r13)
+        addi    r5,r1,64  // original stack ptr of caller
+        xor     r5,r6,r5
+        std     r5,0(r3)  // mangled stack ptr of caller
+        ld      r5,24(r1)
+        std     r5,8(r3)  // caller's saved TOC pointer
+        xor     r0,r6,r0
+        std     r0,16(r3) // caller's mangled return address
+        mfcr    r0
+        // Nonvolatiles.
+        std     r14,24(r3)
+        stfd    f14,176(r3)
+        stw     r0,172(r3) // CR
+        std     r15,32(r3)
+        stfd    f15,184(r3)
+        std     r16,40(r3)
+        stfd    f16,192(r3)
+        std     r17,48(r3)
+        stfd    f17,200(r3)
+        std     r18,56(r3)
+        stfd    f18,208(r3)
+        std     r19,64(r3)
+        stfd    f19,216(r3)
+        std     r20,72(r3)
+        stfd    f20,224(r3)
+        std     r21,80(r3)
+        stfd    f21,232(r3)
+        std     r22,88(r3)
+        stfd    f22,240(r3)
+        std     r23,96(r3)
+        stfd    f23,248(r3)
+        std     r24,104(r3)
+        stfd    f24,256(r3)
+        std     r25,112(r3)
+        stfd    f25,264(r3)
+        std     r26,120(r3)
+        stfd    f26,272(r3)
+        std     r27,128(r3)
+        stfd    f27,280(r3)
+        std     r28,136(r3)
+        stfd    f28,288(r3)
+        std     r29,144(r3)
+        stfd    f29,296(r3)
+        std     r30,152(r3)
+        stfd    f30,304(r3)
+        std     r31,160(r3)
+        stfd    f31,312(r3)
+        addi    r5,r3,320
+        mfspr   r0,256
+        stw     r0,168(r3) // VRSAVE
+        addi    r6,r5,16
+        stvx    v20,0,r5
+        addi    r5,r5,32
+        stvx    v21,0,r6
+        addi    r6,r6,32
+        stvx    v22,0,r5
+        addi    r5,r5,32
+        stvx    v23,0,r6
+        addi    r6,r6,32
+        stvx    v24,0,r5
+        addi    r5,r5,32
+        stvx    v25,0,r6
+        addi    r6,r6,32
+        stvx    v26,0,r5
+        addi    r5,r5,32
+        stvx    v27,0,r6
+        addi    r6,r6,32
+        stvx    v28,0,r5
+        addi    r5,r5,32
+        stvx    v29,0,r6
+        addi    r6,r6,32
+        stvx    v30,0,r5
+        stvx    v31,0,r6
+        // Save into the "mask-saved" slot.
+        stw     r4,512(r3)
+        // Restore TOC, LR, and stack and return to caller.
+        ld      r2,24(r1)
+        ld      r0,48(r1)
+        addi    r1,r1,64
+        li      r3,0  // This is the sigsetjmp return path
+        mtlr    r0
+        blr
+        .size __sigsetjmp, .-.L.__sigsetjmp
+
+        .globl sigsetjmp
+        .type sigsetjmp, @function
+        .align 4
+sigsetjmp:
+        b       __sigsetjmp
+        .size sigsetjmp, .-sigsetjmp
index 7e906e5d7680239c06fdd2b1e6594efe58e9ecd4..5aff6ca56adfc03841ede95591a1383a47529ca0 100644 (file)
@@ -49,8 +49,8 @@ void TsanCheckFailed(const char *file, int line, const char *cond,
 #ifdef TSAN_EXTERNAL_HOOKS
 bool OnReport(const ReportDesc *rep, bool suppressed);
 #else
-SANITIZER_INTERFACE_ATTRIBUTE
-bool WEAK OnReport(const ReportDesc *rep, bool suppressed) {
+SANITIZER_WEAK_CXX_DEFAULT_IMPL
+bool OnReport(const ReportDesc *rep, bool suppressed) {
   (void)rep;
   return suppressed;
 }
@@ -186,7 +186,7 @@ void ScopedReport::AddThread(const ThreadContext *tctx, bool suppressable) {
       return;
   }
   void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread));
-  ReportThread *rt = new(mem) ReportThread();
+  ReportThread *rt = new(mem) ReportThread;
   rep_->threads.PushBack(rt);
   rt->id = tctx->tid;
   rt->pid = tctx->os_id;
@@ -200,16 +200,16 @@ void ScopedReport::AddThread(const ThreadContext *tctx, bool suppressable) {
 }
 
 #ifndef SANITIZER_GO
+static bool FindThreadByUidLockedCallback(ThreadContextBase *tctx, void *arg) {
+  int unique_id = *(int *)arg;
+  return tctx->unique_id == (u32)unique_id;
+}
+
 static ThreadContext *FindThreadByUidLocked(int unique_id) {
   ctx->thread_registry->CheckLocked();
-  for (unsigned i = 0; i < kMaxTid; i++) {
-    ThreadContext *tctx = static_cast<ThreadContext*>(
-        ctx->thread_registry->GetThreadLocked(i));
-    if (tctx && tctx->unique_id == (u32)unique_id) {
-      return tctx;
-    }
-  }
-  return 0;
+  return static_cast<ThreadContext *>(
+      ctx->thread_registry->FindThreadContextLocked(
+          FindThreadByUidLockedCallback, &unique_id));
 }
 
 static ThreadContext *FindThreadByTidLocked(int tid) {
@@ -256,7 +256,7 @@ void ScopedReport::AddMutex(const SyncVar *s) {
       return;
   }
   void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
-  ReportMutex *rm = new(mem) ReportMutex();
+  ReportMutex *rm = new(mem) ReportMutex;
   rep_->mutexes.PushBack(rm);
   rm->id = s->uid;
   rm->addr = s->addr;
@@ -289,7 +289,7 @@ void ScopedReport::AddDeadMutex(u64 id) {
       return;
   }
   void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
-  ReportMutex *rm = new(mem) ReportMutex();
+  ReportMutex *rm = new(mem) ReportMutex;
   rep_->mutexes.PushBack(rm);
   rm->id = id;
   rm->addr = 0;
index 1228c0a18872921f30cf710bf442e9802f110026..b992d78f8cb5de6b4830a32403e26795430b9873 100644 (file)
@@ -34,7 +34,8 @@ static const char *const std_suppressions =
 "race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n";
 
 // Can be overriden in frontend.
-extern "C" const char *WEAK __tsan_default_suppressions() {
+SANITIZER_WEAK_DEFAULT_IMPL
+const char *__tsan_default_suppressions() {
   return 0;
 }
 #endif
@@ -158,8 +159,8 @@ void PrintMatchedSuppressions() {
   Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count,
          (int)internal_getpid());
   for (uptr i = 0; i < matched.size(); i++) {
-    Printf("%d %s:%s\n", matched[i]->hit_count, matched[i]->type,
-           matched[i]->templ);
+    Printf("%d %s:%s\n", atomic_load_relaxed(&matched[i]->hit_count),
+           matched[i]->type, matched[i]->templ);
   }
 }
 }  // namespace __tsan
index a6b9bca0501d101fda04890b8cc467f6515bc441..b2423951795f2e92c2bba35d24ce5b9a9a2b8ed1 100644 (file)
@@ -38,10 +38,10 @@ void ExitSymbolizer() {
 
 // May be overriden by JIT/JAVA/etc,
 // whatever produces PCs marked with kExternalPCBit.
-extern "C" bool WEAK __tsan_symbolize_external(uptr pc,
-                                               char *func_buf, uptr func_siz,
-                                               char *file_buf, uptr file_siz,
-                                               int *line, int *col) {
+SANITIZER_WEAK_DEFAULT_IMPL
+bool __tsan_symbolize_external(uptr pc, char *func_buf, uptr func_siz,
+                               char *file_buf, uptr file_siz, int *line,
+                               int *col) {
   return false;
 }
 
@@ -71,7 +71,7 @@ ReportLocation *SymbolizeData(uptr addr) {
   if (!Symbolizer::GetOrInit()->SymbolizeData(addr, &info))
     return 0;
   ReportLocation *ent = ReportLocation::New(ReportLocationGlobal);
-  ent->global = info;
+  internal_memcpy(&ent->global, &info, sizeof(info));
   return ent;
 }
 
index 1c3f98f3f0bca145c90333bc0efc55ba5baa77d5..51181bab3a79f4cc0f8af34eaee8c4509ab60eb8 100644 (file)
@@ -33,9 +33,12 @@ macro(tsan_compile obj_list source arch)
 endmacro()
 
 macro(add_tsan_unittest testname)
-  # Build unit tests only for 64-bit Linux.
-  if(UNIX AND NOT APPLE)
-    foreach(arch ${TSAN_SUPPORTED_ARCH})
+  set(TSAN_TEST_ARCH ${TSAN_SUPPORTED_ARCH})
+  if(APPLE)
+    darwin_filter_host_archs(TSAN_SUPPORTED_ARCH TSAN_TEST_ARCH)
+  endif()
+  if(UNIX)
+    foreach(arch ${TSAN_TEST_ARCH})
       cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN})
       set(TEST_OBJECTS)
       foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE})
@@ -46,15 +49,38 @@ macro(add_tsan_unittest testname)
       if(NOT COMPILER_RT_STANDALONE_BUILD)
         list(APPEND TEST_DEPS tsan)
       endif()
-      # FIXME: Looks like we should link TSan with just-built runtime,
-      # and not rely on -fsanitize=thread, as these tests are essentially
-      # unit tests.
-      add_compiler_rt_test(TsanUnitTests ${testname}
-              OBJECTS ${TEST_OBJECTS}
-              DEPS ${TEST_DEPS}
-              LINK_FLAGS ${TARGET_LINK_FLAGS}
-                         -fsanitize=thread
-                         -lstdc++ -lm)
+      if(NOT APPLE)
+        # FIXME: Looks like we should link TSan with just-built runtime,
+        # and not rely on -fsanitize=thread, as these tests are essentially
+        # unit tests.
+        add_compiler_rt_test(TsanUnitTests ${testname}
+                OBJECTS ${TEST_OBJECTS}
+                DEPS ${TEST_DEPS}
+                LINK_FLAGS ${TARGET_LINK_FLAGS}
+                           -fsanitize=thread
+                           -lstdc++ -lm)
+      else()
+        set(TSAN_TEST_RUNTIME_OBJECTS
+          $<TARGET_OBJECTS:RTTsan_dynamic.osx>
+          $<TARGET_OBJECTS:RTInterception.osx>
+          $<TARGET_OBJECTS:RTSanitizerCommon.osx>
+          $<TARGET_OBJECTS:RTSanitizerCommonLibc.osx>
+          $<TARGET_OBJECTS:RTUbsan.osx>)
+        set(TSAN_TEST_RUNTIME RTTsanTest.${testname}.${arch})
+        add_library(${TSAN_TEST_RUNTIME} STATIC ${TSAN_TEST_RUNTIME_OBJECTS})
+        set_target_properties(${TSAN_TEST_RUNTIME} PROPERTIES
+          ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+        list(APPEND TEST_OBJECTS lib${TSAN_TEST_RUNTIME}.a)
+        list(APPEND TEST_DEPS ${TSAN_TEST_RUNTIME})
+        # Intentionally do *not* link with `-fsanitize=thread`. We already link
+        # against a static version of the runtime, and we don't want the dynamic
+        # one.
+        add_compiler_rt_test(TsanUnitTests "${testname}-${arch}-Test"
+                OBJECTS ${TEST_OBJECTS}
+                DEPS ${TEST_DEPS}
+                LINK_FLAGS ${TARGET_LINK_FLAGS}
+                           -lc++)
+      endif()
     endforeach()
   endif()
 endmacro()
index 989566d9e041f6f25c3980450409db2e31ad6cf6..a34f08ea965b820ad17ff531be7d7a61a2f585a6 100644 (file)
@@ -7,8 +7,8 @@ set(TSAN_RTL_TEST_SOURCES
   tsan_test.cc
   tsan_thread.cc)
 
-if(UNIX AND NOT APPLE)
-  list(APPEND TSAN_RTL_TEST_SOURCES tsan_test_util_linux.cc)
+if(UNIX)
+  list(APPEND TSAN_RTL_TEST_SOURCES tsan_test_util_posix.cc)
 endif()
 
 set(TSAN_RTL_TEST_HEADERS
index 71fb05c22806af7cfba2b4827ece0fbd25e25aa4..e1a61b5e43f1b3101d86a6519877c294dfcb4884 100644 (file)
@@ -35,7 +35,7 @@ static void thread_secific_dtor(void *v) {
   __tsan_write4(&k->cnt);
   EXPECT_EQ(pthread_mutex_unlock(k->mtx), 0);
   if (k->val == 42) {
-    delete k;
+    // Okay.
   } else if (k->val == 43 || k->val == 44) {
     k->val--;
     EXPECT_EQ(pthread_setspecific(k->key, k), 0);
@@ -57,14 +57,13 @@ TEST(Posix, ThreadSpecificDtors) {
   pthread_mutex_t mtx;
   EXPECT_EQ(pthread_mutex_init(&mtx, 0), 0);
   pthread_t th[3];
-  thread_key *k[3];
-  k[0] = new thread_key(key, &mtx, 42, &cnt);
-  k[1] = new thread_key(key, &mtx, 43, &cnt);
-  k[2] = new thread_key(key, &mtx, 44, &cnt);
-  EXPECT_EQ(pthread_create(&th[0], 0, dtors_thread, k[0]), 0);
-  EXPECT_EQ(pthread_create(&th[1], 0, dtors_thread, k[1]), 0);
+  thread_key k1 = thread_key(key, &mtx, 42, &cnt);
+  thread_key k2 = thread_key(key, &mtx, 43, &cnt);
+  thread_key k3 = thread_key(key, &mtx, 44, &cnt);
+  EXPECT_EQ(pthread_create(&th[0], 0, dtors_thread, &k1), 0);
+  EXPECT_EQ(pthread_create(&th[1], 0, dtors_thread, &k2), 0);
   EXPECT_EQ(pthread_join(th[0], 0), 0);
-  EXPECT_EQ(pthread_create(&th[2], 0, dtors_thread, k[2]), 0);
+  EXPECT_EQ(pthread_create(&th[2], 0, dtors_thread, &k3), 0);
   EXPECT_EQ(pthread_join(th[1], 0), 0);
   EXPECT_EQ(pthread_join(th[2], 0), 0);
   EXPECT_EQ(pthread_key_delete(key), 0);
index b8b9555c2bff13f1cd3322a42504d17774654851..edfede078b683d93a25f326b5edfd5dae1a7f1db 100644 (file)
@@ -47,6 +47,13 @@ int run_tests(int argc, char **argv) {
 
 const char *argv0;
 
+#ifdef __APPLE__
+// On Darwin, turns off symbolication and crash logs to make tests faster.
+extern "C" const char* __tsan_default_options() {
+  return "symbolize=false:abort_on_error=0";
+}
+#endif
+
 int main(int argc, char **argv) {
   argv0 = argv[0];
   return run_tests(argc, argv);
index 84d277b137f0707e8c2a663d5d3c16f4cf26c10e..31b1b188f6247b65db0bfad06e12999b37f7433d 100644 (file)
@@ -31,7 +31,15 @@ class MemLoc {
 
 class Mutex {
  public:
-  enum Type { Normal, Spin, RW };
+  enum Type {
+    Normal,
+    RW,
+#ifndef __APPLE__
+    Spin
+#else
+    Spin = Normal
+#endif
+  };
 
   explicit Mutex(Type type = Normal);
   ~Mutex();
diff --git a/src/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_linux.cc b/src/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_linux.cc
deleted file mode 100644 (file)
index 9298bf0..0000000
+++ /dev/null
@@ -1,470 +0,0 @@
-
-//===-- tsan_test_util_linux.cc -------------------------------------------===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-// Test utils, Linux and FreeBSD implementation.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_atomic.h"
-#include "tsan_interface.h"
-#include "tsan_test_util.h"
-#include "tsan_report.h"
-
-#include "gtest/gtest.h"
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-using namespace __tsan;  // NOLINT
-
-static __thread bool expect_report;
-static __thread bool expect_report_reported;
-static __thread ReportType expect_report_type;
-
-extern "C" void *__interceptor_memcpy(void*, const void*, uptr);
-extern "C" void *__interceptor_memset(void*, int, uptr);
-
-static void *BeforeInitThread(void *param) {
-  (void)param;
-  return 0;
-}
-
-static void AtExit() {
-}
-
-void TestMutexBeforeInit() {
-  // Mutexes must be usable before __tsan_init();
-  pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
-  pthread_mutex_lock(&mtx);
-  pthread_mutex_unlock(&mtx);
-  pthread_mutex_destroy(&mtx);
-  pthread_t thr;
-  pthread_create(&thr, 0, BeforeInitThread, 0);
-  pthread_join(thr, 0);
-  atexit(AtExit);
-}
-
-namespace __tsan {
-bool OnReport(const ReportDesc *rep, bool suppressed) {
-  if (expect_report) {
-    if (rep->typ != expect_report_type) {
-      printf("Expected report of type %d, got type %d\n",
-             (int)expect_report_type, (int)rep->typ);
-      EXPECT_FALSE("Wrong report type");
-      return false;
-    }
-  } else {
-    EXPECT_FALSE("Unexpected report");
-    return false;
-  }
-  expect_report_reported = true;
-  return true;
-}
-}  // namespace __tsan
-
-static void* allocate_addr(int size, int offset_from_aligned = 0) {
-  static uintptr_t foo;
-  static atomic_uintptr_t uniq = {(uintptr_t)&foo};  // Some real address.
-  const int kAlign = 16;
-  CHECK(offset_from_aligned < kAlign);
-  size = (size + 2 * kAlign) & ~(kAlign - 1);
-  uintptr_t addr = atomic_fetch_add(&uniq, size, memory_order_relaxed);
-  return (void*)(addr + offset_from_aligned);
-}
-
-MemLoc::MemLoc(int offset_from_aligned)
-  : loc_(allocate_addr(16, offset_from_aligned)) {
-}
-
-MemLoc::~MemLoc() {
-}
-
-Mutex::Mutex(Type type)
-  : alive_()
-  , type_(type) {
-}
-
-Mutex::~Mutex() {
-  CHECK(!alive_);
-}
-
-void Mutex::Init() {
-  CHECK(!alive_);
-  alive_ = true;
-  if (type_ == Normal)
-    CHECK_EQ(pthread_mutex_init((pthread_mutex_t*)mtx_, 0), 0);
-  else if (type_ == Spin)
-    CHECK_EQ(pthread_spin_init((pthread_spinlock_t*)mtx_, 0), 0);
-  else if (type_ == RW)
-    CHECK_EQ(pthread_rwlock_init((pthread_rwlock_t*)mtx_, 0), 0);
-  else
-    CHECK(0);
-}
-
-void Mutex::StaticInit() {
-  CHECK(!alive_);
-  CHECK(type_ == Normal);
-  alive_ = true;
-  pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER;
-  memcpy(mtx_, &tmp, sizeof(tmp));
-}
-
-void Mutex::Destroy() {
-  CHECK(alive_);
-  alive_ = false;
-  if (type_ == Normal)
-    CHECK_EQ(pthread_mutex_destroy((pthread_mutex_t*)mtx_), 0);
-  else if (type_ == Spin)
-    CHECK_EQ(pthread_spin_destroy((pthread_spinlock_t*)mtx_), 0);
-  else if (type_ == RW)
-    CHECK_EQ(pthread_rwlock_destroy((pthread_rwlock_t*)mtx_), 0);
-}
-
-void Mutex::Lock() {
-  CHECK(alive_);
-  if (type_ == Normal)
-    CHECK_EQ(pthread_mutex_lock((pthread_mutex_t*)mtx_), 0);
-  else if (type_ == Spin)
-    CHECK_EQ(pthread_spin_lock((pthread_spinlock_t*)mtx_), 0);
-  else if (type_ == RW)
-    CHECK_EQ(pthread_rwlock_wrlock((pthread_rwlock_t*)mtx_), 0);
-}
-
-bool Mutex::TryLock() {
-  CHECK(alive_);
-  if (type_ == Normal)
-    return pthread_mutex_trylock((pthread_mutex_t*)mtx_) == 0;
-  else if (type_ == Spin)
-    return pthread_spin_trylock((pthread_spinlock_t*)mtx_) == 0;
-  else if (type_ == RW)
-    return pthread_rwlock_trywrlock((pthread_rwlock_t*)mtx_) == 0;
-  return false;
-}
-
-void Mutex::Unlock() {
-  CHECK(alive_);
-  if (type_ == Normal)
-    CHECK_EQ(pthread_mutex_unlock((pthread_mutex_t*)mtx_), 0);
-  else if (type_ == Spin)
-    CHECK_EQ(pthread_spin_unlock((pthread_spinlock_t*)mtx_), 0);
-  else if (type_ == RW)
-    CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0);
-}
-
-void Mutex::ReadLock() {
-  CHECK(alive_);
-  CHECK(type_ == RW);
-  CHECK_EQ(pthread_rwlock_rdlock((pthread_rwlock_t*)mtx_), 0);
-}
-
-bool Mutex::TryReadLock() {
-  CHECK(alive_);
-  CHECK(type_ == RW);
-  return pthread_rwlock_tryrdlock((pthread_rwlock_t*)mtx_) ==  0;
-}
-
-void Mutex::ReadUnlock() {
-  CHECK(alive_);
-  CHECK(type_ == RW);
-  CHECK_EQ(pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0);
-}
-
-struct Event {
-  enum Type {
-    SHUTDOWN,
-    READ,
-    WRITE,
-    VPTR_UPDATE,
-    CALL,
-    RETURN,
-    MUTEX_CREATE,
-    MUTEX_DESTROY,
-    MUTEX_LOCK,
-    MUTEX_TRYLOCK,
-    MUTEX_UNLOCK,
-    MUTEX_READLOCK,
-    MUTEX_TRYREADLOCK,
-    MUTEX_READUNLOCK,
-    MEMCPY,
-    MEMSET
-  };
-  Type type;
-  void *ptr;
-  uptr arg;
-  uptr arg2;
-  bool res;
-  bool expect_report;
-  ReportType report_type;
-
-  Event(Type type, const void *ptr = 0, uptr arg = 0, uptr arg2 = 0)
-    : type(type)
-    , ptr(const_cast<void*>(ptr))
-    , arg(arg)
-    , arg2(arg2)
-    , res()
-    , expect_report()
-    , report_type() {
-  }
-
-  void ExpectReport(ReportType type) {
-    expect_report = true;
-    report_type = type;
-  }
-};
-
-struct ScopedThread::Impl {
-  pthread_t thread;
-  bool main;
-  bool detached;
-  atomic_uintptr_t event;  // Event*
-
-  static void *ScopedThreadCallback(void *arg);
-  void send(Event *ev);
-  void HandleEvent(Event *ev);
-};
-
-void ScopedThread::Impl::HandleEvent(Event *ev) {
-  CHECK_EQ(expect_report, false);
-  expect_report = ev->expect_report;
-  expect_report_reported = false;
-  expect_report_type = ev->report_type;
-  switch (ev->type) {
-  case Event::READ:
-  case Event::WRITE: {
-    void (*tsan_mop)(void *addr) = 0;
-    if (ev->type == Event::READ) {
-      switch (ev->arg /*size*/) {
-        case 1: tsan_mop = __tsan_read1; break;
-        case 2: tsan_mop = __tsan_read2; break;
-        case 4: tsan_mop = __tsan_read4; break;
-        case 8: tsan_mop = __tsan_read8; break;
-        case 16: tsan_mop = __tsan_read16; break;
-      }
-    } else {
-      switch (ev->arg /*size*/) {
-        case 1: tsan_mop = __tsan_write1; break;
-        case 2: tsan_mop = __tsan_write2; break;
-        case 4: tsan_mop = __tsan_write4; break;
-        case 8: tsan_mop = __tsan_write8; break;
-        case 16: tsan_mop = __tsan_write16; break;
-      }
-    }
-    CHECK_NE(tsan_mop, 0);
-#if defined(__FreeBSD__)
-    const int ErrCode = ESOCKTNOSUPPORT;
-#else
-    const int ErrCode = ECHRNG;
-#endif
-    errno = ErrCode;
-    tsan_mop(ev->ptr);
-    CHECK_EQ(ErrCode, errno);  // In no case must errno be changed.
-    break;
-  }
-  case Event::VPTR_UPDATE:
-    __tsan_vptr_update((void**)ev->ptr, (void*)ev->arg);
-    break;
-  case Event::CALL:
-    __tsan_func_entry((void*)((uptr)ev->ptr));
-    break;
-  case Event::RETURN:
-    __tsan_func_exit();
-    break;
-  case Event::MUTEX_CREATE:
-    static_cast<Mutex*>(ev->ptr)->Init();
-    break;
-  case Event::MUTEX_DESTROY:
-    static_cast<Mutex*>(ev->ptr)->Destroy();
-    break;
-  case Event::MUTEX_LOCK:
-    static_cast<Mutex*>(ev->ptr)->Lock();
-    break;
-  case Event::MUTEX_TRYLOCK:
-    ev->res = static_cast<Mutex*>(ev->ptr)->TryLock();
-    break;
-  case Event::MUTEX_UNLOCK:
-    static_cast<Mutex*>(ev->ptr)->Unlock();
-    break;
-  case Event::MUTEX_READLOCK:
-    static_cast<Mutex*>(ev->ptr)->ReadLock();
-    break;
-  case Event::MUTEX_TRYREADLOCK:
-    ev->res = static_cast<Mutex*>(ev->ptr)->TryReadLock();
-    break;
-  case Event::MUTEX_READUNLOCK:
-    static_cast<Mutex*>(ev->ptr)->ReadUnlock();
-    break;
-  case Event::MEMCPY:
-    __interceptor_memcpy(ev->ptr, (void*)ev->arg, ev->arg2);
-    break;
-  case Event::MEMSET:
-    __interceptor_memset(ev->ptr, ev->arg, ev->arg2);
-    break;
-  default: CHECK(0);
-  }
-  if (expect_report && !expect_report_reported) {
-    printf("Missed expected report of type %d\n", (int)ev->report_type);
-    EXPECT_FALSE("Missed expected race");
-  }
-  expect_report = false;
-}
-
-void *ScopedThread::Impl::ScopedThreadCallback(void *arg) {
-  __tsan_func_entry(__builtin_return_address(0));
-  Impl *impl = (Impl*)arg;
-  for (;;) {
-    Event* ev = (Event*)atomic_load(&impl->event, memory_order_acquire);
-    if (ev == 0) {
-      pthread_yield();
-      continue;
-    }
-    if (ev->type == Event::SHUTDOWN) {
-      atomic_store(&impl->event, 0, memory_order_release);
-      break;
-    }
-    impl->HandleEvent(ev);
-    atomic_store(&impl->event, 0, memory_order_release);
-  }
-  __tsan_func_exit();
-  return 0;
-}
-
-void ScopedThread::Impl::send(Event *e) {
-  if (main) {
-    HandleEvent(e);
-  } else {
-    CHECK_EQ(atomic_load(&event, memory_order_relaxed), 0);
-    atomic_store(&event, (uintptr_t)e, memory_order_release);
-    while (atomic_load(&event, memory_order_acquire) != 0)
-      pthread_yield();
-  }
-}
-
-ScopedThread::ScopedThread(bool detached, bool main) {
-  impl_ = new Impl;
-  impl_->main = main;
-  impl_->detached = detached;
-  atomic_store(&impl_->event, 0, memory_order_relaxed);
-  if (!main) {
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, detached);
-    pthread_attr_setstacksize(&attr, 64*1024);
-    pthread_create(&impl_->thread, &attr,
-        ScopedThread::Impl::ScopedThreadCallback, impl_);
-  }
-}
-
-ScopedThread::~ScopedThread() {
-  if (!impl_->main) {
-    Event event(Event::SHUTDOWN);
-    impl_->send(&event);
-    if (!impl_->detached)
-      pthread_join(impl_->thread, 0);
-  }
-  delete impl_;
-}
-
-void ScopedThread::Detach() {
-  CHECK(!impl_->main);
-  CHECK(!impl_->detached);
-  impl_->detached = true;
-  pthread_detach(impl_->thread);
-}
-
-void ScopedThread::Access(void *addr, bool is_write,
-                          int size, bool expect_race) {
-  Event event(is_write ? Event::WRITE : Event::READ, addr, size);
-  if (expect_race)
-    event.ExpectReport(ReportTypeRace);
-  impl_->send(&event);
-}
-
-void ScopedThread::VptrUpdate(const MemLoc &vptr,
-                              const MemLoc &new_val,
-                              bool expect_race) {
-  Event event(Event::VPTR_UPDATE, vptr.loc(), (uptr)new_val.loc());
-  if (expect_race)
-    event.ExpectReport(ReportTypeRace);
-  impl_->send(&event);
-}
-
-void ScopedThread::Call(void(*pc)()) {
-  Event event(Event::CALL, (void*)((uintptr_t)pc));
-  impl_->send(&event);
-}
-
-void ScopedThread::Return() {
-  Event event(Event::RETURN);
-  impl_->send(&event);
-}
-
-void ScopedThread::Create(const Mutex &m) {
-  Event event(Event::MUTEX_CREATE, &m);
-  impl_->send(&event);
-}
-
-void ScopedThread::Destroy(const Mutex &m) {
-  Event event(Event::MUTEX_DESTROY, &m);
-  impl_->send(&event);
-}
-
-void ScopedThread::Lock(const Mutex &m) {
-  Event event(Event::MUTEX_LOCK, &m);
-  impl_->send(&event);
-}
-
-bool ScopedThread::TryLock(const Mutex &m) {
-  Event event(Event::MUTEX_TRYLOCK, &m);
-  impl_->send(&event);
-  return event.res;
-}
-
-void ScopedThread::Unlock(const Mutex &m) {
-  Event event(Event::MUTEX_UNLOCK, &m);
-  impl_->send(&event);
-}
-
-void ScopedThread::ReadLock(const Mutex &m) {
-  Event event(Event::MUTEX_READLOCK, &m);
-  impl_->send(&event);
-}
-
-bool ScopedThread::TryReadLock(const Mutex &m) {
-  Event event(Event::MUTEX_TRYREADLOCK, &m);
-  impl_->send(&event);
-  return event.res;
-}
-
-void ScopedThread::ReadUnlock(const Mutex &m) {
-  Event event(Event::MUTEX_READUNLOCK, &m);
-  impl_->send(&event);
-}
-
-void ScopedThread::Memcpy(void *dst, const void *src, int size,
-                          bool expect_race) {
-  Event event(Event::MEMCPY, dst, (uptr)src, size);
-  if (expect_race)
-    event.ExpectReport(ReportTypeRace);
-  impl_->send(&event);
-}
-
-void ScopedThread::Memset(void *dst, int val, int size,
-                          bool expect_race) {
-  Event event(Event::MEMSET, dst, val, size);
-  if (expect_race)
-    event.ExpectReport(ReportTypeRace);
-  impl_->send(&event);
-}
diff --git a/src/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cc b/src/compiler-rt/lib/tsan/tests/rtl/tsan_test_util_posix.cc
new file mode 100644 (file)
index 0000000..c8be088
--- /dev/null
@@ -0,0 +1,523 @@
+//===-- tsan_test_util_posix.cc -------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Test utils, Linux, FreeBSD and Darwin implementation.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "tsan_interface.h"
+#include "tsan_test_util.h"
+#include "tsan_report.h"
+
+#include "gtest/gtest.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+using namespace __tsan;  // NOLINT
+
+static __thread bool expect_report;
+static __thread bool expect_report_reported;
+static __thread ReportType expect_report_type;
+
+#ifdef __APPLE__
+#define __interceptor_memcpy wrap_memcpy
+#define __interceptor_memset wrap_memset
+#define __interceptor_pthread_create wrap_pthread_create
+#define __interceptor_pthread_join wrap_pthread_join
+#define __interceptor_pthread_detach wrap_pthread_detach
+#define __interceptor_pthread_mutex_init wrap_pthread_mutex_init
+#define __interceptor_pthread_mutex_lock wrap_pthread_mutex_lock
+#define __interceptor_pthread_mutex_unlock wrap_pthread_mutex_unlock
+#define __interceptor_pthread_mutex_destroy wrap_pthread_mutex_destroy
+#define __interceptor_pthread_mutex_trylock wrap_pthread_mutex_trylock
+#define __interceptor_pthread_rwlock_init wrap_pthread_rwlock_init
+#define __interceptor_pthread_rwlock_destroy wrap_pthread_rwlock_destroy
+#define __interceptor_pthread_rwlock_trywrlock wrap_pthread_rwlock_trywrlock
+#define __interceptor_pthread_rwlock_wrlock wrap_pthread_rwlock_wrlock
+#define __interceptor_pthread_rwlock_unlock wrap_pthread_rwlock_unlock
+#define __interceptor_pthread_rwlock_rdlock wrap_pthread_rwlock_rdlock
+#define __interceptor_pthread_rwlock_tryrdlock wrap_pthread_rwlock_tryrdlock
+#endif
+
+extern "C" void *__interceptor_memcpy(void *, const void *, uptr);
+extern "C" void *__interceptor_memset(void *, int, uptr);
+extern "C" int __interceptor_pthread_create(pthread_t *thread,
+                                            const pthread_attr_t *attr,
+                                            void *(*start_routine)(void *),
+                                            void *arg);
+extern "C" int __interceptor_pthread_join(pthread_t thread, void **value_ptr);
+extern "C" int __interceptor_pthread_detach(pthread_t thread);
+
+extern "C" int __interceptor_pthread_mutex_init(
+    pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
+extern "C" int __interceptor_pthread_mutex_lock(pthread_mutex_t *mutex);
+extern "C" int __interceptor_pthread_mutex_unlock(pthread_mutex_t *mutex);
+extern "C" int __interceptor_pthread_mutex_destroy(pthread_mutex_t *mutex);
+extern "C" int __interceptor_pthread_mutex_trylock(pthread_mutex_t *mutex);
+
+extern "C" int __interceptor_pthread_rwlock_init(
+    pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
+extern "C" int __interceptor_pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
+extern "C" int __interceptor_pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
+extern "C" int __interceptor_pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
+extern "C" int __interceptor_pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
+extern "C" int __interceptor_pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
+extern "C" int __interceptor_pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
+
+
+static void *BeforeInitThread(void *param) {
+  (void)param;
+  return 0;
+}
+
+static void AtExit() {
+}
+
+void TestMutexBeforeInit() {
+  // Mutexes must be usable before __tsan_init();
+  pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+  __interceptor_pthread_mutex_lock(&mtx);
+  __interceptor_pthread_mutex_unlock(&mtx);
+  __interceptor_pthread_mutex_destroy(&mtx);
+  pthread_t thr;
+  __interceptor_pthread_create(&thr, 0, BeforeInitThread, 0);
+  __interceptor_pthread_join(thr, 0);
+  atexit(AtExit);
+}
+
+namespace __tsan {
+bool OnReport(const ReportDesc *rep, bool suppressed) {
+  if (expect_report) {
+    if (rep->typ != expect_report_type) {
+      printf("Expected report of type %d, got type %d\n",
+             (int)expect_report_type, (int)rep->typ);
+      EXPECT_FALSE("Wrong report type");
+      return false;
+    }
+  } else {
+    EXPECT_FALSE("Unexpected report");
+    return false;
+  }
+  expect_report_reported = true;
+  return true;
+}
+}  // namespace __tsan
+
+static void* allocate_addr(int size, int offset_from_aligned = 0) {
+  static uintptr_t foo;
+  static atomic_uintptr_t uniq = {(uintptr_t)&foo};  // Some real address.
+  const int kAlign = 16;
+  CHECK(offset_from_aligned < kAlign);
+  size = (size + 2 * kAlign) & ~(kAlign - 1);
+  uintptr_t addr = atomic_fetch_add(&uniq, size, memory_order_relaxed);
+  return (void*)(addr + offset_from_aligned);
+}
+
+MemLoc::MemLoc(int offset_from_aligned)
+  : loc_(allocate_addr(16, offset_from_aligned)) {
+}
+
+MemLoc::~MemLoc() {
+}
+
+Mutex::Mutex(Type type)
+  : alive_()
+  , type_(type) {
+}
+
+Mutex::~Mutex() {
+  CHECK(!alive_);
+}
+
+void Mutex::Init() {
+  CHECK(!alive_);
+  alive_ = true;
+  if (type_ == Normal)
+    CHECK_EQ(__interceptor_pthread_mutex_init((pthread_mutex_t*)mtx_, 0), 0);
+#ifndef __APPLE__
+  else if (type_ == Spin)
+    CHECK_EQ(pthread_spin_init((pthread_spinlock_t*)mtx_, 0), 0);
+#endif
+  else if (type_ == RW)
+    CHECK_EQ(__interceptor_pthread_rwlock_init((pthread_rwlock_t*)mtx_, 0), 0);
+  else
+    CHECK(0);
+}
+
+void Mutex::StaticInit() {
+  CHECK(!alive_);
+  CHECK(type_ == Normal);
+  alive_ = true;
+  pthread_mutex_t tmp = PTHREAD_MUTEX_INITIALIZER;
+  memcpy(mtx_, &tmp, sizeof(tmp));
+}
+
+void Mutex::Destroy() {
+  CHECK(alive_);
+  alive_ = false;
+  if (type_ == Normal)
+    CHECK_EQ(__interceptor_pthread_mutex_destroy((pthread_mutex_t*)mtx_), 0);
+#ifndef __APPLE__
+  else if (type_ == Spin)
+    CHECK_EQ(pthread_spin_destroy((pthread_spinlock_t*)mtx_), 0);
+#endif
+  else if (type_ == RW)
+    CHECK_EQ(__interceptor_pthread_rwlock_destroy((pthread_rwlock_t*)mtx_), 0);
+}
+
+void Mutex::Lock() {
+  CHECK(alive_);
+  if (type_ == Normal)
+    CHECK_EQ(__interceptor_pthread_mutex_lock((pthread_mutex_t*)mtx_), 0);
+#ifndef __APPLE__
+  else if (type_ == Spin)
+    CHECK_EQ(pthread_spin_lock((pthread_spinlock_t*)mtx_), 0);
+#endif
+  else if (type_ == RW)
+    CHECK_EQ(__interceptor_pthread_rwlock_wrlock((pthread_rwlock_t*)mtx_), 0);
+}
+
+bool Mutex::TryLock() {
+  CHECK(alive_);
+  if (type_ == Normal)
+    return __interceptor_pthread_mutex_trylock((pthread_mutex_t*)mtx_) == 0;
+#ifndef __APPLE__
+  else if (type_ == Spin)
+    return pthread_spin_trylock((pthread_spinlock_t*)mtx_) == 0;
+#endif
+  else if (type_ == RW)
+    return __interceptor_pthread_rwlock_trywrlock((pthread_rwlock_t*)mtx_) == 0;
+  return false;
+}
+
+void Mutex::Unlock() {
+  CHECK(alive_);
+  if (type_ == Normal)
+    CHECK_EQ(__interceptor_pthread_mutex_unlock((pthread_mutex_t*)mtx_), 0);
+#ifndef __APPLE__
+  else if (type_ == Spin)
+    CHECK_EQ(pthread_spin_unlock((pthread_spinlock_t*)mtx_), 0);
+#endif
+  else if (type_ == RW)
+    CHECK_EQ(__interceptor_pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0);
+}
+
+void Mutex::ReadLock() {
+  CHECK(alive_);
+  CHECK(type_ == RW);
+  CHECK_EQ(__interceptor_pthread_rwlock_rdlock((pthread_rwlock_t*)mtx_), 0);
+}
+
+bool Mutex::TryReadLock() {
+  CHECK(alive_);
+  CHECK(type_ == RW);
+  return __interceptor_pthread_rwlock_tryrdlock((pthread_rwlock_t*)mtx_) ==  0;
+}
+
+void Mutex::ReadUnlock() {
+  CHECK(alive_);
+  CHECK(type_ == RW);
+  CHECK_EQ(__interceptor_pthread_rwlock_unlock((pthread_rwlock_t*)mtx_), 0);
+}
+
+struct Event {
+  enum Type {
+    SHUTDOWN,
+    READ,
+    WRITE,
+    VPTR_UPDATE,
+    CALL,
+    RETURN,
+    MUTEX_CREATE,
+    MUTEX_DESTROY,
+    MUTEX_LOCK,
+    MUTEX_TRYLOCK,
+    MUTEX_UNLOCK,
+    MUTEX_READLOCK,
+    MUTEX_TRYREADLOCK,
+    MUTEX_READUNLOCK,
+    MEMCPY,
+    MEMSET
+  };
+  Type type;
+  void *ptr;
+  uptr arg;
+  uptr arg2;
+  bool res;
+  bool expect_report;
+  ReportType report_type;
+
+  Event(Type type, const void *ptr = 0, uptr arg = 0, uptr arg2 = 0)
+    : type(type)
+    , ptr(const_cast<void*>(ptr))
+    , arg(arg)
+    , arg2(arg2)
+    , res()
+    , expect_report()
+    , report_type() {
+  }
+
+  void ExpectReport(ReportType type) {
+    expect_report = true;
+    report_type = type;
+  }
+};
+
+struct ScopedThread::Impl {
+  pthread_t thread;
+  bool main;
+  bool detached;
+  atomic_uintptr_t event;  // Event*
+
+  static void *ScopedThreadCallback(void *arg);
+  void send(Event *ev);
+  void HandleEvent(Event *ev);
+};
+
+void ScopedThread::Impl::HandleEvent(Event *ev) {
+  CHECK_EQ(expect_report, false);
+  expect_report = ev->expect_report;
+  expect_report_reported = false;
+  expect_report_type = ev->report_type;
+  switch (ev->type) {
+  case Event::READ:
+  case Event::WRITE: {
+    void (*tsan_mop)(void *addr) = 0;
+    if (ev->type == Event::READ) {
+      switch (ev->arg /*size*/) {
+        case 1: tsan_mop = __tsan_read1; break;
+        case 2: tsan_mop = __tsan_read2; break;
+        case 4: tsan_mop = __tsan_read4; break;
+        case 8: tsan_mop = __tsan_read8; break;
+        case 16: tsan_mop = __tsan_read16; break;
+      }
+    } else {
+      switch (ev->arg /*size*/) {
+        case 1: tsan_mop = __tsan_write1; break;
+        case 2: tsan_mop = __tsan_write2; break;
+        case 4: tsan_mop = __tsan_write4; break;
+        case 8: tsan_mop = __tsan_write8; break;
+        case 16: tsan_mop = __tsan_write16; break;
+      }
+    }
+    CHECK_NE(tsan_mop, 0);
+#if defined(__FreeBSD__) || defined(__APPLE__)
+    const int ErrCode = ESOCKTNOSUPPORT;
+#else
+    const int ErrCode = ECHRNG;
+#endif
+    errno = ErrCode;
+    tsan_mop(ev->ptr);
+    CHECK_EQ(ErrCode, errno);  // In no case must errno be changed.
+    break;
+  }
+  case Event::VPTR_UPDATE:
+    __tsan_vptr_update((void**)ev->ptr, (void*)ev->arg);
+    break;
+  case Event::CALL:
+    __tsan_func_entry((void*)((uptr)ev->ptr));
+    break;
+  case Event::RETURN:
+    __tsan_func_exit();
+    break;
+  case Event::MUTEX_CREATE:
+    static_cast<Mutex*>(ev->ptr)->Init();
+    break;
+  case Event::MUTEX_DESTROY:
+    static_cast<Mutex*>(ev->ptr)->Destroy();
+    break;
+  case Event::MUTEX_LOCK:
+    static_cast<Mutex*>(ev->ptr)->Lock();
+    break;
+  case Event::MUTEX_TRYLOCK:
+    ev->res = static_cast<Mutex*>(ev->ptr)->TryLock();
+    break;
+  case Event::MUTEX_UNLOCK:
+    static_cast<Mutex*>(ev->ptr)->Unlock();
+    break;
+  case Event::MUTEX_READLOCK:
+    static_cast<Mutex*>(ev->ptr)->ReadLock();
+    break;
+  case Event::MUTEX_TRYREADLOCK:
+    ev->res = static_cast<Mutex*>(ev->ptr)->TryReadLock();
+    break;
+  case Event::MUTEX_READUNLOCK:
+    static_cast<Mutex*>(ev->ptr)->ReadUnlock();
+    break;
+  case Event::MEMCPY:
+    __interceptor_memcpy(ev->ptr, (void*)ev->arg, ev->arg2);
+    break;
+  case Event::MEMSET:
+    __interceptor_memset(ev->ptr, ev->arg, ev->arg2);
+    break;
+  default: CHECK(0);
+  }
+  if (expect_report && !expect_report_reported) {
+    printf("Missed expected report of type %d\n", (int)ev->report_type);
+    EXPECT_FALSE("Missed expected race");
+  }
+  expect_report = false;
+}
+
+void *ScopedThread::Impl::ScopedThreadCallback(void *arg) {
+  __tsan_func_entry(__builtin_return_address(0));
+  Impl *impl = (Impl*)arg;
+  for (;;) {
+    Event* ev = (Event*)atomic_load(&impl->event, memory_order_acquire);
+    if (ev == 0) {
+      sched_yield();
+      continue;
+    }
+    if (ev->type == Event::SHUTDOWN) {
+      atomic_store(&impl->event, 0, memory_order_release);
+      break;
+    }
+    impl->HandleEvent(ev);
+    atomic_store(&impl->event, 0, memory_order_release);
+  }
+  __tsan_func_exit();
+  return 0;
+}
+
+void ScopedThread::Impl::send(Event *e) {
+  if (main) {
+    HandleEvent(e);
+  } else {
+    CHECK_EQ(atomic_load(&event, memory_order_relaxed), 0);
+    atomic_store(&event, (uintptr_t)e, memory_order_release);
+    while (atomic_load(&event, memory_order_acquire) != 0)
+      sched_yield();
+  }
+}
+
+ScopedThread::ScopedThread(bool detached, bool main) {
+  impl_ = new Impl;
+  impl_->main = main;
+  impl_->detached = detached;
+  atomic_store(&impl_->event, 0, memory_order_relaxed);
+  if (!main) {
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(
+        &attr, detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE);
+    pthread_attr_setstacksize(&attr, 64*1024);
+    __interceptor_pthread_create(&impl_->thread, &attr,
+        ScopedThread::Impl::ScopedThreadCallback, impl_);
+  }
+}
+
+ScopedThread::~ScopedThread() {
+  if (!impl_->main) {
+    Event event(Event::SHUTDOWN);
+    impl_->send(&event);
+    if (!impl_->detached)
+      __interceptor_pthread_join(impl_->thread, 0);
+  }
+  delete impl_;
+}
+
+void ScopedThread::Detach() {
+  CHECK(!impl_->main);
+  CHECK(!impl_->detached);
+  impl_->detached = true;
+  __interceptor_pthread_detach(impl_->thread);
+}
+
+void ScopedThread::Access(void *addr, bool is_write,
+                          int size, bool expect_race) {
+  Event event(is_write ? Event::WRITE : Event::READ, addr, size);
+  if (expect_race)
+    event.ExpectReport(ReportTypeRace);
+  impl_->send(&event);
+}
+
+void ScopedThread::VptrUpdate(const MemLoc &vptr,
+                              const MemLoc &new_val,
+                              bool expect_race) {
+  Event event(Event::VPTR_UPDATE, vptr.loc(), (uptr)new_val.loc());
+  if (expect_race)
+    event.ExpectReport(ReportTypeRace);
+  impl_->send(&event);
+}
+
+void ScopedThread::Call(void(*pc)()) {
+  Event event(Event::CALL, (void*)((uintptr_t)pc));
+  impl_->send(&event);
+}
+
+void ScopedThread::Return() {
+  Event event(Event::RETURN);
+  impl_->send(&event);
+}
+
+void ScopedThread::Create(const Mutex &m) {
+  Event event(Event::MUTEX_CREATE, &m);
+  impl_->send(&event);
+}
+
+void ScopedThread::Destroy(const Mutex &m) {
+  Event event(Event::MUTEX_DESTROY, &m);
+  impl_->send(&event);
+}
+
+void ScopedThread::Lock(const Mutex &m) {
+  Event event(Event::MUTEX_LOCK, &m);
+  impl_->send(&event);
+}
+
+bool ScopedThread::TryLock(const Mutex &m) {
+  Event event(Event::MUTEX_TRYLOCK, &m);
+  impl_->send(&event);
+  return event.res;
+}
+
+void ScopedThread::Unlock(const Mutex &m) {
+  Event event(Event::MUTEX_UNLOCK, &m);
+  impl_->send(&event);
+}
+
+void ScopedThread::ReadLock(const Mutex &m) {
+  Event event(Event::MUTEX_READLOCK, &m);
+  impl_->send(&event);
+}
+
+bool ScopedThread::TryReadLock(const Mutex &m) {
+  Event event(Event::MUTEX_TRYREADLOCK, &m);
+  impl_->send(&event);
+  return event.res;
+}
+
+void ScopedThread::ReadUnlock(const Mutex &m) {
+  Event event(Event::MUTEX_READUNLOCK, &m);
+  impl_->send(&event);
+}
+
+void ScopedThread::Memcpy(void *dst, const void *src, int size,
+                          bool expect_race) {
+  Event event(Event::MEMCPY, dst, (uptr)src, size);
+  if (expect_race)
+    event.ExpectReport(ReportTypeRace);
+  impl_->send(&event);
+}
+
+void ScopedThread::Memset(void *dst, int val, int size,
+                          bool expect_race) {
+  Event event(Event::MEMSET, dst, val, size);
+  if (expect_race)
+    event.ExpectReport(ReportTypeRace);
+  impl_->send(&event);
+}
index 92071827d3d871896ef4c1cd008f515d9904ea88..83e25fb5a933615235318e12b04f0036c1747d9c 100644 (file)
@@ -13,6 +13,7 @@
 #include "tsan_clock.h"
 #include "tsan_rtl.h"
 #include "gtest/gtest.h"
+#include <sys/time.h>
 #include <time.h>
 
 namespace __tsan {
@@ -416,9 +417,9 @@ static bool ClockFuzzer(bool printing) {
 }
 
 TEST(Clock, Fuzzer) {
-  timespec ts;
-  clock_gettime(CLOCK_MONOTONIC, &ts);
-  int seed = ts.tv_sec + ts.tv_nsec;
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  int seed = tv.tv_sec + tv.tv_usec;
   printf("seed=%d\n", seed);
   srand(seed);
   if (!ClockFuzzer(false)) {
index bfaefe6487055f0ebac7a39f87edfbb4041fded2..609141c592947f79f96b417358a9170809d338f0 100644 (file)
@@ -141,11 +141,13 @@ TEST(Mman, CallocOverflow) {
   // which is overflown by tsan memory accesses functions in debug mode.
   return;
 #endif
+  ThreadState *thr = cur_thread();
+  uptr pc = 0;
   size_t kArraySize = 4096;
   volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max();
   volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
   volatile void *p = NULL;
-  EXPECT_DEATH(p = calloc(kArraySize, kArraySize2),
+  EXPECT_DEATH(p = user_calloc(thr, pc, kArraySize, kArraySize2),
                "allocator is terminating the process instead of returning 0");
   EXPECT_EQ(0L, p);
 }
index dac479f5141bee96710f42b519675a078a2b20c8..6e086414051ed337883efb7081b22c8e268d6ab8 100644 (file)
 # error "Define UBSAN_CHECK prior to including this file!"
 #endif
 
-// UBSAN_CHECK(Name, SummaryKind, FlagName)
-// SummaryKind and FlagName should be string literals.
+// UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName)
+// SummaryKind and FSanitizeFlagName should be string literals.
 
-UBSAN_CHECK(GenericUB, "undefined-behavior", "-fsanitize=undefined")
-UBSAN_CHECK(NullPointerUse, "null-pointer-use", "-fsanitize=null")
-UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use",
-            "-fsanitize=alignment")
-UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size",
-            "-fsanitize=object-size")
+UBSAN_CHECK(GenericUB, "undefined-behavior", "undefined")
+UBSAN_CHECK(NullPointerUse, "null-pointer-use", "null")
+UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment")
+UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", "object-size")
 UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow",
-            "-fsanitize=signed-integer-overflow")
+            "signed-integer-overflow")
 UBSAN_CHECK(UnsignedIntegerOverflow, "unsigned-integer-overflow",
-            "-fsanitize=unsigned-integer-overflow")
+            "unsigned-integer-overflow")
 UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero",
-            "-fsanitize=integer-divide-by-zero")
-UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero",
-            "-fsanitize=float-divide-by-zero")
-UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "-fsanitize=shift-base")
-UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent",
-            "-fsanitize=shift-exponent")
-UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "-fsanitize=bounds")
-UBSAN_CHECK(UnreachableCall, "unreachable-call", "-fsanitize=unreachable")
-UBSAN_CHECK(MissingReturn, "missing-return", "-fsanitize=return")
-UBSAN_CHECK(NonPositiveVLAIndex, "non-positive-vla-index",
-            "-fsanitize=vla-bound")
-UBSAN_CHECK(FloatCastOverflow, "float-cast-overflow",
-            "-fsanitize=float-cast-overflow")
-UBSAN_CHECK(InvalidBoolLoad, "invalid-bool-load", "-fsanitize=bool")
-UBSAN_CHECK(InvalidEnumLoad, "invalid-enum-load", "-fsanitize=enum")
-UBSAN_CHECK(FunctionTypeMismatch, "function-type-mismatch",
-            "-fsanitize=function")
+            "integer-divide-by-zero")
+UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero", "float-divide-by-zero")
+UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "shift-base")
+UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent", "shift-exponent")
+UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "bounds")
+UBSAN_CHECK(UnreachableCall, "unreachable-call", "unreachable")
+UBSAN_CHECK(MissingReturn, "missing-return", "return")
+UBSAN_CHECK(NonPositiveVLAIndex, "non-positive-vla-index", "vla-bound")
+UBSAN_CHECK(FloatCastOverflow, "float-cast-overflow", "float-cast-overflow")
+UBSAN_CHECK(InvalidBoolLoad, "invalid-bool-load", "bool")
+UBSAN_CHECK(InvalidEnumLoad, "invalid-enum-load", "enum")
+UBSAN_CHECK(FunctionTypeMismatch, "function-type-mismatch", "function")
 UBSAN_CHECK(InvalidNullReturn, "invalid-null-return",
-            "-fsanitize=returns-nonnull-attribute")
-UBSAN_CHECK(InvalidNullArgument, "invalid-null-argument",
-            "-fsanitize=nonnull-attribute")
-UBSAN_CHECK(DynamicTypeMismatch, "dynamic-type-mismatch", "-fsanitize=vptr")
-UBSAN_CHECK(CFIBadType, "cfi-bad-type", "-fsanitize=cfi")
+            "returns-nonnull-attribute")
+UBSAN_CHECK(InvalidNullArgument, "invalid-null-argument", "nonnull-attribute")
+UBSAN_CHECK(DynamicTypeMismatch, "dynamic-type-mismatch", "vptr")
+UBSAN_CHECK(CFIBadType, "cfi-bad-type", "cfi")
index 17e973cf6ea20bf5e9f9b0b2a1e47c230871b669..2476947dc914899d03f0918639f10340108a8b6c 100644 (file)
@@ -45,7 +45,7 @@ static void MaybePrintStackTrace(uptr pc, uptr bp) {
 
 static const char *ConvertTypeToString(ErrorType Type) {
   switch (Type) {
-#define UBSAN_CHECK(Name, SummaryKind, FlagName)                               \
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName)                      \
   case ErrorType::Name:                                                        \
     return SummaryKind;
 #include "ubsan_checks.inc"
@@ -54,6 +54,17 @@ static const char *ConvertTypeToString(ErrorType Type) {
   UNREACHABLE("unknown ErrorType!");
 }
 
+static const char *ConvertTypeToFlagName(ErrorType Type) {
+  switch (Type) {
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName)                      \
+  case ErrorType::Name:                                                        \
+    return FSanitizeFlagName;
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+  }
+  UNREACHABLE("unknown ErrorType!");
+}
+
 static void MaybeReportErrorSummary(Location Loc, ErrorType Type) {
   if (!common_flags()->print_summary)
     return;
@@ -365,14 +376,19 @@ ScopedReport::~ScopedReport() {
   MaybePrintStackTrace(Opts.pc, Opts.bp);
   MaybeReportErrorSummary(SummaryLoc, Type);
   CommonSanitizerReportMutex.Unlock();
-  if (Opts.DieAfterReport || flags()->halt_on_error)
+  if (flags()->halt_on_error)
     Die();
 }
 
 ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
 static SuppressionContext *suppression_ctx = nullptr;
 static const char kVptrCheck[] = "vptr_check";
-static const char *kSuppressionTypes[] = { kVptrCheck };
+static const char *kSuppressionTypes[] = {
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+    kVptrCheck,
+};
 
 void __ubsan::InitializeSuppressions() {
   CHECK_EQ(nullptr, suppression_ctx);
@@ -388,4 +404,28 @@ bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
   return suppression_ctx->Match(TypeName, kVptrCheck, &s);
 }
 
+bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
+  InitAsStandaloneIfNecessary();
+  CHECK(suppression_ctx);
+  const char *SuppType = ConvertTypeToFlagName(ET);
+  // Fast path: don't symbolize PC if there is no suppressions for given UB
+  // type.
+  if (!suppression_ctx->HasSuppressionType(SuppType))
+    return false;
+  Suppression *s = nullptr;
+  // Suppress by file name known to runtime.
+  if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s))
+    return true;
+  // Suppress by module name.
+  if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) {
+    if (suppression_ctx->Match(Module, SuppType, &s))
+      return true;
+  }
+  // Suppress by function or source file name from debug info.
+  SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC));
+  const AddressInfo &AI = Stack.get()->info;
+  return suppression_ctx->Match(AI.function, SuppType, &s) ||
+         suppression_ctx->Match(AI.file, SuppType, &s);
+}
+
 #endif  // CAN_SANITIZE_UB
index 2aa62eb743105005f3964fa01d9451e2139fe9f3..3edb67a03c1f4ff7c732e85dadc3a54ecb9f565f 100644 (file)
@@ -211,23 +211,25 @@ public:
 };
 
 struct ReportOptions {
-  /// If DieAfterReport is specified, UBSan will terminate the program after the
-  /// report is printed.
-  bool DieAfterReport;
+  // If FromUnrecoverableHandler is specified, UBSan runtime handler is not
+  // expected to return.
+  bool FromUnrecoverableHandler;
   /// pc/bp are used to unwind the stack trace.
   uptr pc;
   uptr bp;
 };
 
 enum class ErrorType {
-#define UBSAN_CHECK(Name, SummaryKind, FlagName) Name,
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) Name,
 #include "ubsan_checks.inc"
 #undef UBSAN_CHECK
 };
 
-#define GET_REPORT_OPTIONS(die_after_report) \
+bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET);
+
+#define GET_REPORT_OPTIONS(unrecoverable_handler) \
     GET_CALLER_PC_BP; \
-    ReportOptions Opts = {die_after_report, pc, bp}
+    ReportOptions Opts = {unrecoverable_handler, pc, bp}
 
 /// \brief Instantiate this class before printing diagnostics in the error
 /// report. This class ensures that reports from different threads and from
@@ -238,14 +240,15 @@ class ScopedReport {
   ErrorType Type;
 
 public:
-  ScopedReport(ReportOptions Opts, Location SummaryLoc,
-               ErrorType Type = ErrorType::GenericUB);
-  void setErrorType(ErrorType T) { Type = T; }
+  ScopedReport(ReportOptions Opts, Location SummaryLoc, ErrorType Type);
   ~ScopedReport();
 };
 
 void InitializeSuppressions();
 bool IsVptrCheckSuppressed(const char *TypeName);
+// Sometimes UBSan runtime can know filename from handlers arguments, even if
+// debug info is missing.
+bool IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename);
 
 } // namespace __ubsan
 
index ec6b54e437b681d79a6d9001e129daf0e733353a..5d82e9afcd09ef96de5101df31c650174b0ac3ae 100644 (file)
 using namespace __sanitizer;
 using namespace __ubsan;
 
-static bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) {
-  // If source location is already acquired, we don't need to print an error
-  // report for the second time. However, if we're in an unrecoverable handler,
-  // it's possible that location was required by concurrently running thread.
-  // In this case, we should continue the execution to ensure that any of
-  // threads will grab the report mutex and print the report before
-  // crashing the program.
-  return SLoc.isDisabled() && !Opts.DieAfterReport;
+namespace __ubsan {
+bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) {
+  // We are not allowed to skip error report: if we are in unrecoverable
+  // handler, we have to terminate the program right now, and therefore
+  // have to print some diagnostic.
+  //
+  // Even if source location is disabled, it doesn't mean that we have
+  // already report an error to the user: some concurrently running
+  // thread could have acquired it, but not yet printed the report.
+  if (Opts.FromUnrecoverableHandler)
+    return false;
+  return SLoc.isDisabled() || IsPCSuppressed(ET, Opts.pc, SLoc.getFilename());
 }
 
-namespace __ubsan {
 const char *TypeCheckKinds[] = {
     "load of", "store to", "reference binding to", "member access within",
     "member call on", "constructor call on", "downcast of", "downcast of",
@@ -41,8 +44,18 @@ const char *TypeCheckKinds[] = {
 static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
                                    ReportOptions Opts) {
   Location Loc = Data->Loc.acquire();
-  // Use the SourceLocation from Data to track deduplication, even if 'invalid'
-  if (ignoreReport(Loc.getSourceLocation(), Opts))
+
+  ErrorType ET;
+  if (!Pointer)
+    ET = ErrorType::NullPointerUse;
+  else if (Data->Alignment && (Pointer & (Data->Alignment - 1)))
+    ET = ErrorType::MisalignedPointerUse;
+  else
+    ET = ErrorType::InsufficientObjectSize;
+
+  // Use the SourceLocation from Data to track deduplication, even if it's
+  // invalid.
+  if (ignoreReport(Loc.getSourceLocation(), Opts, ET))
     return;
 
   SymbolizedStackHolder FallbackLoc;
@@ -51,24 +64,28 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
     Loc = FallbackLoc;
   }
 
-  ScopedReport R(Opts, Loc);
+  ScopedReport R(Opts, Loc, ET);
 
-  if (!Pointer) {
-    R.setErrorType(ErrorType::NullPointerUse);
+  switch (ET) {
+  case ErrorType::NullPointerUse:
     Diag(Loc, DL_Error, "%0 null pointer of type %1")
-      << TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
-  } else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) {
-    R.setErrorType(ErrorType::MisalignedPointerUse);
+        << TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
+    break;
+  case ErrorType::MisalignedPointerUse:
     Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, "
                         "which requires %2 byte alignment")
-      << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer
-      << Data->Alignment << Data->Type;
-  } else {
-    R.setErrorType(ErrorType::InsufficientObjectSize);
+        << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer
+        << Data->Alignment << Data->Type;
+    break;
+  case ErrorType::InsufficientObjectSize:
     Diag(Loc, DL_Error, "%0 address %1 with insufficient space "
                         "for an object of type %2")
-      << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
+        << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type;
+    break;
+  default:
+    UNREACHABLE("unexpected error type!");
   }
+
   if (Pointer)
     Diag(Pointer, DL_Note, "pointer points here");
 }
@@ -91,12 +108,14 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
                                       const char *Operator, T RHS,
                                       ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  bool IsSigned = Data->Type.isSignedIntegerTy();
+  ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
+                          : ErrorType::UnsignedIntegerOverflow;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  bool IsSigned = Data->Type.isSignedIntegerTy();
-  ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
-                                     : ErrorType::UnsignedIntegerOverflow);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "%0 integer overflow: "
                       "%1 %2 %3 cannot be represented in type %4")
@@ -104,12 +123,13 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
     << Value(Data->Type, LHS) << Operator << RHS << Data->Type;
 }
 
-#define UBSAN_OVERFLOW_HANDLER(handler_name, op, abort)                        \
+#define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable)                \
   void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS,              \
                              ValueHandle RHS) {                                \
-    GET_REPORT_OPTIONS(abort);                                                 \
+    GET_REPORT_OPTIONS(unrecoverable);                                         \
     handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts);    \
-    if (abort) Die();                                                          \
+    if (unrecoverable)                                                         \
+      Die();                                                                   \
   }
 
 UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow, "+", false)
@@ -122,12 +142,14 @@ UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*", true)
 static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
                                      ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  bool IsSigned = Data->Type.isSignedIntegerTy();
+  ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
+                          : ErrorType::UnsignedIntegerOverflow;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  bool IsSigned = Data->Type.isSignedIntegerTy();
-  ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
-                                     : ErrorType::UnsignedIntegerOverflow);
+  ScopedReport R(Opts, Loc, ET);
 
   if (IsSigned)
     Diag(Loc, DL_Error,
@@ -154,22 +176,30 @@ void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data,
 static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
                                      ValueHandle RHS, ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  Value LHSVal(Data->Type, LHS);
+  Value RHSVal(Data->Type, RHS);
+
+  ErrorType ET;
+  if (RHSVal.isMinusOne())
+    ET = ErrorType::SignedIntegerOverflow;
+  else if (Data->Type.isIntegerTy())
+    ET = ErrorType::IntegerDivideByZero;
+  else
+    ET = ErrorType::FloatDivideByZero;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc);
+  ScopedReport R(Opts, Loc, ET);
 
-  Value LHSVal(Data->Type, LHS);
-  Value RHSVal(Data->Type, RHS);
-  if (RHSVal.isMinusOne()) {
-    R.setErrorType(ErrorType::SignedIntegerOverflow);
-    Diag(Loc, DL_Error,
-         "division of %0 by -1 cannot be represented in type %1")
-      << LHSVal << Data->Type;
-  } else {
-    R.setErrorType(Data->Type.isIntegerTy() ? ErrorType::IntegerDivideByZero
-                                            : ErrorType::FloatDivideByZero);
+  switch (ET) {
+  case ErrorType::SignedIntegerOverflow:
+    Diag(Loc, DL_Error, "division of %0 by -1 cannot be represented in type %1")
+        << LHSVal << Data->Type;
+    break;
+  default:
     Diag(Loc, DL_Error, "division by zero");
+    break;
   }
 }
 
@@ -190,29 +220,34 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
                                        ValueHandle LHS, ValueHandle RHS,
                                        ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  Value LHSVal(Data->LHSType, LHS);
+  Value RHSVal(Data->RHSType, RHS);
+
+  ErrorType ET;
+  if (RHSVal.isNegative() ||
+      RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth())
+    ET = ErrorType::InvalidShiftExponent;
+  else
+    ET = ErrorType::InvalidShiftBase;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc);
+  ScopedReport R(Opts, Loc, ET);
 
-  Value LHSVal(Data->LHSType, LHS);
-  Value RHSVal(Data->RHSType, RHS);
-  if (RHSVal.isNegative()) {
-    R.setErrorType(ErrorType::InvalidShiftExponent);
-    Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal;
-  } else if (RHSVal.getPositiveIntValue() >=
-             Data->LHSType.getIntegerBitWidth()) {
-    R.setErrorType(ErrorType::InvalidShiftExponent);
-    Diag(Loc, DL_Error, "shift exponent %0 is too large for %1-bit type %2")
-        << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
-  } else if (LHSVal.isNegative()) {
-    R.setErrorType(ErrorType::InvalidShiftBase);
-    Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal;
+  if (ET == ErrorType::InvalidShiftExponent) {
+    if (RHSVal.isNegative())
+      Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal;
+    else
+      Diag(Loc, DL_Error, "shift exponent %0 is too large for %1-bit type %2")
+          << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
   } else {
-    R.setErrorType(ErrorType::InvalidShiftBase);
-    Diag(Loc, DL_Error,
-         "left shift of %0 by %1 places cannot be represented in type %2")
-        << LHSVal << RHSVal << Data->LHSType;
+    if (LHSVal.isNegative())
+      Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal;
+    else
+      Diag(Loc, DL_Error,
+           "left shift of %0 by %1 places cannot be represented in type %2")
+          << LHSVal << RHSVal << Data->LHSType;
   }
 }
 
@@ -234,10 +269,12 @@ void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
 static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
                                   ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::OutOfBoundsIndex;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc, ErrorType::OutOfBoundsIndex);
+  ScopedReport R(Opts, Loc, ET);
 
   Value IndexVal(Data->IndexType, Index);
   Diag(Loc, DL_Error, "index %0 out of bounds for type %1")
@@ -284,10 +321,12 @@ void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
 static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
                                       ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::NonPositiveVLAIndex;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc, ErrorType::NonPositiveVLAIndex);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "variable length array bound evaluates to "
                       "non-positive value %0")
@@ -329,6 +368,7 @@ static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
   SymbolizedStackHolder CallerLoc;
   Location Loc;
   const TypeDescriptor *FromType, *ToType;
+  ErrorType ET = ErrorType::FloatCastOverflow;
 
   if (looksLikeFloatCastOverflowDataV1(DataPtr)) {
     auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr);
@@ -339,14 +379,14 @@ static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
   } else {
     auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr);
     SourceLocation SLoc = Data->Loc.acquire();
-    if (ignoreReport(SLoc, Opts))
+    if (ignoreReport(SLoc, Opts, ET))
       return;
     Loc = SLoc;
     FromType = &Data->FromType;
     ToType = &Data->ToType;
   }
 
-  ScopedReport R(Opts, Loc, ErrorType::FloatCastOverflow);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error,
        "value %0 is outside the range of representable values of type %2")
@@ -367,14 +407,16 @@ void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data,
 static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
                                    ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
-    return;
-
   // This check could be more precise if we used different handlers for
   // -fsanitize=bool and -fsanitize=enum.
   bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'"));
-  ScopedReport R(Opts, Loc, IsBool ? ErrorType::InvalidBoolLoad
-                                   : ErrorType::InvalidEnumLoad);
+  ErrorType ET =
+      IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad;
+
+  if (ignoreReport(Loc, Opts, ET))
+    return;
+
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error,
        "load of value %0, which is not a valid value for type %1")
@@ -397,10 +439,12 @@ static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
                                        ValueHandle Function,
                                        ReportOptions Opts) {
   SourceLocation CallLoc = Data->Loc.acquire();
-  if (ignoreReport(CallLoc, Opts))
+  ErrorType ET = ErrorType::FunctionTypeMismatch;
+
+  if (ignoreReport(CallLoc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, CallLoc, ErrorType::FunctionTypeMismatch);
+  ScopedReport R(Opts, CallLoc, ET);
 
   SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
   const char *FName = FLoc.get()->info.function;
@@ -429,10 +473,12 @@ void __ubsan::__ubsan_handle_function_type_mismatch_abort(
 
 static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::InvalidNullReturn;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc, ErrorType::InvalidNullReturn);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "null pointer returned from function declared to never "
                       "return null");
@@ -453,10 +499,12 @@ void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) {
 
 static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::InvalidNullArgument;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc, ErrorType::InvalidNullArgument);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to "
        "never be null") << Data->ArgIndex;
@@ -478,10 +526,12 @@ void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
 static void handleCFIBadIcall(CFIBadIcallData *Data, ValueHandle Function,
                               ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::CFIBadType;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
                       "indirect function call")
index 674e406c5858640d97c2ec538759a51ebb8d8122..3e81be67163b703ddd9c58bf02faee4754ed86e6 100644 (file)
@@ -29,23 +29,25 @@ namespace __ubsan {
   extern const char *TypeCheckKinds[];
 }
 
-static void HandleDynamicTypeCacheMiss(
+// Returns true if UBSan has printed an error report.
+static bool HandleDynamicTypeCacheMiss(
     DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash,
     ReportOptions Opts) {
   if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash))
     // Just a cache miss. The type matches after all.
-    return;
+    return false;
 
   // Check if error report should be suppressed.
   DynamicTypeInfo DTI = getDynamicTypeInfoFromObject((void*)Pointer);
   if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName()))
-    return;
+    return false;
 
   SourceLocation Loc = Data->Loc.acquire();
-  if (Loc.isDisabled())
-    return;
+  ErrorType ET = ErrorType::DynamicTypeMismatch;
+  if (ignoreReport(Loc, Opts, ET))
+    return false;
 
-  ScopedReport R(Opts, Loc, ErrorType::DynamicTypeMismatch);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error,
        "%0 address %1 which does not point to an object of type %2")
@@ -69,6 +71,7 @@ static void HandleDynamicTypeCacheMiss(
         << TypeName(DTI.getSubobjectTypeName())
         << Range(Pointer, Pointer + sizeof(uptr),
                  "vptr for %2 base class of %1");
+  return true;
 }
 
 void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
@@ -78,14 +81,21 @@ void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
 }
 void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
     DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
-  GET_REPORT_OPTIONS(true);
-  HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts);
+  // Note: -fsanitize=vptr is always recoverable.
+  GET_REPORT_OPTIONS(false);
+  if (HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts))
+    Die();
 }
 
 static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable,
                              ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  ScopedReport R(Opts, Loc, ErrorType::CFIBadType);
+  ErrorType ET = ErrorType::CFIBadType;
+
+  if (ignoreReport(Loc, Opts, ET))
+    return;
+
+  ScopedReport R(Opts, Loc, ET);
   DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable);
 
   static const char *TypeCheckKinds[] = {
@@ -117,6 +127,7 @@ void __ubsan::__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data,
                                                 ValueHandle Vtable) {
   GET_REPORT_OPTIONS(true);
   HandleCFIBadType(Data, Vtable, Opts);
+  Die();
 }
 
 #endif  // CAN_SANITIZE_UB
index 0998aca0de37292064f4771874a09bad52b00547..094fd160f9d0c8dabe2389133272e7b166f03309 100644 (file)
@@ -44,6 +44,6 @@ endif
 ###
 # Common compiler options
 COMMON_INCLUDES=-I${ProjSrcRoot}/lib -I${ProjSrcRoot}/include
-COMMON_CXXFLAGS=-std=c++11 -fno-exceptions -funwind-tables $(COMMON_INCLUDES)
-COMMON_CFLAGS=$(COMMON_INCLUDES)
+COMMON_CXXFLAGS=-std=c++11 -fno-exceptions -fPIC -funwind-tables $(COMMON_INCLUDES)
+COMMON_CFLAGS=-fPIC $(COMMON_INCLUDES)
 COMMON_ASMFLAGS=$(COMMON_INCLUDES)
diff --git a/src/compiler-rt/make/platform/clang_darwin.mk b/src/compiler-rt/make/platform/clang_darwin.mk
new file mode 100644 (file)
index 0000000..9944481
--- /dev/null
@@ -0,0 +1,567 @@
+# These are the functions which clang needs when it is targeting a previous
+# version of the OS. The issue is that the backend may use functions which were
+# not present in the libgcc that shipped on the platform. In such cases, we link
+# with a version of the library which contains private_extern definitions of all
+# the extra functions which might be referenced.
+
+Description := Static runtime libraries for clang/Darwin.
+
+# A function that ensures we don't try to build for architectures and SDKs
+# that we don't have working toolchains for. Arguments:
+# (1): List of architectures
+# (2): Library name
+# (3): SDK path
+# The result is a possibly empty subset of the architectures from argument 1.
+CheckArches = \
+  $(shell \
+    result=""; \
+    if [ "X$(3)" != X ]; then \
+      for arch in $(1); do \
+        if $(LD) -v 2>&1 | grep "configured to support" \
+             | tr ' ' '\n' | grep "^$$arch$$" >/dev/null 2>/dev/null; then \
+          if $(CC) -arch $$arch \
+            -integrated-as \
+            $(ProjSrcRoot)/make/platform/clang_darwin_test_input.c \
+            -isysroot $(3) \
+            -o /dev/null > /dev/null 2> /dev/null; then \
+              result="$$result$$arch "; \
+          else \
+            printf 1>&2 \
+             "warning: clang_darwin.mk: dropping arch '$$arch' from lib '$(2)'"; \
+            printf 1>&2 " (clang or system libraries do not support it)\n"; \
+          fi; \
+        else \
+          printf 1>&2 \
+            "warning: clang_darwin.mk: dropping arch '$$arch' from lib '$(2)'";\
+          printf 1>&2 " (ld does not support it)\n"; \
+        fi; \
+      done; \
+    fi; \
+    echo $$result)
+
+XCRun = \
+  $(shell \
+    result=`xcrun -find $(1) 2> /dev/null`; \
+    if [ "$$?" != "0" ]; then result=$(1); fi; \
+    echo $$result)
+# Prefer building with the internal SDKs.
+XCRunSdkPath = \
+  $(shell \
+    result=`xcrun --sdk $(1).internal --show-sdk-path 2> /dev/null`; \
+    if [ "$$?" != "0" ]; then \
+      result=`xcrun --sdk $(1) --show-sdk-path 2> /dev/null`; \
+      if [ "$$?" != "0" ]; then result=""; fi; \
+    fi; \
+    echo $$result)
+###
+
+CC       := $(call XCRun,clang)
+LD       := $(shell $(CC) -print-prog-name=ld)
+AR       := $(call XCRun,ar)
+RANLIB   := $(call XCRun,ranlib)
+STRIP    := $(call XCRun,strip)
+LIPO     := $(call XCRun,lipo)
+DSYMUTIL := $(call XCRun,dsymutil)
+
+OSX_SDK := $(call XCRunSdkPath,macosx)
+IOS_SDK := $(call XCRunSdkPath,iphoneos)
+IOSSIM_SDK := $(call XCRunSdkPath,iphonesimulator)
+
+Configs :=
+UniversalArchs :=
+
+# Configuration solely for providing access to an eprintf symbol, which may
+# still be referenced from Darwin system headers. This symbol is only ever
+# needed on i386.
+Configs += eprintf
+UniversalArchs.eprintf := $(call CheckArches,i386,eprintf,$(OSX_SDK))
+
+# Configuration for targeting 10.4. We need a few functions missing from
+# libgcc_s.10.4.dylib. We only build x86 slices since clang doesn't really
+# support targeting PowerPC.
+Configs += 10.4
+UniversalArchs.10.4 := $(call CheckArches,i386 x86_64,10.4,$(OSX_SDK))
+
+# Configuration for targeting iOS for a couple of functions that didn't
+# make it into libSystem.
+Configs += ios
+UniversalArchs.ios := $(call CheckArches,i386 x86_64,ios,$(IOSSIM_SDK))
+UniversalArchs.ios += $(call CheckArches,armv7 arm64,ios,$(IOS_SDK))
+
+# Configuration for targeting OSX. These functions may not be in libSystem
+# so we should provide our own.
+Configs += osx
+UniversalArchs.osx := $(call CheckArches,i386 x86_64 x86_64h,osx,$(OSX_SDK))
+
+# Configuration for use with kernel/kexts.
+Configs += cc_kext
+UniversalArchs.cc_kext := $(call CheckArches,i386 x86_64 x86_64h,cc_kext,$(OSX_SDK))
+
+# Configuration for use with iOS kernel/kexts
+Configs += cc_kext_ios
+UniversalArchs.cc_kext_ios += $(call CheckArches,armv7,cc_kext_ios,$(IOS_SDK))
+
+# Configurations which define the profiling support functions.
+Configs += profile_osx
+UniversalArchs.profile_osx := $(call CheckArches,i386 x86_64 x86_64h,profile_osx,$(OSX_SDK))
+Configs += profile_ios
+UniversalArchs.profile_ios := $(call CheckArches,i386 x86_64,profile_ios,$(IOSSIM_SDK))
+UniversalArchs.profile_ios += $(call CheckArches,armv7 arm64,profile_ios,$(IOS_SDK))
+
+# Configurations which define the ASAN support functions.
+Configs += asan_osx_dynamic
+UniversalArchs.asan_osx_dynamic := $(call CheckArches,i386 x86_64 x86_64h,asan_osx_dynamic,$(OSX_SDK))
+
+Configs += asan_iossim_dynamic
+UniversalArchs.asan_iossim_dynamic := $(call CheckArches,i386 x86_64,asan_iossim_dynamic,$(IOSSIM_SDK))
+
+Configs += ubsan_osx_dynamic
+UniversalArchs.ubsan_osx_dynamic := $(call CheckArches,i386 x86_64 x86_64h,ubsan_osx_dynamic,$(OSX_SDK))
+
+Configs += ubsan_iossim_dynamic
+UniversalArchs.ubsan_iossim_dynamic := $(call CheckArches,i386 x86_64,ubsan_iossim_dynamic,$(IOSSIM_SDK))
+
+# Darwin 10.6 has a bug in cctools that makes it unable to use ranlib on our ARM
+# object files. If we are on that platform, strip out all ARM archs. We still
+# build the libraries themselves so that Clang can find them where it expects
+# them, even though they might not have an expected slice.
+ifneq ($(shell test -x /usr/bin/sw_vers && sw_vers -productVersion | grep 10.6),)
+UniversalArchs.ios := $(filter-out armv7, $(UniversalArchs.ios))
+UniversalArchs.cc_kext_ios := $(filter-out armv7, $(UniversalArchs.cc_kext_ios))
+UniversalArchs.profile_ios := $(filter-out armv7, $(UniversalArchs.profile_ios))
+endif
+
+# If RC_SUPPORTED_ARCHS is defined, treat it as a list of the architectures we
+# are intended to support and limit what we try to build to that.
+ifneq ($(RC_SUPPORTED_ARCHS),)
+$(foreach config,$(Configs),\
+  $(call Set,UniversalArchs.$(config),\
+       $(filter $(RC_SUPPORTED_ARCHS),$(UniversalArchs.$(config)))))
+endif
+
+# Remove empty configs if we end up dropping all the requested
+# archs for a particular config.
+$(foreach config,$(Configs),\
+  $(if $(strip $(UniversalArchs.$(config))),,\
+       $(call Set,Configs,$(filter-out $(config),$(Configs)))))
+
+###
+
+# Forcibly strip off any -arch, as that totally breaks our universal support.
+override CC := $(subst -arch ,-arch_,$(CC))
+override CC := $(patsubst -arch_%,,$(CC))
+
+CFLAGS := -Wall -Werror -O3 -fomit-frame-pointer
+
+# Always set deployment target arguments for every build, these libraries should
+# never depend on the environmental overrides. We simply set them to minimum
+# supported deployment target -- nothing in the compiler-rt libraries should
+# actually depend on the deployment target.
+OSX_DEPLOYMENT_ARGS := -mmacosx-version-min=10.4
+IOS_DEPLOYMENT_ARGS := -mios-version-min=1.0
+IOS6_DEPLOYMENT_ARGS := -mios-version-min=6.0
+IOSSIM_DEPLOYMENT_ARGS := -mios-simulator-version-min=1.0
+
+OSX_DEPLOYMENT_ARGS += -isysroot $(OSX_SDK)
+IOS_DEPLOYMENT_ARGS += -isysroot $(IOS_SDK)
+IOS6_DEPLOYMENT_ARGS += -isysroot $(IOS_SDK)
+IOSSIM_DEPLOYMENT_ARGS += -isysroot $(IOSSIM_SDK)
+
+CFLAGS.eprintf         := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.10.4            := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+
+SANITIZER_MACOSX_DEPLOYMENT_ARGS := -mmacosx-version-min=10.7
+SANITIZER_IOSSIM_DEPLOYMENT_ARGS := -mios-simulator-version-min=7.0 \
+  -isysroot $(IOSSIM_SDK)
+SANITIZER_CFLAGS := -fno-builtin -gline-tables-only -stdlib=libc++
+
+CFLAGS.asan_osx_dynamic := \
+       $(CFLAGS) $(SANITIZER_MACOSX_DEPLOYMENT_ARGS) \
+       $(SANITIZER_CFLAGS) \
+       -DMAC_INTERPOSE_FUNCTIONS=1 \
+       -DASAN_DYNAMIC=1
+
+CFLAGS.asan_iossim_dynamic := \
+       $(CFLAGS) $(SANITIZER_IOSSIM_DEPLOYMENT_ARGS) \
+       $(SANITIZER_CFLAGS) \
+       -DMAC_INTERPOSE_FUNCTIONS=1 \
+       -DASAN_DYNAMIC=1
+
+CFLAGS.ubsan_osx_dynamic := \
+       $(CFLAGS) $(SANITIZER_MACOSX_DEPLOYMENT_ARGS) \
+       $(SANITIZER_CFLAGS)
+
+CFLAGS.ubsan_iossim_dynamic := \
+       $(CFLAGS) $(SANITIZER_IOSSIM_DEPLOYMENT_ARGS) \
+       $(SANITIZER_CFLAGS)
+
+
+CFLAGS.ios.i386                := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
+CFLAGS.ios.x86_64      := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
+CFLAGS.ios.armv7       := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.ios.armv7k      := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.ios.armv7s      := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.ios.arm64       := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+CFLAGS.osx.i386                := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.osx.x86_64      := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.osx.x86_64h     := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext.i386    := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext.x86_64  := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext.x86_64h := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext_ios.armv7       := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext_ios.armv7k      := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext_ios.armv7s      := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext_ios.arm64       := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+CFLAGS.profile_osx.i386    := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.profile_osx.x86_64  := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.profile_osx.x86_64h := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.i386    := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.x86_64  := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.armv7  := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.arm64  := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+
+SANITIZER_LDFLAGS := -stdlib=libc++ -lc++ -lc++abi
+
+SHARED_LIBRARY.asan_osx_dynamic := 1
+LDFLAGS.asan_osx_dynamic := $(SANITIZER_LDFLAGS) -install_name @rpath/libclang_rt.asan_osx_dynamic.dylib \
+  $(SANITIZER_MACOSX_DEPLOYMENT_ARGS)
+
+SHARED_LIBRARY.asan_iossim_dynamic := 1
+LDFLAGS.asan_iossim_dynamic := $(SANITIZER_LDFLAGS) -install_name @rpath/libclang_rt.asan_iossim_dynamic.dylib \
+  -Wl,-ios_simulator_version_min,7.0.0 $(SANITIZER_IOSSIM_DEPLOYMENT_ARGS)
+
+SHARED_LIBRARY.ubsan_osx_dynamic := 1
+LDFLAGS.ubsan_osx_dynamic := $(SANITIZER_LDFLAGS) -install_name @rpath/libclang_rt.ubsan_osx_dynamic.dylib \
+  $(SANITIZER_MACOSX_DEPLOYMENT_ARGS)
+
+SHARED_LIBRARY.ubsan_iossim_dynamic := 1
+LDFLAGS.ubsan_iossim_dynamic := $(SANITIZER_LDFLAGS) -install_name @rpath/libclang_rt.ubsan_iossim_dynamic.dylib \
+  -Wl,-ios_simulator_version_min,7.0.0 $(SANITIZER_IOSSIM_DEPLOYMENT_ARGS)
+
+ifneq ($(OSX_SDK),)
+CFLAGS.asan_osx_dynamic += -isysroot $(OSX_SDK)
+LDFLAGS.asan_osx_dynamic += -isysroot $(OSX_SDK)
+CFLAGS.ubsan_osx_dynamic += -isysroot $(OSX_SDK)
+LDFLAGS.ubsan_osx_dynamic += -isysroot $(OSX_SDK)
+endif
+
+ATOMIC_FUNCTIONS := \
+       atomic_flag_clear \
+       atomic_flag_clear_explicit \
+       atomic_flag_test_and_set \
+       atomic_flag_test_and_set_explicit \
+       atomic_signal_fence \
+       atomic_thread_fence
+
+FP16_FUNCTIONS := \
+       extendhfsf2 \
+       truncdfhf2 \
+       truncsfhf2
+
+FUNCTIONS.eprintf := eprintf
+FUNCTIONS.10.4 := eprintf floatundidf floatundisf floatundixf
+
+FUNCTIONS.ios      := divmodsi4 udivmodsi4 mulosi4 mulodi4 muloti4 \
+                       $(ATOMIC_FUNCTIONS) $(FP16_FUNCTIONS)
+# On x86, the divmod functions reference divsi.
+FUNCTIONS.ios.i386    := $(FUNCTIONS.ios) \
+                         divsi3 udivsi3
+FUNCTIONS.ios.x86_64  := $(FUNCTIONS.ios.i386)
+FUNCTIONS.ios.arm64   := mulsc3 muldc3 divsc3 divdc3 udivti3 umodti3 \
+                         $(ATOMIC_FUNCTIONS)
+
+FUNCTIONS.osx  := mulosi4 mulodi4 muloti4 $(ATOMIC_FUNCTIONS) $(FP16_FUNCTIONS)
+
+FUNCTIONS.profile_osx := GCDAProfiling InstrProfiling InstrProfilingBuffer \
+                         InstrProfilingFile InstrProfilingPlatformDarwin \
+                         InstrProfilingRuntime InstrProfilingUtil \
+                         InstrProfilingWriter InstrProfilingValue
+FUNCTIONS.profile_ios := $(FUNCTIONS.profile_osx)
+
+FUNCTIONS.asan_osx_dynamic := $(AsanFunctions) $(AsanCXXFunctions) \
+                              $(InterceptionFunctions) \
+                              $(SanitizerCommonFunctions) \
+                              $(AsanDynamicFunctions) \
+                              $(UbsanFunctions) $(UbsanCXXFunctions)
+
+FUNCTIONS.asan_iossim_dynamic := $(AsanFunctions) $(AsanCXXFunctions) \
+                                 $(InterceptionFunctions) \
+                                 $(SanitizerCommonFunctions) \
+                                 $(AsanDynamicFunctions) \
+                                 $(UbsanFunctions) $(UbsanCXXFunctions)
+
+FUNCTIONS.ubsan_osx_dynamic := $(UbsanFunctions) $(UbsanCXXFunctions) \
+                               $(SanitizerCommonFunctions) \
+                               $(UbsanStandaloneFunctions)
+
+FUNCTIONS.ubsan_iossim_dynamic := $(UbsanFunctions) $(UbsanCXXFunctions) \
+                                  $(SanitizerCommonFunctions) \
+                                  $(UbsanStandaloneFunctions)
+
+CCKEXT_PROFILE_FUNCTIONS := \
+       InstrProfiling \
+       InstrProfilingBuffer \
+       InstrProfilingPlatformDarwin
+
+CCKEXT_COMMON_FUNCTIONS := \
+       $(CCKEXT_PROFILE_FUNCTIONS) \
+       absvdi2 \
+       absvsi2 \
+       addvdi3 \
+       addvsi3 \
+       ashldi3 \
+       ashrdi3 \
+       bswapdi2 \
+       bswapsi2 \
+       clzdi2 \
+       clzsi2 \
+       cmpdi2 \
+       ctzdi2 \
+       ctzsi2 \
+       divdc3 \
+       divdi3 \
+       divsc3 \
+       divmodsi4 \
+       udivmodsi4 \
+       do_global_dtors \
+       eprintf \
+       extendhfsf2 \
+       ffsdi2 \
+       fixdfdi \
+       fixsfdi \
+       fixunsdfdi \
+       fixunsdfsi \
+       fixunssfdi \
+       fixunssfsi \
+       floatdidf \
+       floatdisf \
+       floatundidf \
+       floatundisf \
+       gcc_bcmp \
+       lshrdi3 \
+       moddi3 \
+       muldc3 \
+       muldi3 \
+       mulsc3 \
+       mulvdi3 \
+       mulvsi3 \
+       negdi2 \
+       negvdi2 \
+       negvsi2 \
+       paritydi2 \
+       paritysi2 \
+       popcountdi2 \
+       popcountsi2 \
+       powidf2 \
+       powisf2 \
+       subvdi3 \
+       subvsi3 \
+       truncdfhf2 \
+       truncsfhf2 \
+       ucmpdi2 \
+       udiv_w_sdiv \
+       udivdi3 \
+       udivmoddi4 \
+       umoddi3
+
+CCKEXT_ARM_FUNCTIONS := $(CCKEXT_COMMON_FUNCTIONS) \
+       adddf3 \
+       addsf3 \
+       aeabi_cdcmpeq \
+       aeabi_cdrcmple \
+       aeabi_cfcmpeq \
+       aeabi_cfrcmple \
+       aeabi_dcmpeq \
+       aeabi_dcmpge \
+       aeabi_dcmpgt \
+       aeabi_dcmple \
+       aeabi_dcmplt \
+       aeabi_drsub \
+       aeabi_fcmpeq \
+       aeabi_fcmpge \
+       aeabi_fcmpgt \
+       aeabi_fcmple \
+       aeabi_fcmplt \
+       aeabi_frsub \
+       aeabi_idivmod \
+       aeabi_uidivmod \
+       cmpdf2 \
+       cmpsf2 \
+       div0 \
+       divdf3 \
+       divsf3 \
+       divsi3 \
+       extendsfdf2 \
+       ffssi2 \
+       fixdfsi \
+       fixsfsi \
+       floatsidf \
+       floatsisf \
+       floatunsidf \
+       floatunsisf \
+       comparedf2 \
+       comparesf2 \
+       modsi3 \
+       muldf3 \
+       mulsf3 \
+       mulodi4 \
+       negdf2 \
+       negsf2 \
+       subdf3 \
+       subsf3 \
+       switch16 \
+       switch32 \
+       switch8 \
+       switchu8 \
+       truncdfsf2 \
+       udivsi3 \
+       umodsi3 \
+       unorddf2 \
+       unordsf2
+
+CCKEXT_ARMVFP_FUNCTIONS := $(CCKEXT_ARM_FUNCTIONS) \
+       adddf3vfp \
+       addsf3vfp \
+       divdf3vfp \
+       divsf3vfp \
+       eqdf2vfp \
+       eqsf2vfp \
+       extendsfdf2vfp \
+       fixdfsivfp \
+       fixsfsivfp \
+       fixunsdfsivfp \
+       fixunssfsivfp \
+       floatsidfvfp \
+       floatsisfvfp \
+       floatunssidfvfp \
+       floatunssisfvfp \
+       gedf2vfp \
+       gesf2vfp \
+       gtdf2vfp \
+       gtsf2vfp \
+       ledf2vfp \
+       lesf2vfp \
+       ltdf2vfp \
+       ltsf2vfp \
+       muldf3vfp \
+       mulsf3vfp \
+       nedf2vfp \
+       nesf2vfp \
+       subdf3vfp \
+       subsf3vfp \
+       truncdfsf2vfp \
+       unorddf2vfp \
+       unordsf2vfp
+
+CCKEXT_ARM64_FUNCTIONS := \
+       $(CCKEXT_PROFILE_FUNCTIONS) \
+       divdc3 \
+       divsc3 \
+       muldc3 \
+       mulsc3 \
+       udivti3 \
+       umodti3
+
+FUNCTIONS.cc_kext_ios.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext_ios.armv7k := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext_ios.armv7s := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext_ios.arm64 := $(CCKEXT_ARM64_FUNCTIONS)
+
+CCKEXT_X86_FUNCTIONS := $(CCKEXT_COMMON_FUNCTIONS) \
+       divxc3 \
+       fixunsxfdi \
+       fixunsxfsi \
+       fixxfdi \
+       floatdixf \
+       floatundixf \
+       mulxc3 \
+       powixf2
+
+FUNCTIONS.cc_kext.i386 := $(CCKEXT_X86_FUNCTIONS) \
+       ffssi2 \
+       i686.get_pc_thunk.eax \
+       i686.get_pc_thunk.ebp \
+       i686.get_pc_thunk.ebx \
+       i686.get_pc_thunk.ecx \
+       i686.get_pc_thunk.edi \
+       i686.get_pc_thunk.edx \
+       i686.get_pc_thunk.esi
+
+FUNCTIONS.cc_kext.x86_64 := $(CCKEXT_X86_FUNCTIONS) \
+       absvti2 \
+       addvti3 \
+       ashlti3 \
+       ashrti3 \
+       clzti2 \
+       cmpti2 \
+       ctzti2 \
+       divti3 \
+       ffsti2 \
+       fixdfti \
+       fixsfti \
+       fixunsdfti \
+       fixunssfti \
+       fixunsxfti \
+       fixxfti \
+       floattidf \
+       floattisf \
+       floattixf \
+       floatuntidf \
+       floatuntisf \
+       floatuntixf \
+       lshrti3 \
+       modti3 \
+       multi3 \
+       mulvti3 \
+       negti2 \
+       negvti2 \
+       parityti2 \
+       popcountti2 \
+       subvti3 \
+       ucmpti2 \
+       udivmodti4 \
+       udivti3 \
+       umodti3
+
+FUNCTIONS.cc_kext.x86_64h := $(FUNCTIONS.cc_kext.x86_64)
+
+# FIXME: Currently, compiler-rt is missing implementations for a number of the
+# functions that need to go into libcc_kext.a. Filter them out for now.
+CCKEXT_MISSING_FUNCTIONS := \
+       cmpdf2 cmpsf2 div0 \
+       ffssi2 \
+       udiv_w_sdiv unorddf2 unordsf2 bswapdi2 \
+       bswapsi2 \
+       gcc_bcmp \
+       do_global_dtors \
+       i686.get_pc_thunk.eax i686.get_pc_thunk.ebp i686.get_pc_thunk.ebx \
+       i686.get_pc_thunk.ecx i686.get_pc_thunk.edi i686.get_pc_thunk.edx \
+       i686.get_pc_thunk.esi \
+       aeabi_cdcmpeq aeabi_cdrcmple aeabi_cfcmpeq aeabi_cfrcmple aeabi_dcmpeq \
+       aeabi_dcmpge aeabi_dcmpgt aeabi_dcmple aeabi_dcmplt aeabi_drsub aeabi_fcmpeq \
+       aeabi_fcmpge aeabi_fcmpgt aeabi_fcmple aeabi_fcmplt aeabi_frsub aeabi_idivmod \
+       aeabi_uidivmod
+
+FUNCTIONS.cc_kext_ios.armv7 := \
+       $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios.armv7))
+FUNCTIONS.cc_kext_ios.armv7k := \
+       $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios.armv7k))
+FUNCTIONS.cc_kext_ios.armv7s := \
+       $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios.armv7s))
+FUNCTIONS.cc_kext_ios.arm64 := \
+       $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios.arm64))
+FUNCTIONS.cc_kext.i386 := \
+       $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.i386))
+FUNCTIONS.cc_kext.x86_64 := \
+       $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.x86_64))
+FUNCTIONS.cc_kext.x86_64h := \
+       $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.x86_64h))
+
+KERNEL_USE.cc_kext := 1
+KERNEL_USE.cc_kext_ios := 1
+
+VISIBILITY_HIDDEN := 1
+
+SHARED_LIBRARY_SUFFIX := dylib
diff --git a/src/compiler-rt/make/platform/clang_darwin_test_input.c b/src/compiler-rt/make/platform/clang_darwin_test_input.c
new file mode 100644 (file)
index 0000000..b406a28
--- /dev/null
@@ -0,0 +1,15 @@
+/* Include the headers we use in int_lib.h, to verify that they work. */
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+// Force us to link at least one symbol in a system library
+// to detect systems where we don't have those for a given
+// architecture.
+int main(int argc, const char **argv) {
+    int x;
+    memcpy(&x,&argc,sizeof(int));
+}
diff --git a/src/compiler-rt/make/platform/clang_linux.mk b/src/compiler-rt/make/platform/clang_linux.mk
new file mode 100644 (file)
index 0000000..bf5ee4a
--- /dev/null
@@ -0,0 +1,91 @@
+Description := Static runtime libraries for clang/Linux.
+
+###
+
+CC := clang
+Arch := unknown
+Configs :=
+
+# We don't currently have any general purpose way to target architectures other
+# than the compiler defaults (because there is no generalized way to invoke
+# cross compilers). For now, we just find the target architecture of the
+# compiler and only define configurations we know that compiler can generate.
+CompilerTargetTriple := $(shell \
+       LANG=C $(CC) -v 2>&1 | grep 'Target:' | cut -d' ' -f2)
+ifeq ($(CompilerTargetTriple),)
+$(error "unable to infer compiler target triple for $(CC)")
+endif
+
+# Only define configs if we detected a linux target.
+ifneq ($(findstring -linux-,$(CompilerTargetTriple)),)
+
+# Define configs only if arch in triple is i386 or x86_64
+CompilerTargetArch := $(firstword $(subst -, ,$(CompilerTargetTriple)))
+ifeq ($(call contains,i386 x86_64,$(CompilerTargetArch)),true)
+
+# TryCompile compiler source flags
+# Returns exit code of running a compiler invocation.
+TryCompile = \
+  $(shell \
+    cflags=""; \
+    for flag in $(3); do \
+      cflags="$$cflags $$flag"; \
+    done; \
+    $(1) $$cflags $(2) -o /dev/null > /dev/null 2> /dev/null ; \
+    echo $$?)
+
+test_source = $(ProjSrcRoot)/make/platform/clang_linux_test_input.c
+ifeq ($(CompilerTargetArch),i386)
+  SupportedArches := i386
+  ifeq ($(call TryCompile,$(CC),$(test_source),-m64),0)
+    SupportedArches += x86_64
+  endif
+else
+  SupportedArches := x86_64
+  ifeq ($(call TryCompile,$(CC),$(test_source),-m32),0)
+    SupportedArches += i386
+  endif
+endif
+
+# Build runtime libraries for i386.
+ifeq ($(call contains,$(SupportedArches),i386),true)
+Configs += builtins-i386 profile-i386
+Arch.builtins-i386 := i386
+Arch.profile-i386 := i386
+endif
+
+# Build runtime libraries for x86_64.
+ifeq ($(call contains,$(SupportedArches),x86_64),true)
+Configs += builtins-x86_64 profile-x86_64
+Arch.builtins-x86_64 := x86_64
+Arch.profile-x86_64 := x86_64
+endif
+
+endif
+
+endif
+
+###
+
+CFLAGS := -Wall -Werror -O3 -fomit-frame-pointer
+
+CFLAGS.builtins-i386 := $(CFLAGS) -m32
+CFLAGS.builtins-x86_64 := $(CFLAGS) -m64
+CFLAGS.profile-i386 := $(CFLAGS) -m32
+CFLAGS.profile-x86_64 := $(CFLAGS) -m64
+
+FUNCTIONS.builtins-i386 := $(CommonFunctions) $(ArchFunctions.i386)
+FUNCTIONS.builtins-x86_64 := $(CommonFunctions) $(ArchFunctions.x86_64)
+FUNCTIONS.profile-i386 := GCDAProfiling InstrProfiling InstrProfilingBuffer \
+                          InstrProfilingFile InstrProfilingPlatformOther \
+                          InstrProfilingRuntime InstrProfilingUtil \
+                          InstrProfilingWriter InstrProfilingValue
+FUNCTIONS.profile-x86_64 := $(FUNCTIONS.profile-i386)
+
+# Always use optimized variants.
+OPTIMIZED := 1
+
+# We don't need to use visibility hidden on Linux.
+VISIBILITY_HIDDEN := 0
+
+SHARED_LIBRARY_SUFFIX := so
diff --git a/src/compiler-rt/make/platform/clang_linux_test_input.c b/src/compiler-rt/make/platform/clang_linux_test_input.c
new file mode 100644 (file)
index 0000000..e65ce98
--- /dev/null
@@ -0,0 +1,4 @@
+// This file is used to check if we can produce working executables
+// for i386 and x86_64 archs on Linux.
+#include <stdlib.h>
+int main(){}
diff --git a/src/compiler-rt/make/platform/clang_macho_embedded.mk b/src/compiler-rt/make/platform/clang_macho_embedded.mk
new file mode 100644 (file)
index 0000000..d7870d4
--- /dev/null
@@ -0,0 +1,297 @@
+# These are the functions which clang needs when it is targeting a previous
+# version of the OS. The issue is that the backend may use functions which were
+# not present in the libgcc that shipped on the platform. In such cases, we link
+# with a version of the library which contains private_extern definitions of all
+# the extra functions which might be referenced.
+
+Description := Static runtime libraries for embedded clang/Darwin
+
+# A function that ensures we don't try to build for architectures that we
+# don't have working toolchains for.
+CheckArches = \
+  $(shell \
+    result=""; \
+    for arch in $(1); do \
+      if $(CC) -arch $$arch -c \
+         -integrated-as \
+         $(ProjSrcRoot)/make/platform/clang_macho_embedded_test_input.c \
+         -o /dev/null > /dev/null 2> /dev/null; then \
+        result="$$result$$arch "; \
+      else \
+       printf 1>&2 \
+         "warning: clang_macho_embedded.mk: dropping arch '$$arch' from lib '$(2)'\n"; \
+      fi; \
+    done; \
+    echo $$result)
+
+XCRun = \
+  $(shell \
+    result=`xcrun -find $(1) 2> /dev/null`; \
+    if [ "$$?" != "0" ]; then result=$(1); fi; \
+    echo $$result)
+
+###
+
+CC       := $(call XCRun,clang)
+AR       := $(call XCRun,ar)
+RANLIB   := $(call XCRun,ranlib)
+STRIP    := $(call XCRun,strip)
+LIPO     := $(call XCRun,lipo)
+DSYMUTIL := $(call XCRun,dsymutil)
+
+Configs :=
+UniversalArchs :=
+
+# Soft-float version of the runtime. No floating-point instructions will be used
+# and the ABI (out of necessity) passes floating values in normal registers:
+# non-VFP variant of the AAPCS.
+UniversalArchs.soft_static := $(call CheckArches,armv6m armv7m armv7em armv7,soft_static)
+Configs += $(if $(UniversalArchs.soft_static),soft_static)
+
+# Hard-float version of the runtime. On ARM VFP instructions and registers are
+# allowed, and floating point values get passed in them. VFP variant of the
+# AAPCS.
+UniversalArchs.hard_static := $(call CheckArches,armv7em armv7 i386 x86_64,hard_static)
+Configs += $(if $(UniversalArchs.hard_static),hard_static)
+
+UniversalArchs.soft_pic := $(call CheckArches,armv6m armv7m armv7em armv7,soft_pic)
+Configs += $(if $(UniversalArchs.soft_pic),soft_pic)
+
+UniversalArchs.hard_pic := $(call CheckArches,armv7em armv7 i386 x86_64,hard_pic)
+Configs += $(if $(UniversalArchs.hard_pic),hard_pic)
+
+CFLAGS := -Wall -Werror -Oz -fomit-frame-pointer -ffreestanding
+
+PIC_CFLAGS := -fPIC
+STATIC_CFLAGS := -static
+
+CFLAGS_SOFT := -mfloat-abi=soft
+CFLAGS_HARD := -mfloat-abi=hard
+
+CFLAGS_ARMV7 := -target thumbv7-apple-darwin-eabi
+CFLAGS_I386  := -march=pentium
+
+CFLAGS.soft_static := $(CFLAGS) $(STATIC_CFLAGS) $(CFLAGS_SOFT)
+CFLAGS.hard_static := $(CFLAGS) $(STATIC_CFLAGS) $(CFLAGS_HARD)
+CFLAGS.soft_pic    := $(CFLAGS) $(PIC_CFLAGS) $(CFLAGS_SOFT)
+CFLAGS.hard_pic    := $(CFLAGS) $(PIC_CFLAGS) $(CFLAGS_HARD)
+
+CFLAGS.soft_static.armv7 := $(CFLAGS.soft_static) $(CFLAGS_ARMV7)
+CFLAGS.hard_static.armv7 := $(CFLAGS.hard_static) $(CFLAGS_ARMV7)
+CFLAGS.soft_pic.armv7    := $(CFLAGS.soft_pic) $(CFLAGS_ARMV7)
+CFLAGS.hard_pic.armv7    := $(CFLAGS.hard_pic) $(CFLAGS_ARMV7)
+
+# x86 platforms ignore -mfloat-abi options and complain about doing so. Despite
+# this they're hard-float.
+CFLAGS.hard_static.i386   := $(CFLAGS) $(STATIC_CFLAGS) $(CFLAGS_I386)
+CFLAGS.hard_pic.i386      := $(CFLAGS) $(PIC_CFLAGS) $(CFLAGS_I386)
+CFLAGS.hard_static.x86_64 := $(CFLAGS) $(STATIC_CFLAGS)
+CFLAGS.hard_pic.x86_64    := $(CFLAGS) $(PIC_CFLAGS)
+
+# Functions not wanted:
+#   + eprintf is obsolete anyway
+#   + *vfp: designed for Thumb1 CPUs with VFPv2
+
+COMMON_FUNCTIONS := \
+       absvdi2 \
+       absvsi2 \
+       addvdi3 \
+       addvsi3 \
+       ashldi3 \
+       ashrdi3 \
+       bswapdi2 \
+       bswapsi2 \
+       clzdi2 \
+       clzsi2 \
+       cmpdi2 \
+       ctzdi2 \
+       ctzsi2 \
+       divdc3 \
+       divdi3 \
+       divsc3 \
+       divmodsi4 \
+       udivmodsi4 \
+       do_global_dtors \
+       ffsdi2 \
+       fixdfdi \
+       fixsfdi \
+       fixunsdfdi \
+       fixunsdfsi \
+       fixunssfdi \
+       fixunssfsi \
+       floatdidf \
+       floatdisf \
+       floatundidf \
+       floatundisf \
+       gcc_bcmp \
+       lshrdi3 \
+       moddi3 \
+       muldc3 \
+       muldi3 \
+       mulsc3 \
+       mulvdi3 \
+       mulvsi3 \
+       negdi2 \
+       negvdi2 \
+       negvsi2 \
+       paritydi2 \
+       paritysi2 \
+       popcountdi2 \
+       popcountsi2 \
+       powidf2 \
+       powisf2 \
+       subvdi3 \
+       subvsi3 \
+       ucmpdi2 \
+       udiv_w_sdiv \
+       udivdi3 \
+       udivmoddi4 \
+       umoddi3 \
+       adddf3 \
+       addsf3 \
+       cmpdf2 \
+       cmpsf2 \
+       div0 \
+       divdf3 \
+       divsf3 \
+       divsi3 \
+       extendsfdf2 \
+       extendhfsf2 \
+       ffssi2 \
+       fixdfsi \
+       fixsfsi \
+       floatsidf \
+       floatsisf \
+       floatunsidf \
+       floatunsisf \
+       comparedf2 \
+       comparesf2 \
+       modsi3 \
+       muldf3 \
+       mulsf3 \
+       negdf2 \
+       negsf2 \
+       subdf3 \
+       subsf3 \
+       truncdfhf2 \
+       truncdfsf2 \
+       truncsfhf2 \
+       udivsi3 \
+       umodsi3 \
+       unorddf2 \
+       unordsf2 \
+       atomic_flag_clear \
+       atomic_flag_clear_explicit \
+       atomic_flag_test_and_set \
+       atomic_flag_test_and_set_explicit \
+       atomic_signal_fence \
+       atomic_thread_fence
+
+ARM_FUNCTIONS := \
+       aeabi_cdcmpeq \
+       aeabi_cdrcmple \
+       aeabi_cfcmpeq \
+       aeabi_cfrcmple \
+       aeabi_dcmpeq \
+       aeabi_dcmpge \
+       aeabi_dcmpgt \
+       aeabi_dcmple \
+       aeabi_dcmplt \
+       aeabi_drsub \
+       aeabi_fcmpeq \
+       aeabi_fcmpge \
+       aeabi_fcmpgt \
+       aeabi_fcmple \
+       aeabi_fcmplt \
+       aeabi_frsub \
+       aeabi_idivmod \
+       aeabi_uidivmod \
+
+# ARM Assembly implementation which requires Thumb2 (i.e. won't work on v6M).
+THUMB2_FUNCTIONS := \
+       switch16 \
+       switch32 \
+       switch8 \
+       switchu8 \
+       sync_fetch_and_add_4 \
+       sync_fetch_and_sub_4 \
+       sync_fetch_and_and_4 \
+       sync_fetch_and_or_4 \
+       sync_fetch_and_xor_4 \
+       sync_fetch_and_nand_4 \
+       sync_fetch_and_max_4 \
+       sync_fetch_and_umax_4 \
+       sync_fetch_and_min_4 \
+       sync_fetch_and_umin_4 \
+       sync_fetch_and_add_8 \
+       sync_fetch_and_sub_8 \
+       sync_fetch_and_and_8 \
+       sync_fetch_and_or_8 \
+       sync_fetch_and_xor_8 \
+       sync_fetch_and_nand_8 \
+       sync_fetch_and_max_8 \
+       sync_fetch_and_umax_8 \
+       sync_fetch_and_min_8 \
+       sync_fetch_and_umin_8
+
+I386_FUNCTIONS :=  \
+       i686.get_pc_thunk.eax \
+       i686.get_pc_thunk.ebp \
+       i686.get_pc_thunk.ebx \
+       i686.get_pc_thunk.ecx \
+       i686.get_pc_thunk.edi \
+       i686.get_pc_thunk.edx \
+       i686.get_pc_thunk.esi
+
+# FIXME: Currently, compiler-rt is missing implementations for a number of the
+# functions. Filter them out for now.
+MISSING_FUNCTIONS := \
+       cmpdf2 cmpsf2 div0 \
+       ffssi2 \
+       udiv_w_sdiv unorddf2 unordsf2 bswapdi2 \
+       bswapsi2 \
+       gcc_bcmp \
+       do_global_dtors \
+       i686.get_pc_thunk.eax i686.get_pc_thunk.ebp i686.get_pc_thunk.ebx \
+       i686.get_pc_thunk.ecx i686.get_pc_thunk.edi i686.get_pc_thunk.edx \
+       i686.get_pc_thunk.esi \
+       aeabi_cdcmpeq aeabi_cdrcmple aeabi_cfcmpeq aeabi_cfrcmple aeabi_dcmpeq \
+       aeabi_dcmpge aeabi_dcmpgt aeabi_dcmple aeabi_dcmplt aeabi_drsub \
+       aeabi_fcmpeq \ aeabi_fcmpge aeabi_fcmpgt aeabi_fcmple aeabi_fcmplt \
+       aeabi_frsub aeabi_idivmod aeabi_uidivmod
+
+FUNCTIONS_ARMV6M  := $(COMMON_FUNCTIONS) $(ARM_FUNCTIONS)
+FUNCTIONS_ARM_ALL := $(COMMON_FUNCTIONS) $(ARM_FUNCTIONS) $(THUMB2_FUNCTIONS)
+FUNCTIONS_I386    := $(COMMON_FUNCTIONS) $(I386_FUNCTIONS)
+FUNCTIONS_X86_64  := $(COMMON_FUNCTIONS)
+
+FUNCTIONS_ARMV6M := \
+       $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_ARMV6M))
+FUNCTIONS_ARM_ALL := \
+       $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_ARM_ALL))
+FUNCTIONS_I386 := \
+       $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_I386))
+FUNCTIONS_X86_64 := \
+       $(filter-out $(MISSING_FUNCTIONS),$(FUNCTIONS_X86_64))
+
+FUNCTIONS.soft_static.armv6m := $(FUNCTIONS_ARMV6M)
+FUNCTIONS.soft_pic.armv6m    := $(FUNCTIONS_ARMV6M)
+
+FUNCTIONS.soft_static.armv7m := $(FUNCTIONS_ARM_ALL)
+FUNCTIONS.soft_pic.armv7m    := $(FUNCTIONS_ARM_ALL)
+
+FUNCTIONS.soft_static.armv7em := $(FUNCTIONS_ARM_ALL)
+FUNCTIONS.hard_static.armv7em := $(FUNCTIONS_ARM_ALL)
+FUNCTIONS.soft_pic.armv7em    := $(FUNCTIONS_ARM_ALL)
+FUNCTIONS.hard_pic.armv7em    := $(FUNCTIONS_ARM_ALL)
+
+FUNCTIONS.soft_static.armv7 := $(FUNCTIONS_ARM_ALL)
+FUNCTIONS.hard_static.armv7 := $(FUNCTIONS_ARM_ALL)
+FUNCTIONS.soft_pic.armv7    := $(FUNCTIONS_ARM_ALL)
+FUNCTIONS.hard_pic.armv7    := $(FUNCTIONS_ARM_ALL)
+
+FUNCTIONS.hard_static.i386 := $(FUNCTIONS_I386)
+FUNCTIONS.hard_pic.i386    := $(FUNCTIONS_I386)
+
+FUNCTIONS.hard_static.x86_64 := $(FUNCTIONS_X86_64)
+FUNCTIONS.hard_pic.x86_64    := $(FUNCTIONS_X86_64)
diff --git a/src/compiler-rt/make/platform/clang_macho_embedded_test_input.c b/src/compiler-rt/make/platform/clang_macho_embedded_test_input.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/compiler-rt/make/platform/clang_mingw.mk b/src/compiler-rt/make/platform/clang_mingw.mk
new file mode 100644 (file)
index 0000000..2aedbc3
--- /dev/null
@@ -0,0 +1,30 @@
+Description := Static runtime libraries for mingw-w64
+
+###
+
+CC ?= cc
+AR ?= ar
+
+Arch := unknown
+Configs :=
+
+SupportedArches := x86_64 i386 arm
+
+Configs += builtins-x86_64 builtins-i386 builtins-arm
+Arch.builtins-x86_64 := x86_64
+Arch.builtins-i386 := i386
+Arch.builtins-arm := arm
+
+###
+
+CFLAGS := -Wall -O3 -fomit-frame-pointer
+CFLAGS.builtins-x86_64 := -target x86_64-windows-gnu $(CFLAGS)
+CFLAGS.builtins-i386 := -target i686-windows-gnu $(CFLAGS)
+CFLAGS.builtins-arm := -target armv7-windows-gnu $(CFLAGS)
+
+FUNCTIONS.builtins-x86_64 := $(CommonFunctions) $(ArchFunctions.x86_64)
+FUNCTIONS.builtins-i386 := $(CommonFunctions) $(ArchFunctions.i386)
+FUNCTIONS.builtins-arm := $(CommonFunctions) $(ArchFunctions.arm)
+
+# Always use optimized variants.
+OPTIMIZED := 1
diff --git a/src/compiler-rt/make/platform/darwin_bni.mk b/src/compiler-rt/make/platform/darwin_bni.mk
new file mode 100644 (file)
index 0000000..8e066e8
--- /dev/null
@@ -0,0 +1,135 @@
+
+Description := Target for Darwin using an Apple-style build.
+
+Configs := Debug Release Profile Static
+
+# We override this with RC_ARCHS because B&I may want to build on an ARCH we
+# haven't explicitly defined support for. If all goes well, this will just work
+# and the resulting lib will just have generic versions for anything unknown.
+UniversalArchs := $(RC_ARCHS)
+
+ifneq (,$(SDKROOT))
+       override CC := $(shell xcrun -sdk $(SDKROOT) -find clang || echo "false") 
+       AR := $(shell xcrun -sdk $(SDKROOT) -find ar || echo "false") 
+       RANLIB := $(shell xcrun -sdk $(SDKROOT) -find ranlib || echo "false") 
+       STRIP := $(shell xcrun -sdk $(SDKROOT) -find strip || echo "false") 
+       LIPO := $(shell xcrun -sdk $(SDKROOT) -find lipo || echo "false")
+       DSYMUTIL := $(shell xcrun -sdk $(SDKROOT) -find dsymutil || echo "false")
+endif
+
+ifneq ($(IPHONEOS_DEPLOYMENT_TARGET),)
+       DEPLOYMENT_FLAGS := -miphoneos-version-min=$(IPHONEOS_DEPLOYMENT_TARGET) 
+else
+       ifneq ($(MACOSX_DEPLOYMENT_TARGET),)
+               DEPLOYMENT_FLAGS := -mmacosx-version-min=$(MACOSX_DEPLOYMENT_TARGET) 
+       endif
+endif
+
+ifneq (,$(SDKROOT))
+       DEPLOYMENT_FLAGS += -isysroot $(SDKROOT)
+endif
+
+CFLAGS := -Wall -Os -fomit-frame-pointer -g $(DEPLOYMENT_FLAGS)
+CFLAGS.Static := $(CFLAGS) -static  
+DYLIB_FLAGS := $(DEPLOYMENT_FLAGS) \
+               -Xarch_arm -Wl,-alias_list,$(SRCROOT)/lib/builtins/arm/softfloat-alias.list
+
+VISIBILITY_HIDDEN := 0
+VISIBILITY_HIDDEN.Static  := 1
+
+
+FUNCTIONS := absvdi2 absvsi2 addvdi3 addvsi3 ashldi3 ashrdi3 \
+             clzdi2 clzsi2 cmpdi2 ctzdi2 ctzsi2 \
+             divdc3 divdi3 divsc3 ffsdi2 \
+             fixdfdi fixsfdi fixunsdfdi fixunsdfsi fixunssfdi \
+             fixunssfsi floatdidf floatdisf floatundidf floatundisf \
+             gcc_personality_v0 lshrdi3 moddi3 muldc3 muldi3 mulosi4 \
+             mulodi4 muloti4 mulsc3 mulvdi3 mulvsi3 negdi2 negvdi2 negvsi2 \
+             paritydi2 paritysi2 popcountdi2 popcountsi2 powidf2 \
+             powisf2 subvdi3 subvsi3 ucmpdi2 udivdi3 \
+             udivmoddi4 umoddi3 apple_versioning eprintf atomic \
+             atomic_flag_clear atomic_flag_clear_explicit \
+             atomic_flag_test_and_set atomic_flag_test_and_set_explicit \
+             atomic_signal_fence atomic_thread_fence \
+             extendhfsf2 truncdfhf2 truncsfhf2 
+
+FUNCTIONS.i386 := $(FUNCTIONS) \
+                divxc3 fixunsxfdi fixunsxfsi fixxfdi floatdixf \
+                floatundixf mulxc3 powixf2 clear_cache \
+                enable_execute_stack
+FUNCTIONS.ppc := $(FUNCTIONS) \
+                divtc3 fixtfdi fixunstfdi floatditf floatunditf \
+                gcc_qadd gcc_qdiv gcc_qmul gcc_qsub multc3 \
+                powitf2 restFP saveFP trampoline_setup \
+                clear_cache enable_execute_stack
+FUNCTIONS.x86_64 := $(FUNCTIONS) \
+                absvti2 addvti3 ashlti3 ashrti3 clzti2 cmpti2 \
+                ctzti2 divti3 divxc3 ffsti2 fixdfti fixsfti \
+                fixunsdfti fixunssfti fixunsxfdi fixunsxfsi \
+                fixunsxfti fixxfdi fixxfti floatdixf floattidf \
+                floattisf floattixf floatundixf floatuntidf \
+                floatuntisf floatuntixf lshrti3 modti3 multi3 \
+                muloti4 mulvti3 mulxc3 negti2 negvti2 parityti2 \
+                popcountti2 powixf2 subvti3 ucmpti2 udivmodti4 \
+                udivti3 umodti3 clear_cache enable_execute_stack
+
+FUNCTIONS.armv4t := $(FUNCTIONS) 
+
+FUNCTIONS.armv5 := $(FUNCTIONS) \
+                adddf3 addsf3 bswapdi2 bswapsi2  \
+                comparedf2 comparesf2 extendsfdf2 \
+                divdf3 divsf3 \
+                fixdfsi fixsfsi fixunsdfsi fixunssfsi \
+                floatsidf floatsisf floatunsidf floatunsisf \
+                muldf3 mulsf3 \
+                negdf2 negsf2 \
+                truncdfsf2  \
+                modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4 \
+                switch8 switchu8 switch16 switch32 \
+                sync_synchronize
+
+FUNCTIONS.armv6 := $(FUNCTIONS) \
+                               comparedf2 comparesf2 \
+                adddf3vfp addsf3vfp bswapdi2 bswapsi2 divdf3vfp \
+                divsf3vfp eqdf2vfp eqsf2vfp extendsfdf2vfp \
+                fixdfsivfp fixsfsivfp fixunsdfsivfp fixunssfsivfp \
+                floatsidfvfp floatsisfvfp floatunssidfvfp floatunssisfvfp \
+                gedf2vfp gesf2vfp gtdf2vfp gtsf2vfp \
+                ledf2vfp lesf2vfp ltdf2vfp ltsf2vfp \
+                muldf3vfp mulsf3vfp \
+                nedf2vfp nesf2vfp \
+                subdf3vfp subsf3vfp truncdfsf2vfp unorddf2vfp unordsf2vfp \
+                modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4 \
+                switch8 switchu8 switch16 switch32 \
+                restore_vfp_d8_d15_regs save_vfp_d8_d15_regs \
+                sync_synchronize
+
+FUNCTIONS.armv7 := $(FUNCTIONS) \
+                               comparedf2 comparesf2 \
+                adddf3vfp addsf3vfp bswapdi2 bswapsi2 divdf3vfp \
+                divsf3vfp eqdf2vfp eqsf2vfp extendsfdf2vfp \
+                fixdfsivfp fixsfsivfp fixunsdfsivfp fixunssfsivfp \
+                floatsidfvfp floatsisfvfp floatunssidfvfp floatunssisfvfp \
+                gedf2vfp gesf2vfp gtdf2vfp gtsf2vfp \
+                ledf2vfp lesf2vfp ltdf2vfp ltsf2vfp \
+                muldf3vfp mulsf3vfp \
+                nedf2vfp nesf2vfp \
+                subdf3vfp subsf3vfp truncdfsf2vfp unorddf2vfp unordsf2vfp \
+                modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4
+
+FUNCTIONS.armv7s := $(FUNCTIONS.armv7)
+
+FUNCTIONS.arm64 :=  divti3 modti3 \
+                                       udivmodti4 \
+                                       udivti3 umodti3 \
+                                       mulsc3 muldc3 \
+                                       powisf2 powidf2 \
+                                       clzti2 \
+                                       fixdfti fixsfti \
+                                       fixunsdfti fixunssfti fixunssfti \
+                                       floattidf floattisf floatuntidf floatuntisf \
+                                       gcc_personality_v0 atomic \
+                                       atomic_flag_clear atomic_flag_clear_explicit \
+                                       atomic_flag_test_and_set \
+                                       atomic_flag_test_and_set_explicit \
+                                       atomic_signal_fence atomic_thread_fence
diff --git a/src/compiler-rt/make/platform/multi_arch.mk b/src/compiler-rt/make/platform/multi_arch.mk
new file mode 100644 (file)
index 0000000..fe6ac4b
--- /dev/null
@@ -0,0 +1,15 @@
+Description := Example configuration for build two libraries for separate \
+architectures.
+
+Configs := m32 m64
+Arch := i386
+Arch.m64 := x86_64
+
+CC := clang
+
+CFLAGS := -Wall -Werror
+CFLAGS.m32 := $(CFLAGS) -m32 -O3
+CFLAGS.m64 := $(CFLAGS) -m64 -O3
+
+FUNCTIONS := moddi3 floatundixf udivdi3
+FUNCTIONS.m64 := $(FUNCTIONS) lshrdi3
diff --git a/src/compiler-rt/make/platform/triple.mk b/src/compiler-rt/make/platform/triple.mk
deleted file mode 100644 (file)
index 0d68b2e..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-# This "platform" file is intended for building compiler-rt using gcc.
-# The actual target platform is selected by setting the TargetTriple variable to the corresponding LLVM triple.
-
-Description := Static runtime libraries for platforms selected by 'TargetTriple'
-
-# Provide defaults for the required vars
-ifndef CC
-       CC := gcc
-endif
-ifndef CFLAGS
-       CFLAGS := -Wall -O3
-endif
-
-Configs := builtins
-
-Arch := $(word 1,$(subst -, ,$(TargetTriple)))
-ifeq ($(Arch),i686)
-       Arch := i386
-else ifeq ($(Arch),arm)
-ifneq (,$(findstring ios,$(TargetTriple)))
-       Arch := armv7
-else ifneq (,$(findstring android,$(TargetTriple)))
-       Arch := armv7
-endif
-endif
-
-# Filter out stuff that gcc cannot compile (these are only needed for clang-generated code anywasys).
-CommonFunctions_gcc := $(filter-out atomic% enable_execute_stack,$(CommonFunctions))
-
-# Filter out stuff which is not available on specific target
-# For example, sync_fetch_and_add_4 uses Thumb instructions, which are unavailable
-# when building for arm-linux-androideabi
-ifeq ($(TargetTriple),arm-linux-androideabi)
-    ArchDisabledFunctions := \
-                sync_fetch_and_add_4 \
-                sync_fetch_and_sub_4 \
-                sync_fetch_and_and_4 \
-                sync_fetch_and_or_4 \
-                sync_fetch_and_xor_4 \
-                sync_fetch_and_nand_4 \
-                sync_fetch_and_max_4 \
-                sync_fetch_and_umax_4 \
-                sync_fetch_and_min_4 \
-                sync_fetch_and_umin_4 \
-                sync_fetch_and_add_8 \
-                sync_fetch_and_sub_8 \
-                sync_fetch_and_and_8 \
-                sync_fetch_and_or_8 \
-                sync_fetch_and_xor_8 \
-                sync_fetch_and_nand_8 \
-                sync_fetch_and_max_8 \
-                sync_fetch_and_umax_8 \
-                sync_fetch_and_min_8 \
-                sync_fetch_and_umin_8
-endif
-
-# Disable emutls on MIPS
-# Rust uses GCC 4.4 to cross-compile, which doesn't have some builtins
-ifneq (,$(findstring mips,$(TargetTriple)))
-    CommonDisabledFunctions := emutls
-endif
-
-# Disable emutls on Windows
-# emutls works on POSIX only as it uses pthreads
-ifneq (,$(findstring windows,$(TargetTriple)))
-    CommonDisabledFunctions := emutls
-endif
-
-# Clear cache is builtin on aarch64-apple-ios
-# arm64 and aarch64 are synonims, but iOS targets usually use arm64 (history reasons)
-ifeq (aarch64-apple-ios,$(subst arm64,aarch64,$(TargetTriple)))
-CommonDisabledFunctions := clear_cache
-endif
-
-ArchEnabledFunctions := $(filter-out $(ArchDisabledFunctions),$(value ArchFunctions.$(Arch)))
-CommonEnabledFunctions := $(filter-out $(CommonDisabledFunctions),$(CommonFunctions_gcc))
-
-FUNCTIONS.builtins := $(CommonEnabledFunctions) $(ArchEnabledFunctions)
index 684b409bb8f72f7b3da67f2784d6e8bb629ffb7b..e5c51c8cd47493f82f18131d56bc2c97c3a66ae3 100644 (file)
@@ -19,8 +19,8 @@ if(NOT ANDROID)
   if(NOT COMPILER_RT_STANDALONE_BUILD)
     # Use LLVM utils and Clang from the same build tree.
     list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS
-      clang clang-headers FileCheck count not llvm-config llvm-nm llvm-symbolizer
-      compiler-rt-headers)
+      clang clang-headers FileCheck count not llvm-config llvm-nm llvm-objdump
+      llvm-symbolizer compiler-rt-headers)
     if (COMPILER_RT_HAS_PROFILE)
       list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS profile)
     endif()
index aa881ae134e64ac9700e1e3817c88bfb2b72ef98..4595fb547f579d925dc41e4940ce320bf099502b 100644 (file)
@@ -1,9 +1,10 @@
 // Check that when having a DYLD_ROOT_PATH set, the symbolizer still works.
+// RUN: %clangxx_asan -O0 %s -o %t
 // RUN: %env_asan_opts=verbosity=2 DYLD_ROOT_PATH="/" ASAN_SYMBOLIZER_PATH=$(which atos) \
 // RUN:   not %run %t 2>&1 | FileCheck %s
 //
 // Due to a bug in atos, this only works on x86_64.
-// REQUIRES: x86_64
+// REQUIRES: asan-64-bits
 
 #include <stdlib.h>
 #include <string.h>
@@ -16,11 +17,11 @@ int main(int argc, char **argv) {
   // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0
   // CHECK: Using atos at user-specified path:
   // CHECK: #0 0x{{.*}} in {{.*}}free
-  // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer.cc:[[@LINE-4]]
+  // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer-dyld-root-path.cc:[[@LINE-4]]
   // CHECK: freed by thread T0 here:
   // CHECK: #0 0x{{.*}} in {{.*}}free
-  // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer.cc:[[@LINE-8]]
+  // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer-dyld-root-path.cc:[[@LINE-8]]
   // CHECK: allocated by thread T0 here:
-  // CHECK: atos-symbolizer.cc:[[@LINE-13]]
+  // CHECK: atos-symbolizer-dyld-root-path.cc:[[@LINE-13]]
   return res;
 }
index e9af5396e1c318e0ae3027c1dc609f86a21e1b23..9151614819874f381eb8949979b20a6a760acb0a 100644 (file)
@@ -1,6 +1,11 @@
 // RUN: %clang_asan -O0 %s -o %t
 // RUN: not %run %t 2>&1 | FileCheck %s
 
+// Since ASan is built with -fomit-frame-pointer, backtrace is not able to
+// symbolicate the trace past ASan runtime on i386. (This is fixed in
+// latest OS X.)
+// REQUIRES: asan-64-bits
+
 #include <execinfo.h>
 #include <sanitizer/common_interface_defs.h>
 #include <stdio.h>
index 7e8d9173d2966efd85f18992b023e618779db063..b22036a7efedae2c2e69c51d42a7eb2f38b1cfc8 100644 (file)
@@ -37,10 +37,10 @@ int main() {
 
 // CHECK-NOINSERT: exec()-ing the program with
 // CHECK-NOINSERT: DYLD_INSERT_LIBRARIES
-// CHECK-NOINSERT: to enable ASan wrappers.
+// CHECK-NOINSERT: to enable wrappers.
 // CHECK-NOINSERT: Passed
 
 // CHECK-NOT: exec()-ing the program with
 // CHECK-NOT: DYLD_INSERT_LIBRARIES
-// CHECK-NOT: to enable ASan wrappers.
+// CHECK-NOT: to enable wrappers.
 // CHECK: Passed
index 9bd32869c9ee00d5b7300d7c3cbfffec6106a4a0..ed5779ebe220a1fb7eae0ddb5996b9799bf28346 100644 (file)
 // RUN: echo __asan_report_store16 >> %t.interface
 // RUN: echo __asan_report_load_n >> %t.interface
 // RUN: echo __asan_report_store_n >> %t.interface
+// RUN: echo __asan_report_load1_noabort >> %t.interface
+// RUN: echo __asan_report_load2_noabort >> %t.interface
+// RUN: echo __asan_report_load4_noabort >> %t.interface
+// RUN: echo __asan_report_load8_noabort >> %t.interface
+// RUN: echo __asan_report_load16_noabort >> %t.interface
+// RUN: echo __asan_report_store1_noabort >> %t.interface
+// RUN: echo __asan_report_store2_noabort >> %t.interface
+// RUN: echo __asan_report_store4_noabort >> %t.interface
+// RUN: echo __asan_report_store8_noabort >> %t.interface
+// RUN: echo __asan_report_store16_noabort >> %t.interface
+// RUN: echo __asan_report_load_n_noabort >> %t.interface
+// RUN: echo __asan_report_store_n_noabort >> %t.interface
 // RUN: echo __asan_report_exp_load1 >> %t.interface
 // RUN: echo __asan_report_exp_load2 >> %t.interface
 // RUN: echo __asan_report_exp_load4 >> %t.interface
diff --git a/src/compiler-rt/test/asan/TestCases/Linux/calloc-preload.c b/src/compiler-rt/test/asan/TestCases/Linux/calloc-preload.c
new file mode 100644 (file)
index 0000000..eb1c673
--- /dev/null
@@ -0,0 +1,36 @@
+// Test that initially callocked memory is properly freed
+// (see https://github.com/google/sanitizers/issues/626).
+// 
+// RUN: %clang %s -o %t
+// RUN: env LD_PRELOAD=%shared_libasan %run %t
+//
+// REQUIRES: asan-dynamic-runtime
+//
+// This way of setting LD_PRELOAD does not work with Android test runner.
+// REQUIRES: not-android
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void *ptr;
+
+// This constructor will run before __asan_init
+// so calloc will allocate memory from special pool.
+static void init() {
+  ptr = calloc(10, 1);
+}
+
+__attribute__((section(".preinit_array"), used))
+void *dummy = init;
+
+void free_memory() {
+  // This used to abort because
+  // Asan's free didn't recognize ptr.
+  free(ptr);
+}
+
+int main() {
+  free_memory();
+  return 0;
+}
+
index beb44bdc0b0b10500f9b9eb3a3ad3c11dd9ad0fc..971feb5dc09f347e69f7033eb0c46e8c2d0f7ca5 100644 (file)
 // RUN: echo __asan_report_store16 >> %t.interface
 // RUN: echo __asan_report_load_n >> %t.interface
 // RUN: echo __asan_report_store_n >> %t.interface
+// RUN: echo __asan_report_load1_noabort >> %t.interface
+// RUN: echo __asan_report_load2_noabort >> %t.interface
+// RUN: echo __asan_report_load4_noabort >> %t.interface
+// RUN: echo __asan_report_load8_noabort >> %t.interface
+// RUN: echo __asan_report_load16_noabort >> %t.interface
+// RUN: echo __asan_report_store1_noabort >> %t.interface
+// RUN: echo __asan_report_store2_noabort >> %t.interface
+// RUN: echo __asan_report_store4_noabort >> %t.interface
+// RUN: echo __asan_report_store8_noabort >> %t.interface
+// RUN: echo __asan_report_store16_noabort >> %t.interface
+// RUN: echo __asan_report_load_n_noabort >> %t.interface
+// RUN: echo __asan_report_store_n_noabort >> %t.interface
 // RUN: echo __asan_report_exp_load1 >> %t.interface
 // RUN: echo __asan_report_exp_load2 >> %t.interface
 // RUN: echo __asan_report_exp_load4 >> %t.interface
diff --git a/src/compiler-rt/test/asan/TestCases/Linux/mincore.cc b/src/compiler-rt/test/asan/TestCases/Linux/mincore.cc
new file mode 100644 (file)
index 0000000..30f4508
--- /dev/null
@@ -0,0 +1,34 @@
+// RUN: %clangxx_asan -std=c++11 -O0 %s -o %t && %run %t
+
+#include <assert.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+int main(void) {
+  unsigned char vec[20];
+  int res;
+  size_t PS = sysconf(_SC_PAGESIZE);
+  void *addr = mmap(nullptr, 20 * PS, PROT_READ | PROT_WRITE,
+                    MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+
+  res = mincore(addr, 10 * PS, vec);
+  assert(res == 0);
+  for (int i = 0; i < 10; ++i)
+    assert((vec[i] & 1) == 0);
+
+  for (int i = 0; i < 5; ++i)
+    ((char *)addr)[i * PS] = 1;
+  res = mincore(addr, 10 * PS, vec);
+  assert(res == 0);
+  for (int i = 0; i < 10; ++i)
+    assert((vec[i] & 1) == (i < 5));
+
+  for (int i = 5; i < 10; ++i)
+    ((char *)addr)[i * PS] = 1;
+  res = mincore(addr, 10 * PS, vec);
+  assert(res == 0);
+  for (int i = 0; i < 10; ++i)
+    assert((vec[i] & 1) == 1);
+
+  return 0;
+}
index ff699f3d60c45ba9265c580bbbc46c7ff9662634..11bb1a4f849c265768776366c45b7ce9b61ca168 100644 (file)
@@ -9,7 +9,7 @@
 // CHECK: __tls_get_addr: static tls
 // CHECK: after
 
-// XFAIL: powerpc64, aarch64
+// XFAIL: aarch64
 
 #ifndef SHARED
 #include <stdio.h>
diff --git a/src/compiler-rt/test/asan/TestCases/Posix/halt_on_error-signals.c b/src/compiler-rt/test/asan/TestCases/Posix/halt_on_error-signals.c
new file mode 100644 (file)
index 0000000..60916f6
--- /dev/null
@@ -0,0 +1,102 @@
+// Test interaction of Asan recovery mode with asynch signals.
+//
+// RUN: %clang_asan -fsanitize-recover=address -pthread %s -o %t
+//
+// RUN: rm -f %t.log
+// RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=false %run %t 100 >%t.log 2>&1 || true
+// Collision will almost always get triggered but we still need to check the unlikely case:
+// RUN: FileCheck --check-prefix=CHECK-COLLISION %s < %t.log || FileCheck --check-prefix=CHECK-NO-COLLISION %s < %t.log
+
+#define _SVID_SOURCE 1  // SA_NODEFER
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <time.h>
+#include <signal.h>
+
+#include <sanitizer/asan_interface.h>
+
+void random_delay(unsigned *seed) {
+  *seed = 1664525 * *seed + 1013904223;
+  struct timespec delay = { 0, (*seed % 1000) * 1000 };
+  nanosleep(&delay, 0);
+}
+
+volatile char bad[2] = {1, };
+
+void error() {
+  // CHECK-COLLISION: AddressSanitizer: nested bug in the same thread, aborting
+  // CHECK-NO-COLLISION: AddressSanitizer: use-after-poison
+  volatile int idx = 0;
+  bad[idx] = 0;
+}
+
+#define CHECK_CALL(e, msg) do {             \
+  if (0 != (e)) {                           \
+    fprintf(stderr, "Failed to " msg "\n"); \
+    exit(1);                                \
+  }                                         \
+} while (0)
+
+size_t niter = 10;
+pthread_t sender_tid, receiver_tid;
+
+pthread_mutex_t keep_alive_mu = PTHREAD_MUTEX_INITIALIZER;
+
+void *sender(void *arg) {
+  unsigned seed = 0;
+  for (size_t i = 0; i < niter; ++i) {
+    random_delay(&seed);
+    CHECK_CALL(pthread_kill(receiver_tid, SIGUSR1), "send signal");
+  }
+  return 0;
+}
+
+void handler(int sig) {
+  // Expect error collisions here
+  error();
+}
+
+void *receiver(void *arg) {
+  unsigned seed = 1;
+  for (size_t i = 0; i < niter; ++i) {
+    random_delay(&seed);
+    // And here
+    error();
+  }
+  // Parent will release this when it's ok to terminate
+  CHECK_CALL(pthread_mutex_lock(&keep_alive_mu), "unlock mutex");
+  return 0;
+}
+
+int main(int argc, char **argv) {
+  if (argc != 2) {
+    fprintf(stderr, "Syntax: %s niter\n", argv[0]);
+    exit(1);
+  }
+
+  niter = (size_t)strtoul(argv[1], 0, 0);
+
+  struct sigaction sa;
+  memset(&sa, 0, sizeof(sa));
+  sa.sa_handler = handler;
+  sa.sa_flags = SA_NODEFER; // Enable nested handlers to add more stress
+  CHECK_CALL(sigaction(SIGUSR1, &sa, 0), "set sighandler");
+
+  __asan_poison_memory_region(&bad, sizeof(bad));
+
+  CHECK_CALL(pthread_mutex_lock(&keep_alive_mu), "lock mutex");
+  CHECK_CALL(pthread_create(&receiver_tid, 0, receiver, 0), "start thread");
+  CHECK_CALL(pthread_create(&sender_tid, 0, sender, 0), "start thread");
+  CHECK_CALL(pthread_join(sender_tid, 0), "join thread");
+  // Now allow receiver to die
+  CHECK_CALL(pthread_mutex_unlock(&keep_alive_mu), "unlock mutex");
+  CHECK_CALL(pthread_join(receiver_tid, 0), "join thread");
+
+  // CHECK-NO-COLLISION: All threads terminated
+  printf("All threads terminated\n");
+
+  return 0;
+}
diff --git a/src/compiler-rt/test/asan/TestCases/Posix/halt_on_error-torture.cc b/src/compiler-rt/test/asan/TestCases/Posix/halt_on_error-torture.cc
new file mode 100644 (file)
index 0000000..019f7d1
--- /dev/null
@@ -0,0 +1,87 @@
+// Stress test recovery mode with many threads.
+//
+// RUN: %clangxx_asan -fsanitize-recover=address -pthread %s -o %t
+//
+// RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=false %run %t 1 10 >1.txt 2>&1
+// RUN: FileCheck %s < 1.txt
+// RUN: [ $(grep -c 'ERROR: AddressSanitizer: use-after-poison' 1.txt) -eq 10 ]
+// RUN: FileCheck --check-prefix=CHECK-NO-COLLISION %s < 1.txt
+//
+// Collisions are unlikely but still possible so we need the ||.
+// RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=false %run %t 10 20 >10.txt 2>&1 || true
+// This one is racy although _very_ unlikely to fail:
+// RUN: FileCheck %s < 10.txt
+// RUN: FileCheck --check-prefix=CHECK-COLLISION %s < 1.txt || FileCheck --check-prefix=CHECK-NO-COLLISION %s < 1.txt
+//
+// Collisions are unlikely but still possible so we need the ||.
+// RUN: %env_asan_opts=halt_on_error=false %run %t 10 20 >10.txt 2>&1 || true
+// This one is racy although _very_ unlikely to fail:
+// RUN: FileCheck %s < 10.txt
+// RUN: FileCheck --check-prefix=CHECK-COLLISION %s < 1.txt || FileCheck --check-prefix=CHECK-NO-COLLISION %s < 1.txt
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <time.h>
+
+#include <sanitizer/asan_interface.h>
+
+size_t nthreads = 10;
+size_t niter = 10;
+
+void random_delay(unsigned *seed) {
+  *seed = 1664525 * *seed + 1013904223;
+  struct timespec delay = { 0, (*seed % 1000) * 1000 };
+  nanosleep(&delay, 0);
+}
+
+void *run(void *arg) {
+  unsigned seed = (unsigned)(size_t)arg;
+
+  volatile char tmp[2];
+  __asan_poison_memory_region(&tmp, sizeof(tmp)); 
+
+  for (size_t i = 0; i < niter; ++i) {
+    random_delay(&seed);
+    // Expect error collisions here
+    // CHECK: ERROR: AddressSanitizer: use-after-poison
+    volatile int idx = 0;
+    tmp[idx] = 0;
+  }
+
+  return 0;
+}
+
+int main(int argc, char **argv) {
+  if (argc != 3) {
+    fprintf(stderr, "Syntax: %s nthreads niter\n", argv[0]);
+    exit(1);
+  }
+
+  nthreads = (size_t)strtoul(argv[1], 0, 0);
+  niter = (size_t)strtoul(argv[2], 0, 0);
+
+  pthread_t *tids = new pthread_t[nthreads];
+
+  for (size_t i = 0; i < nthreads; ++i) {
+    if (0 != pthread_create(&tids[i], 0, run, (void *)i)) {
+      fprintf(stderr, "Failed to create thread\n");
+      exit(1);
+    }
+  }
+
+  for (size_t i = 0; i < nthreads; ++i) {
+    if (0 != pthread_join(tids[i], 0)) {
+      fprintf(stderr, "Failed to join thread\n");
+      exit(1);
+    }
+  }
+
+  // CHECK-COLLISION: AddressSanitizer: nested bug in the same thread, aborting
+  // CHECK-NO-COLLISION: All threads terminated
+  printf("All threads terminated\n");
+
+  delete [] tids;
+
+  return 0;
+}
diff --git a/src/compiler-rt/test/asan/TestCases/Posix/halt_on_error_suppress_equal_pcs.cc b/src/compiler-rt/test/asan/TestCases/Posix/halt_on_error_suppress_equal_pcs.cc
new file mode 100644 (file)
index 0000000..98b0348
--- /dev/null
@@ -0,0 +1,55 @@
+// Test reports dedupication for recovery mode.
+//
+// RUN: %clang_asan -fsanitize-recover=address %s -o %t
+//
+// Check for reports dedupication.
+// RUN: %env_asan_opts=halt_on_error=false %run %t 2>&1 | FileCheck %s
+//
+// Check that we die after reaching different reports number threshold.
+// RUN: %env_asan_opts=halt_on_error=false not %run %t 1 > %t1.log 2>&1
+// RUN: [ $(grep -c 'ERROR: AddressSanitizer: stack-buffer-overflow' %t1.log) -eq 25 ]
+//
+// Check suppress_equal_pcs=true behavior is equal to default one.
+// RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=true %run %t 2>&1 | FileCheck %s
+//
+// Check suppress_equal_pcs=false behavior isn't equal to default one.
+// RUN: %env_asan_opts=halt_on_error=false:suppress_equal_pcs=false %run %t > %t2.log 2>&1
+// RUN: [ $(grep -c 'ERROR: AddressSanitizer: stack-buffer-overflow' %t2.log) -eq 30 ]
+
+#define ACCESS_ARRAY_FIVE_ELEMENTS(array, i)     \
+  array[i] = i;                                  \
+  array[i + 1] = i + 1;                          \
+  array[i + 2] = i + 2;                          \
+  array[i + 3] = i + 3;                          \
+  array[i + 4] = i + 4;                          \
+
+volatile int ten = 10;
+unsigned kNumIterations = 10;
+
+int main(int argc, char **argv) {
+  char a[10];
+  char b[10];
+
+  if (argc == 1) {
+    for (int i = 0; i < kNumIterations; ++i) {
+      // CHECK: READ of size 1
+      volatile int res = a[ten + i];
+      // CHECK: WRITE of size 1
+      a[i + ten] = res + 3;
+      // CHECK: READ of size 1
+      res = a[ten + i];
+      // CHECK-NOT: ERROR
+    }
+  } else {
+    for (int i = 0; i < kNumIterations; ++i) {
+      ACCESS_ARRAY_FIVE_ELEMENTS(a, ten);
+      ACCESS_ARRAY_FIVE_ELEMENTS(a, ten + 5);
+      ACCESS_ARRAY_FIVE_ELEMENTS(a, ten + 10);
+      ACCESS_ARRAY_FIVE_ELEMENTS(b, ten);
+      ACCESS_ARRAY_FIVE_ELEMENTS(b, ten + 5);
+      ACCESS_ARRAY_FIVE_ELEMENTS(b, ten + 10);
+    }
+  }
+  return 0;
+}
+
index e86616f112bd4b2ac3850abbb6b9d59782dc2195..3873c3fceea8527157f20b5eed79855bb2233728 100644 (file)
@@ -2,7 +2,6 @@
 // RUN: %run %t 2>&1
 //
 // REQUIRES: stable-runtime
-// XFAIL: powerpc64
 
 // This testcase checks correct interaction between VLAs and allocas.
 
diff --git a/src/compiler-rt/test/asan/TestCases/coverage-pc-buffer.cc b/src/compiler-rt/test/asan/TestCases/coverage-pc-buffer.cc
new file mode 100644 (file)
index 0000000..67b6935
--- /dev/null
@@ -0,0 +1,48 @@
+// Test __sanitizer_coverage_pc_buffer().
+
+// RUN: %clangxx_asan -fsanitize-coverage=edge %s -o %t && %run %t
+
+// UNSUPPORTED: android
+
+#include <assert.h>
+#include <sanitizer/coverage_interface.h>
+#include <stdio.h>
+
+static volatile int sink;
+__attribute__((noinline)) void bar() { sink = 2; }
+__attribute__((noinline)) void foo() { sink = 1; }
+
+void assertNotZeroPcs(uintptr_t *buf, uintptr_t size) {
+  assert(buf);
+  for (uintptr_t i = 0; i < size; ++i)
+    assert(buf[i]);
+}
+
+int main() {
+  uintptr_t *buf = NULL;
+  uintptr_t sz = __sanitizer_get_coverage_pc_buffer(&buf);
+  assertNotZeroPcs(buf, sz);
+  assert(sz);
+
+  foo();
+  bar();
+  uintptr_t *buf1 = NULL;
+  uintptr_t sz1 = __sanitizer_get_coverage_pc_buffer(&buf1);
+  assertNotZeroPcs(buf1, sz1);
+  assert(buf1 == buf);
+  assert(sz1 > sz);
+
+  bar();
+  uintptr_t *buf2 = NULL;
+  uintptr_t sz2 = __sanitizer_get_coverage_pc_buffer(&buf2);
+  assertNotZeroPcs(buf2, sz2);
+  assert(buf2 == buf);
+  assert(sz2 > sz1);
+
+  __sanitizer_reset_coverage();
+  uintptr_t *buf3 = NULL;
+  uintptr_t sz3 = __sanitizer_get_coverage_pc_buffer(&buf3);
+  assertNotZeroPcs(buf3, sz3);
+  assert(buf3 == buf);
+  assert(sz3 < sz2);
+}
index 1f5eed4c85f4034e08fe341404e8ba722efb5ca1..eb8da8c1aa0645e611d014727009c31bca5e626d 100644 (file)
@@ -42,6 +42,7 @@ int main() {
   assert(IS_POWER_OF_TWO(bar_bit));
 
   __sanitizer_reset_coverage();
+  assert(__sanitizer_get_total_unique_coverage() == 0);
   GET_AND_PRINT_COVERAGE();
   assert(bitset == 0);
 
diff --git a/src/compiler-rt/test/asan/TestCases/halt_on_error-1.c b/src/compiler-rt/test/asan/TestCases/halt_on_error-1.c
new file mode 100644 (file)
index 0000000..63c65e5
--- /dev/null
@@ -0,0 +1,29 @@
+// Test recovery mode.
+//
+// RUN: %clang_asan -fsanitize-recover=address %s -o %t
+//
+// RUN: env not %run %t 2>&1 | FileCheck %s
+// RUN: %env_asan_opts=halt_on_error=true not %run %t 2>&1 | FileCheck %s
+// RUN: %env_asan_opts=halt_on_error=false %run %t 2>&1 | FileCheck %s --check-prefix CHECK-RECOVER
+
+#include <string.h>
+
+volatile int ten = 10;
+
+int main() {
+  char x[10];
+  // CHECK: WRITE of size 11
+  // CHECK-RECOVER: WRITE of size 11
+  memset(x, 0, 11);
+  // CHECK-NOT: READ of size 1
+  // CHECK-RECOVER: READ of size 1
+  volatile int res = x[ten];
+  // CHECK-NOT: WRITE of size 1
+  // CHECK-RECOVER: WRITE of size 1
+  x[ten] = res + 3;
+  // CHECK-NOT: READ of size 1
+  // CHECK-RECOVER: READ of size 1
+  res = x[ten];
+  return  0;
+}
+
diff --git a/src/compiler-rt/test/asan/TestCases/speculative_load2.cc b/src/compiler-rt/test/asan/TestCases/speculative_load2.cc
new file mode 100644 (file)
index 0000000..51051eb
--- /dev/null
@@ -0,0 +1,24 @@
+// Verifies that speculative loads from unions do not happen under asan.
+// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1
+// RUN: %clangxx_asan -O1 %s -o %t && %run %t 2>&1
+// RUN: %clangxx_asan -O2 %s -o %t && %run %t 2>&1
+// RUN: %clangxx_asan -O3 %s -o %t && %run %t 2>&1
+
+typedef union {
+  short q;
+  struct {
+    short x;
+    short y;
+    int for_alignment;
+  } w;
+} U;
+
+int main() {
+  char *buf = new char[2];
+  buf[0] = buf[1] = 0x0;
+  U *u = (U *)buf;
+  short result = u->q == 0 ? 0 : u->w.y;
+  delete[] buf;
+  return result;
+}
+
index bce48199dbf71116a31f4d1e2492b1e0afc2912d..01083510c32f014f9411cc2b88750e9fb7c9e77b 100644 (file)
@@ -1,8 +1,5 @@
 // RUN: %clangxx_asan -O %s -o %t && %run %t
 
-// Clang doesn't support exceptions on Windows yet.
-// XFAIL: win32
-
 #include <assert.h>
 #include <stdio.h>
 #include <sanitizer/asan_interface.h>
index fd99f669961a2a0e104ce0b967054d221bbf84c7..835547090a17d0a075ed09a292afc6b3bb88ee6d 100644 (file)
@@ -34,7 +34,9 @@ default_asan_opts = ''
 if config.host_os == 'Darwin':
   # On Darwin, we default to `abort_on_error=1`, which would make tests run
   # much slower. Let's override this and run lit tests with 'abort_on_error=0'.
+  # Also, make sure we do not overwhelm the syslog while testing.
   default_asan_opts = 'abort_on_error=0'
+  default_asan_opts += ':log_to_syslog=0'
 if default_asan_opts:
   config.environment['ASAN_OPTIONS'] = default_asan_opts
   default_asan_opts += ':'
index ad2c96dc0409d6cc5eaa86d49759bc01f655cc43..a1f0613441da79153fb2b8eb353df0c3b87fafa2 100644 (file)
@@ -13,8 +13,6 @@
 
 #include <stdio.h>
 
-#if _ARCH_PPC
-
 #include "int_lib.h"
 #include <math.h>
 #include <complex.h>
@@ -104,7 +102,7 @@ int test__divtc3(long double a, long double b, long double c, long double d)
             {
             long double _Complex z = (a * c + b * d) / (c * c + d * d)
                                    + (b * c - a * d) / (c * c + d * d) * _Complex_I;
-            if (cabs((r - z)/r) > 1.e-6)
+            if (cabsl((r - z)/r) > 1.e-6)
                 return 1;
             }
             break;
@@ -358,11 +356,8 @@ long double x[][2] =
 
 };
 
-#endif
-
 int main()
 {
-#if _ARCH_PPC
     const unsigned N = sizeof(x) / sizeof(x[0]);
     unsigned i, j;
     for (i = 0; i < N; ++i)
@@ -373,11 +368,7 @@ int main()
                 return 1;
         }
     }
-       
-//     printf("No errors found.\n");
 
-#else
-    printf("skipped\n");
-#endif
+//     printf("No errors found.\n");
     return 0;
 }
index 09672953b5145f0a91fe87bd47cc75f596c050e6..5626a6e50ed797843b94d77ddf7c007a3ae29b70 100644 (file)
@@ -9,6 +9,9 @@ if(NOT COMPILER_RT_STANDALONE_BUILD)
     opt
     ubsan
   )
+  if(COMPILER_RT_HAS_CFI)
+    list(APPEND CFI_TEST_DEPS cfi)
+  endif()
   if(LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR)
     list(APPEND CFI_TEST_DEPS
       LLVMgold
index 9655891efbd009bf8ddc29f218cb5251c3f1c4e6..c5e9db1092a6f9dcaf5acb61628f847b6cabc3ff 100644 (file)
@@ -82,7 +82,7 @@ int main() {
   fprintf(stderr, "1\n");
 
   // CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during base-to-derived cast
-  // CFI-DIAG-NEXT: note: vtable is of type 'A<B>'
+  // CFI-DIAG-NEXT: note: vtable is of type '{{(class )?}}A<{{(class )?}}B>'
   B* b = new B;
   break_optimization(b);
   delete b; // UB here
diff --git a/src/compiler-rt/test/cfi/cross-dso/icall/icall-from-dso.cpp b/src/compiler-rt/test/cfi/cross-dso/icall/icall-from-dso.cpp
new file mode 100644 (file)
index 0000000..1995f05
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %clangxx_cfi_dso -DSHARED_LIB %s -fPIC -shared -o %t-so.so
+// RUN: %clangxx_cfi_dso %s -o %t %t-so.so && %expect_crash %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+
+#ifdef SHARED_LIB
+void g();
+void f() {
+  // CHECK: =1=
+  fprintf(stderr, "=1=\n");
+  ((void (*)(void))g)();
+  // CHECK: =2=
+  fprintf(stderr, "=2=\n");
+  ((void (*)(int))g)(42); // UB here
+  // CHECK-NOT: =3=
+  fprintf(stderr, "=3=\n");
+}
+#else
+void f();
+void g() {
+}
+
+int main() {
+  f();
+}
+#endif
diff --git a/src/compiler-rt/test/cfi/cross-dso/icall/icall.cpp b/src/compiler-rt/test/cfi/cross-dso/icall/icall.cpp
new file mode 100644 (file)
index 0000000..d7cc2f9
--- /dev/null
@@ -0,0 +1,21 @@
+// RUN: %clangxx_cfi_dso -DSHARED_LIB %s -fPIC -shared -o %t-so.so
+// RUN: %clangxx_cfi_dso %s -o %t %t-so.so && %expect_crash %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+
+#ifdef SHARED_LIB
+void f() {
+}
+#else
+void f();
+int main() {
+  // CHECK: =1=
+  fprintf(stderr, "=1=\n");
+  ((void (*)(void))f)();
+  // CHECK: =2=
+  fprintf(stderr, "=2=\n");
+  ((void (*)(int))f)(42); // UB here
+  // CHECK-NOT: =3=
+  fprintf(stderr, "=3=\n");
+}
+#endif
diff --git a/src/compiler-rt/test/cfi/cross-dso/icall/lit.local.cfg b/src/compiler-rt/test/cfi/cross-dso/icall/lit.local.cfg
new file mode 100644 (file)
index 0000000..db08765
--- /dev/null
@@ -0,0 +1,3 @@
+# The cfi-icall checker is only supported on x86 and x86_64 for now.
+if config.root.host_arch not in ['x86', 'x86_64']:
+  config.unsupported = True
diff --git a/src/compiler-rt/test/cfi/cross-dso/lit.local.cfg b/src/compiler-rt/test/cfi/cross-dso/lit.local.cfg
new file mode 100644 (file)
index 0000000..57271b8
--- /dev/null
@@ -0,0 +1,9 @@
+def getRoot(config):
+  if not config.parent:
+    return config
+  return getRoot(config.parent)
+
+root = getRoot(config)
+
+if root.host_os not in ['Linux']:
+  config.unsupported = True
diff --git a/src/compiler-rt/test/cfi/cross-dso/simple-fail.cpp b/src/compiler-rt/test/cfi/cross-dso/simple-fail.cpp
new file mode 100644 (file)
index 0000000..64db288
--- /dev/null
@@ -0,0 +1,92 @@
+// RUN: %clangxx_cfi_dso -DSHARED_LIB %s -fPIC -shared -o %t1-so.so
+// RUN: %clangxx_cfi_dso %s -o %t1 %t1-so.so
+// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s
+// RUN: %expect_crash %t1 x 2>&1 | FileCheck --check-prefix=CFI-CAST %s
+
+// RUN: %clangxx_cfi_dso -DB32 -DSHARED_LIB %s -fPIC -shared -o %t2-so.so
+// RUN: %clangxx_cfi_dso -DB32 %s -o %t2 %t2-so.so
+// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
+// RUN: %expect_crash %t2 x 2>&1 | FileCheck --check-prefix=CFI-CAST %s
+
+// RUN: %clangxx_cfi_dso -DB64 -DSHARED_LIB %s -fPIC -shared -o %t3-so.so
+// RUN: %clangxx_cfi_dso -DB64 %s -o %t3 %t3-so.so
+// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s
+// RUN: %expect_crash %t3 x 2>&1 | FileCheck --check-prefix=CFI-CAST %s
+
+// RUN: %clangxx_cfi_dso -DBM -DSHARED_LIB %s -fPIC -shared -o %t4-so.so
+// RUN: %clangxx_cfi_dso -DBM %s -o %t4 %t4-so.so
+// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s
+// RUN: %expect_crash %t4 x 2>&1 | FileCheck --check-prefix=CFI-CAST %s
+
+// RUN: %clangxx -DBM -DSHARED_LIB %s -fPIC -shared -o %t5-so.so
+// RUN: %clangxx -DBM %s -o %t5 %t5-so.so
+// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s
+// RUN: %t5 x 2>&1 | FileCheck --check-prefix=NCFI %s
+
+// RUN: %clangxx -DBM -DSHARED_LIB %s -fPIC -shared -o %t6-so.so
+// RUN: %clangxx_cfi_dso -DBM %s -o %t6 %t6-so.so
+// RUN: %t6 2>&1 | FileCheck --check-prefix=NCFI %s
+// RUN: %t6 x 2>&1 | FileCheck --check-prefix=NCFI %s
+
+// Tests that the CFI mechanism crashes the program when making a virtual call
+// to an object of the wrong class but with a compatible vtable, by casting a
+// pointer to such an object and attempting to make a call through it.
+
+// REQUIRES: cxxabi
+
+#include <stdio.h>
+#include <string.h>
+
+struct A {
+  virtual void f();
+};
+
+void *create_B();
+
+#ifdef SHARED_LIB
+
+#include "../utils.h"
+struct B {
+  virtual void f();
+};
+void B::f() {}
+
+void *create_B() {
+  create_derivers<B>();
+  return (void *)(new B());
+}
+
+#else
+
+void A::f() {}
+
+int main(int argc, char *argv[]) {
+  void *p = create_B();
+  A *a;
+
+  // CFI: =0=
+  // CFI-CAST: =0=
+  // NCFI: =0=
+  fprintf(stderr, "=0=\n");
+
+  if (argc > 1 && argv[1][0] == 'x') {
+    // Test cast. BOOM.
+    a = (A*)p;
+  } else {
+    // Invisible to CFI. Test virtual call later.
+    memcpy(&a, &p, sizeof(a));
+  }
+
+  // CFI: =1=
+  // CFI-CAST-NOT: =1=
+  // NCFI: =1=
+  fprintf(stderr, "=1=\n");
+
+  a->f(); // UB here
+
+  // CFI-NOT: =2=
+  // CFI-CAST-NOT: =2=
+  // NCFI: =2=
+  fprintf(stderr, "=2=\n");
+}
+#endif
diff --git a/src/compiler-rt/test/cfi/cross-dso/simple-pass.cpp b/src/compiler-rt/test/cfi/cross-dso/simple-pass.cpp
new file mode 100644 (file)
index 0000000..42f7a27
--- /dev/null
@@ -0,0 +1,65 @@
+// RUN: %clangxx_cfi_dso -DSHARED_LIB %s -fPIC -shared -o %t1-so.so
+// RUN: %clangxx_cfi_dso %s -o %t1 %t1-so.so
+// RUN: %t1 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi_dso -DB32 -DSHARED_LIB %s -fPIC -shared -o %t2-so.so
+// RUN: %clangxx_cfi_dso -DB32 %s -o %t2 %t2-so.so
+// RUN: %t2 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi_dso -DB64 -DSHARED_LIB %s -fPIC -shared -o %t3-so.so
+// RUN: %clangxx_cfi_dso -DB64 %s -o %t3 %t3-so.so
+// RUN: %t3 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi_dso -DBM -DSHARED_LIB %s -fPIC -shared -o %t4-so.so
+// RUN: %clangxx_cfi_dso -DBM %s -o %t4 %t4-so.so
+// RUN: %t4 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx -DBM -DSHARED_LIB %s -fPIC -shared -o %t5-so.so
+// RUN: %clangxx -DBM %s -o %t5 %t5-so.so
+// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s
+// RUN: %t5 x 2>&1 | FileCheck --check-prefix=NCFI %s
+
+// Tests that the CFI mechanism crashes the program when making a virtual call
+// to an object of the wrong class but with a compatible vtable, by casting a
+// pointer to such an object and attempting to make a call through it.
+
+// REQUIRES: cxxabi
+
+#include <stdio.h>
+#include <string.h>
+
+struct A {
+  virtual void f();
+};
+
+A *create_B();
+
+#ifdef SHARED_LIB
+
+#include "../utils.h"
+struct B : public A {
+  virtual void f();
+};
+void B::f() {}
+
+A *create_B() {
+  create_derivers<B>();
+  return new B();
+}
+
+#else
+
+void A::f() {}
+
+int main(int argc, char *argv[]) {
+  A *a = create_B();
+
+  // CFI: =1=
+  // NCFI: =1=
+  fprintf(stderr, "=1=\n");
+  a->f(); // OK
+  // CFI: =2=
+  // NCFI: =2=
+  fprintf(stderr, "=2=\n");
+}
+#endif
index a9b8992ea1068c3a68771eddfedb949ed92650b9..687c80f4f08dc6c20279f2df734f7399739ac92c 100644 (file)
@@ -10,8 +10,11 @@ clangxx = ' '.join([config.clang] + config.cxx_mode_flags)
 config.substitutions.append((r"%clangxx ", clangxx + ' '))
 if config.lto_supported:
   clangxx_cfi = ' '.join(config.lto_launch + [clangxx] + config.lto_flags + ['-flto -fsanitize=cfi '])
+  clangxx_cfi_diag = clangxx_cfi + '-fno-sanitize-trap=cfi -fsanitize-recover=cfi '
   config.substitutions.append((r"%clangxx_cfi ", clangxx_cfi))
-  config.substitutions.append((r"%clangxx_cfi_diag ", clangxx_cfi + '-fno-sanitize-trap=cfi -fsanitize-recover=cfi '))
+  config.substitutions.append((r"%clangxx_cfi_diag ", clangxx_cfi_diag))
+  config.substitutions.append((r"%clangxx_cfi_dso ", clangxx_cfi + '-fsanitize-cfi-cross-dso '))
+  config.substitutions.append((r"%clangxx_cfi_dso_diag ", clangxx_cfi_diag + '-fsanitize-cfi-cross-dso '))
 else:
   config.unsupported = True
 
diff --git a/src/compiler-rt/test/lsan/TestCases/cleanup_in_tsd_destructor.c b/src/compiler-rt/test/lsan/TestCases/cleanup_in_tsd_destructor.c
new file mode 100644 (file)
index 0000000..debf05c
--- /dev/null
@@ -0,0 +1,45 @@
+// Regression test for thread lifetime tracking. Thread data should be
+// considered live during the thread's termination, at least until the
+// user-installed TSD destructors have finished running (since they may contain
+// additional cleanup tasks). LSan doesn't actually meet that goal 100%, but it
+// makes its best effort.
+// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0"
+// RUN: %clang_lsan %s -o %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %run %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %run %t 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sanitizer/lsan_interface.h"
+
+pthread_key_t key;
+__thread void *p;
+
+void key_destructor(void *arg) {
+  // Generally this may happen on a different thread.
+  __lsan_do_leak_check();
+}
+
+void *thread_func(void *arg) {
+  p = malloc(1337);
+  fprintf(stderr, "Test alloc: %p.\n", p);
+  int res = pthread_setspecific(key, (void*)1);
+  assert(res == 0);
+  return 0;
+}
+
+int main() {
+  int res = pthread_key_create(&key, &key_destructor);
+  assert(res == 0);
+  pthread_t thread_id;
+  res = pthread_create(&thread_id, 0, thread_func, 0);
+  assert(res == 0);
+  res = pthread_join(thread_id, 0);
+  assert(res == 0);
+  return 0;
+}
+// CHECK: Test alloc: [[ADDR:.*]].
+// CHECK: [[ADDR]] (1337 bytes)
diff --git a/src/compiler-rt/test/lsan/TestCases/cleanup_in_tsd_destructor.cc b/src/compiler-rt/test/lsan/TestCases/cleanup_in_tsd_destructor.cc
deleted file mode 100644 (file)
index 5335454..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-// Regression test for thread lifetime tracking. Thread data should be
-// considered live during the thread's termination, at least until the
-// user-installed TSD destructors have finished running (since they may contain
-// additional cleanup tasks). LSan doesn't actually meet that goal 100%, but it
-// makes its best effort.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %run %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %run %t 2>&1 | FileCheck %s
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-pthread_key_t key;
-__thread void *p;
-
-void key_destructor(void *arg) {
-  // Generally this may happen on a different thread.
-  __lsan_do_leak_check();
-}
-
-void *thread_func(void *arg) {
-  p = malloc(1337);
-  fprintf(stderr, "Test alloc: %p.\n", p);
-  int res = pthread_setspecific(key, (void*)1);
-  assert(res == 0);
-  return 0;
-}
-
-int main() {
-  int res = pthread_key_create(&key, &key_destructor);
-  assert(res == 0);
-  pthread_t thread_id;
-  res = pthread_create(&thread_id, 0, thread_func, 0);
-  assert(res == 0);
-  res = pthread_join(thread_id, 0);
-  assert(res == 0);
-  return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: [[ADDR]] (1337 bytes)
diff --git a/src/compiler-rt/test/lsan/TestCases/disabler.c b/src/compiler-rt/test/lsan/TestCases/disabler.c
new file mode 100644 (file)
index 0000000..1c4529d
--- /dev/null
@@ -0,0 +1,24 @@
+// Test for __lsan_disable() / __lsan_enable().
+// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0"
+// RUN: %clang_lsan %s -o %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sanitizer/lsan_interface.h"
+
+int main() {
+  void **p;
+  {
+    __lsan_disable();
+    p = malloc(sizeof(void *));
+    __lsan_enable();
+  }
+  *p = malloc(666);
+  void *q = malloc(1337);
+  // Break optimization.
+  fprintf(stderr, "Test alloc: %p.\n", q);
+  return 0;
+}
+// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)
index f83106501fa38c1d85e11c3ca0ee6e7d38459873..12e5ffe4d44a744c814766e655e0ff909f5058cd 100644 (file)
@@ -13,11 +13,13 @@ int main() {
   {
     __lsan::ScopedDisabler d;
     p = new void *;
+    fprintf(stderr, "Test alloc p: %p.\n", p);
   }
-  *reinterpret_cast<void **>(p) = malloc(666);
+  *p = malloc(666);
   void *q = malloc(1337);
-  // Break optimization.
-  fprintf(stderr, "Test alloc: %p.\n", q);
+  fprintf(stderr, "Test alloc q: %p.\n", q);
   return 0;
 }
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)
+
+// CHECK: Test alloc p: [[ADDR:.*]].
+// CHECK-NOT: [[ADDR]]
diff --git a/src/compiler-rt/test/lsan/TestCases/disabler_in_tsd_destructor.c b/src/compiler-rt/test/lsan/TestCases/disabler_in_tsd_destructor.c
new file mode 100644 (file)
index 0000000..982fb89
--- /dev/null
@@ -0,0 +1,39 @@
+// Regression test. Disabler should not depend on TSD validity.
+// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=1"
+// RUN: %clang_lsan %s -o %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sanitizer/lsan_interface.h"
+
+pthread_key_t key;
+
+void key_destructor(void *arg) {
+  __lsan_disable();
+  void *p = malloc(1337);
+  // Break optimization.
+  fprintf(stderr, "Test alloc: %p.\n", p);
+  pthread_setspecific(key, 0);
+  __lsan_enable();
+}
+
+void *thread_func(void *arg) {
+  int res = pthread_setspecific(key, (void*)1);
+  assert(res == 0);
+  return 0;
+}
+
+int main() {
+  int res = pthread_key_create(&key, &key_destructor);
+  assert(res == 0);
+  pthread_t thread_id;
+  res = pthread_create(&thread_id, 0, thread_func, 0);
+  assert(res == 0);
+  res = pthread_join(thread_id, 0);
+  assert(res == 0);
+  return 0;
+}
diff --git a/src/compiler-rt/test/lsan/TestCases/disabler_in_tsd_destructor.cc b/src/compiler-rt/test/lsan/TestCases/disabler_in_tsd_destructor.cc
deleted file mode 100644 (file)
index a0012c7..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-// Regression test. Disabler should not depend on TSD validity.
-// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=1"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t
-
-#include <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-pthread_key_t key;
-
-void key_destructor(void *arg) {
-  __lsan::ScopedDisabler d;
-  void *p = malloc(1337);
-  // Break optimization.
-  fprintf(stderr, "Test alloc: %p.\n", p);
-  pthread_setspecific(key, 0);
-}
-
-void *thread_func(void *arg) {
-  int res = pthread_setspecific(key, (void*)1);
-  assert(res == 0);
-  return 0;
-}
-
-int main() {
-  int res = pthread_key_create(&key, &key_destructor);
-  assert(res == 0);
-  pthread_t thread_id;
-  res = pthread_create(&thread_id, 0, thread_func, 0);
-  assert(res == 0);
-  res = pthread_join(thread_id, 0);
-  assert(res == 0);
-  return 0;
-}
diff --git a/src/compiler-rt/test/lsan/TestCases/ignore_object.c b/src/compiler-rt/test/lsan/TestCases/ignore_object.c
new file mode 100644 (file)
index 0000000..2aa4f14
--- /dev/null
@@ -0,0 +1,23 @@
+// Test for __lsan_ignore_object().
+// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0"
+// RUN: %clang_lsan %s -o %t
+// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sanitizer/lsan_interface.h"
+
+int main() {
+  // Explicitly ignored object.
+  void **p = malloc(sizeof(void *));
+  // Transitively ignored object.
+  *p = malloc(666);
+  // Non-ignored object.
+  volatile void *q = malloc(1337);
+  fprintf(stderr, "Test alloc: %p.\n", p);
+  __lsan_ignore_object(p);
+  return 0;
+}
+// CHECK: Test alloc: [[ADDR:.*]].
+// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)
diff --git a/src/compiler-rt/test/lsan/TestCases/ignore_object.cc b/src/compiler-rt/test/lsan/TestCases/ignore_object.cc
deleted file mode 100644 (file)
index ac69e12..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-// Test for __lsan_ignore_object().
-// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0:use_tls=0"
-// RUN: %clangxx_lsan %s -o %t
-// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sanitizer/lsan_interface.h"
-
-int main() {
-  // Explicitly ignored object.
-  void **p = new void *;
-  // Transitively ignored object.
-  *p = malloc(666);
-  // Non-ignored object.
-  volatile void *q = malloc(1337);
-  fprintf(stderr, "Test alloc: %p.\n", p);
-  __lsan_ignore_object(p);
-  return 0;
-}
-// CHECK: Test alloc: [[ADDR:.*]].
-// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte(s) leaked in 1 allocation(s)
diff --git a/src/compiler-rt/test/msan/ctermid.cc b/src/compiler-rt/test/msan/ctermid.cc
new file mode 100644 (file)
index 0000000..a2818e6
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: %clangxx_msan -std=c++11 -O0 %s -o %t && %run %t
+
+#include <sanitizer/msan_interface.h>
+#include <stdio.h>
+#include <string.h>
+
+int main(void) {
+  unsigned char s[L_ctermid + 1];
+  char *res = ctermid((char *)s);
+  if (res)
+    printf("%zd\n", strlen(res));
+  return 0;
+}
diff --git a/src/compiler-rt/test/msan/dlopen_executable.cc b/src/compiler-rt/test/msan/dlopen_executable.cc
new file mode 100644 (file)
index 0000000..ac8a14b
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: %clangxx_msan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+static int my_global;
+
+int main(void) {
+  int *uninit = (int*)malloc(sizeof(int));
+  my_global = *uninit;
+  void *p = dlopen(0, RTLD_NOW);
+  assert(p && "failed to get handle to executable");
+  return my_global;
+  // CHECK: MemorySanitizer: use-of-uninitialized-value
+  // CHECK: #0 {{.*}} in main{{.*}}dlopen_executable.cc:[[@LINE-2]]
+}
index 78a62d549ec3ce727863564249d4e6541d81f5e1..38c3616ec16405e448f829d9b2152405327f7045 100644 (file)
@@ -4,6 +4,11 @@
 
 // RUN: %clangxx_msan -std=c++11 -fsanitize-memory-track-origins=2 -g -O3 %s -o %t
 // RUN: MSAN_OPTIONS=store_context_size=1000,origin_history_size=0,origin_history_per_stack_limit=0 %run %t |& FileCheck %s
+//
+// Big-endian mips64 currently hangs on this test. Mark it unsupported to allow
+// llvm-lit to finish. This also marks mips unsupported in most cases but msan
+// is already unsupported for 32-bit mips.
+// UNSUPPORTED: mips64-supported-target
 
 // Fun fact: if test output is redirected to a file (as opposed to
 // being piped directly to FileCheck), we may lose some "done"s due to
index 96d27f08937e00b08bb1dff1c01f9a0f70126ed9..a0c70023f2f6fd1b6e127f2bc5a2625a442c9955 100644 (file)
@@ -4,7 +4,6 @@
 // RUN: FileCheck %s < %t.out && FileCheck %s < %t.out
 
 // Test origin propagation through insertvalue IR instruction.
-// REQUIRES: stable-runtime
 
 #include <stdio.h>
 #include <stdint.h>
index d25bf820ffa0489719745e9804f6d3a17a5a9b7a..5bc6f59213b14db359f4055b012d5dd173718d0b 100644 (file)
@@ -1,8 +1,5 @@
 // RUN: %clangxx_msan -std=c++11 -O0 %s -o %t && %run %t
 //
-// AArch64 lacks var args instrumentation.
-// XFAIL: aarch64
-
 // Test that va_arg shadow from a signal handler does not leak outside.
 
 #include <signal.h>
diff --git a/src/compiler-rt/test/profile/Inputs/instrprof-shared-lib.c b/src/compiler-rt/test/profile/Inputs/instrprof-shared-lib.c
new file mode 100644 (file)
index 0000000..d22b0a5
--- /dev/null
@@ -0,0 +1,9 @@
+int g1 = 0;
+int g2 = 1;
+
+void foo(int n) {
+  if (n % 5 == 0)
+    g1++;
+  else
+    g2++;
+}
diff --git a/src/compiler-rt/test/profile/Inputs/instrprof-shared-main.c b/src/compiler-rt/test/profile/Inputs/instrprof-shared-main.c
new file mode 100644 (file)
index 0000000..60da3b4
--- /dev/null
@@ -0,0 +1,13 @@
+extern int g1, g2;
+extern void foo(int n);
+
+int main() {
+  int i, j;
+  for (i = 0; i < 1000; i++)
+    for (j = 0; j < 1000; j++)
+      foo(i * j);
+
+  if (g2 - g1 == 280001)
+    return 0;
+  return 1;
+}
diff --git a/src/compiler-rt/test/profile/Linux/instrprof-basic.c b/src/compiler-rt/test/profile/Linux/instrprof-basic.c
new file mode 100644 (file)
index 0000000..7ae683d
--- /dev/null
@@ -0,0 +1,31 @@
+// RUN: %clang_profgen -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections -o %t -O3 %s
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t
+// RUN: llvm-profdata merge -o %t.profdata %t.profraw
+// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s
+
+int begin(int i) {
+  // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]]
+  if (i)
+    return 0;
+  return 1;
+}
+
+int end(int i) {
+  // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD2:[0-9]+]]
+  if (i)
+    return 0;
+  return 1;
+}
+
+int main(int argc, const char *argv[]) {
+  begin(0);
+  end(1);
+
+  // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD2:[0-9]+]]
+  if (argc)
+    return 0;
+  return 1;
+}
+
+// CHECK: ![[PD1]] = !{!"branch_weights", i32 1, i32 2}
+// CHECK: ![[PD2]] = !{!"branch_weights", i32 2, i32 1}
diff --git a/src/compiler-rt/test/profile/Linux/instrprof-dlopen.test b/src/compiler-rt/test/profile/Linux/instrprof-dlopen.test
new file mode 100644 (file)
index 0000000..618367c
--- /dev/null
@@ -0,0 +1,34 @@
+RUN: mkdir -p %t.d
+RUN: %clang_profgen -o %t.d/func.shared -fPIC -shared -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections  %S/../Inputs/instrprof-dlopen-func.c
+RUN: %clang_profgen -o %t.d/func2.shared -fPIC -shared -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections  %S/../Inputs/instrprof-dlopen-func2.c
+RUN: %clang -o %t-local -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS="RTLD_LAZY | RTLD_LOCAL" %S/../Inputs/instrprof-dlopen-main.c
+RUN: %clang -o %t-global -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS="RTLD_LAZY | RTLD_GLOBAL" %S/../Inputs/instrprof-dlopen-main.c
+
+RUN: %clang -c -o %t.d/main.o %S/../Inputs/instrprof-dlopen-main.c
+RUN: %clang_profgen -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections  -o %t-static %S/../Inputs/instrprof-dlopen-func.c %S/../Inputs/instrprof-dlopen-func2.c %t.d/main.o
+
+RUN: env LLVM_PROFILE_FILE=%t-static.profraw %run %t-static
+RUN: env LLVM_PROFILE_FILE=%t-local.profraw %run %t-local
+RUN: env LLVM_PROFILE_FILE=%t-global.profraw %run %t-global
+
+RUN: llvm-profdata merge -o %t-static.profdata %t-static.profraw
+RUN: llvm-profdata merge -o %t-local.profdata %t-local.profraw
+RUN: llvm-profdata merge -o %t-global.profdata %t-global.profraw
+
+RUN: %clang_profuse=%t-static.profdata -o %t-func.static.ll -S -emit-llvm %S/../Inputs/instrprof-dlopen-func.c
+RUN: %clang_profuse=%t-local.profdata -o %t-func.local.ll -S -emit-llvm %S/../Inputs/instrprof-dlopen-func.c
+RUN: %clang_profuse=%t-global.profdata -o %t-func.global.ll -S -emit-llvm %S/../Inputs/instrprof-dlopen-func.c
+RUN: diff %t-func.static.ll %t-func.local.ll
+RUN: diff %t-func.static.ll %t-func.global.ll
+
+RUN: %clang_profuse=%t-static.profdata -o %t-func2.static.ll -S -emit-llvm %S/../Inputs/instrprof-dlopen-func2.c
+RUN: %clang_profuse=%t-local.profdata -o %t-func2.local.ll -S -emit-llvm %S/../Inputs/instrprof-dlopen-func2.c
+RUN: %clang_profuse=%t-global.profdata -o %t-func2.global.ll -S -emit-llvm %S/../Inputs/instrprof-dlopen-func2.c
+RUN: diff %t-func2.static.ll %t-func2.local.ll
+RUN: diff %t-func2.static.ll %t-func2.global.ll
+
+RUN: %clang_profuse=%t-static.profdata -o %t-main.static.ll -S -emit-llvm %S/../Inputs/instrprof-dlopen-main.c
+RUN: %clang_profuse=%t-local.profdata -o %t-main.local.ll -S -emit-llvm %S/../Inputs/instrprof-dlopen-main.c
+RUN: %clang_profuse=%t-local.profdata -o %t-main.global.ll -S -emit-llvm %S/../Inputs/instrprof-dlopen-main.c
+RUN: diff %t-main.static.ll %t-main.local.ll
+RUN: diff %t-main.static.ll %t-main.global.ll
diff --git a/src/compiler-rt/test/profile/Linux/instrprof-dynamic-one-shared.test b/src/compiler-rt/test/profile/Linux/instrprof-dynamic-one-shared.test
new file mode 100644 (file)
index 0000000..52f40bf
--- /dev/null
@@ -0,0 +1,23 @@
+RUN: mkdir -p %t.d
+RUN: %clang_profgen -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections -o %t.d/a.shared -fPIC -shared %S/../Inputs/instrprof-dynamic-a.cpp
+RUN: %clang_profgen -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections -o %t-shared -fPIC -rpath %t.d %t.d/a.shared %S/../Inputs/instrprof-dynamic-b.cpp %S/../Inputs/instrprof-dynamic-main.cpp
+
+RUN: %clang_profgen -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections -o %t-static %S/../Inputs/instrprof-dynamic-a.cpp %S/../Inputs/instrprof-dynamic-b.cpp %S/../Inputs/instrprof-dynamic-main.cpp
+
+RUN: env LLVM_PROFILE_FILE=%t-static.profraw %run %t-static
+RUN: env LLVM_PROFILE_FILE=%t-shared.profraw %run %t-shared
+
+RUN: llvm-profdata merge -o %t-static.profdata %t-static.profraw
+RUN: llvm-profdata merge -o %t-shared.profdata %t-shared.profraw
+
+RUN: %clang_profuse=%t-static.profdata -o %t-a.static.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-a.cpp
+RUN: %clang_profuse=%t-shared.profdata -o %t-a.shared.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-a.cpp
+RUN: diff %t-a.static.ll %t-a.shared.ll
+
+RUN: %clang_profuse=%t-static.profdata -o %t-b.static.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-b.cpp
+RUN: %clang_profuse=%t-shared.profdata -o %t-b.shared.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-b.cpp
+RUN: diff %t-b.static.ll %t-b.shared.ll
+
+RUN: %clang_profuse=%t-static.profdata -o %t-main.static.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-main.cpp
+RUN: %clang_profuse=%t-shared.profdata -o %t-main.shared.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-main.cpp
+RUN: diff %t-main.static.ll %t-main.shared.ll
diff --git a/src/compiler-rt/test/profile/Linux/instrprof-dynamic-two-shared.test b/src/compiler-rt/test/profile/Linux/instrprof-dynamic-two-shared.test
new file mode 100644 (file)
index 0000000..9499146
--- /dev/null
@@ -0,0 +1,24 @@
+RUN: mkdir -p %t.d
+RUN: %clang_profgen -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections -o %t.d/a.shared -fPIC -shared %S/../Inputs/instrprof-dynamic-a.cpp
+RUN: %clang_profgen -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections -o %t.d/b.shared -fPIC -shared %S/../Inputs/instrprof-dynamic-b.cpp
+RUN: %clang_profgen -fdata-sections -ffunction-sections -fuse-ld=gold -Wl,--gc-sections -o %t-shared -fPIC -rpath %t.d %t.d/a.shared %t.d/b.shared %S/../Inputs/instrprof-dynamic-main.cpp
+
+RUN: %clang_profgen -o %t-static %S/../Inputs/instrprof-dynamic-a.cpp %S/../Inputs/instrprof-dynamic-b.cpp %S/../Inputs/instrprof-dynamic-main.cpp
+
+RUN: env LLVM_PROFILE_FILE=%t-static.profraw %run %t-static
+RUN: env LLVM_PROFILE_FILE=%t-shared.profraw %run %t-shared
+
+RUN: llvm-profdata merge -o %t-static.profdata %t-static.profraw
+RUN: llvm-profdata merge -o %t-shared.profdata %t-shared.profraw
+
+RUN: %clang_profuse=%t-static.profdata -o %t-a.static.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-a.cpp
+RUN: %clang_profuse=%t-shared.profdata -o %t-a.shared.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-a.cpp
+RUN: diff %t-a.static.ll %t-a.shared.ll
+
+RUN: %clang_profuse=%t-static.profdata -o %t-b.static.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-b.cpp
+RUN: %clang_profuse=%t-shared.profdata -o %t-b.shared.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-b.cpp
+RUN: diff %t-b.static.ll %t-b.shared.ll
+
+RUN: %clang_profuse=%t-static.profdata -o %t-main.static.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-main.cpp
+RUN: %clang_profuse=%t-shared.profdata -o %t-main.shared.ll -S -emit-llvm %S/../Inputs/instrprof-dynamic-main.cpp
+RUN: diff %t-main.static.ll %t-main.shared.ll
diff --git a/src/compiler-rt/test/profile/Linux/lit.local.cfg b/src/compiler-rt/test/profile/Linux/lit.local.cfg
new file mode 100644 (file)
index 0000000..c8c79fc
--- /dev/null
@@ -0,0 +1,37 @@
+import subprocess
+
+def getRoot(config):
+  if not config.parent:
+    return config
+  return getRoot(config.parent)
+
+
+def is_gold_linker_available():
+
+  if not config.gold_executable:
+    return False
+  try:
+    ld_cmd = subprocess.Popen([config.gold_executable, '--help'], stdout = subprocess.PIPE)
+    ld_out = ld_cmd.stdout.read().decode()
+    ld_cmd.wait()
+  except:
+    return False
+
+  if not '-plugin' in ld_out:
+    return False
+
+  clang_cmd = subprocess.Popen([config.clang, '-fuse-ld=gold', '-xc', '-'],
+                               stdin = subprocess.PIPE,
+                               stdout = subprocess.PIPE,
+                               stderr = subprocess.PIPE)
+  clang_err = clang_cmd.communicate('int main() { return 0; }')[1]
+
+  if not 'invalid linker' in clang_err:
+    return True
+
+  return False
+
+root = getRoot(config)
+
+if root.host_os not in ['Linux'] or not is_gold_linker_available():
+  config.unsupported = True
diff --git a/src/compiler-rt/test/profile/instrprof-bufferio.c b/src/compiler-rt/test/profile/instrprof-bufferio.c
new file mode 100644 (file)
index 0000000..eed548f
--- /dev/null
@@ -0,0 +1,128 @@
+// RUN: %clang_profgen -O3 -o %t %s
+// RUN: %run %t %t.out.1 %t.out.2 %t.out.3 %t.out.4
+// RUN: cat %t.out.1 | FileCheck %s
+// RUN: diff %t.out.1 %t.out.2
+// RUN: diff %t.out.2 %t.out.3
+// RUN: diff %t.out.3 %t.out.4
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct ProfBufferIO ProfBufferIO;
+ProfBufferIO *llvmCreateBufferIOInternal(FILE *File, uint32_t DefaultBufferSz);
+void llvmDeleteBufferIO(ProfBufferIO *BufferIO);
+
+int llvmBufferIOWrite(ProfBufferIO *BufferIO, const char *Data, uint32_t Size);
+int llvmBufferIOFlush(ProfBufferIO *BufferIO);
+
+int __llvm_profile_runtime = 0;
+
+const char *SmallData = "ABC\n";
+const char *MediumData =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n";
+char LargeData[10 * 1024];
+int main(int argc, const char *argv[]) {
+  ProfBufferIO *BufferIO;
+  FILE *File[4];
+  uint32_t IOBufferSize[4] = {8, 128, 8 * 1024, 11 * 1024};
+  int I, J;
+  if (argc < 5)
+    return 1;
+
+  for (I = 0; I < 10 * 1024 - 2; I++)
+    LargeData[I] = 'A';
+
+  LargeData[I++] = '\n';
+  LargeData[I++] = '\0';
+
+  for (J = 0; J < 4; J++) {
+    File[J] = fopen(argv[1 + J], "w");
+    if (!File[J])
+      return 1;
+
+    BufferIO = llvmCreateBufferIOInternal(File[J], IOBufferSize[J]);
+
+    llvmBufferIOWrite(BufferIO, "Short Strings:\n", strlen("Short Strings:\n"));
+    for (I = 0; I < 1024; I++) {
+      llvmBufferIOWrite(BufferIO, SmallData, strlen(SmallData));
+    }
+    llvmBufferIOWrite(BufferIO, "Long Strings:\n", strlen("Long Strings:\n"));
+    for (I = 0; I < 1024; I++) {
+      llvmBufferIOWrite(BufferIO, MediumData, strlen(MediumData));
+    }
+    llvmBufferIOWrite(BufferIO, "Extra Long Strings:\n",
+                      strlen("Extra Long Strings:\n"));
+    for (I = 0; I < 10; I++) {
+      llvmBufferIOWrite(BufferIO, LargeData, strlen(LargeData));
+    }
+    llvmBufferIOWrite(BufferIO, "Mixed Strings:\n", strlen("Mixed Strings:\n"));
+    for (I = 0; I < 1024; I++) {
+      llvmBufferIOWrite(BufferIO, MediumData, strlen(MediumData));
+      llvmBufferIOWrite(BufferIO, SmallData, strlen(SmallData));
+    }
+    llvmBufferIOWrite(BufferIO, "Endings:\n", strlen("Endings:\n"));
+    llvmBufferIOWrite(BufferIO, "END\n", strlen("END\n"));
+    llvmBufferIOWrite(BufferIO, "ENDEND\n", strlen("ENDEND\n"));
+    llvmBufferIOWrite(BufferIO, "ENDENDEND\n", strlen("ENDENDEND\n"));
+    llvmBufferIOWrite(BufferIO, "ENDENDENDEND\n", strlen("ENDENDENDEND\n"));
+    llvmBufferIOFlush(BufferIO);
+
+    llvmDeleteBufferIO(BufferIO);
+
+    fclose(File[J]);
+  }
+  return 0;
+}
+
+// CHECK-LABEL: Short Strings:
+// CHECK: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABC
+// CHECK-LABEL: Long Strings:
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-LABEL: Mixed Strings:
+// CHECK: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABC
+// CHECK-NEXT: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+// CHECK-NEXT: ABC
+// CHECK-LABEL: Endings:
+// CHECK: END
+// CHECK-NEXT: ENDEND
+// CHECK-NEXT: ENDENDEND
+// CHECK-NEXT: ENDENDENDEND
diff --git a/src/compiler-rt/test/profile/instrprof-error.c b/src/compiler-rt/test/profile/instrprof-error.c
new file mode 100644 (file)
index 0000000..4386d53
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: %clang_profgen -o %t -O3 %s
+// RUN: touch %t.profraw
+// RUN: chmod -w %t.profraw
+// RUN: LLVM_PROFILE_FILE=%t.profraw LLVM_PROFILE_VERBOSE_ERRORS=1 %run %t 1 2>&1 | FileCheck %s
+// RUN: chmod +w %t.profraw
+
+int main(int argc, const char *argv[]) {
+  if (argc < 2)
+    return 1;
+  return 0;
+}
+// CHECK: LLVM Profile: Failed to write file 
diff --git a/src/compiler-rt/test/profile/instrprof-shared.test b/src/compiler-rt/test/profile/instrprof-shared.test
new file mode 100644 (file)
index 0000000..b3f0b9a
--- /dev/null
@@ -0,0 +1,75 @@
+"""
+This test produces three shared libraries:
+
+1. libt-instr.so is instrumented
+2. libt-no-instr1.so is not instrumented
+3. libt-no-instr2.so is built with profile rt linked in (via -u<hook>), but the object file is built
+   with instrumentation turned off.
+
+After the libraries are built, the main program is then built with/without instrumentation and linked
+against 3 libraries above.
+
+The test is to verify that programs linked against these shared objects with and without instrumentation
+enabled behave as expected.
+"""
+
+RUN: mkdir -p %t.d
+RUN: %clang_profgen -o %t.d/libt-instr.so -fPIC -shared %S/Inputs/instrprof-shared-lib.c
+RUN: %clang -o %t.d/libt-no-instr1.so -fPIC -shared %S/Inputs/instrprof-shared-lib.c
+RUN: %clang -c -o %t.d/instrprof-shared-lib-no-instr2.o -fPIC  %S/Inputs/instrprof-shared-lib.c
+RUN: %clang_profgen -o %t.d/libt-no-instr2.so -fPIC -shared %t.d/instrprof-shared-lib-no-instr2.o
+
+RUN: %clang_profgen -o %t-instr-instr -L%t.d -rpath %t.d -lt-instr  %S/Inputs/instrprof-shared-main.c
+RUN: %clang_profgen -o %t-instr-no-instr1 -L%t.d -rpath %t.d -lt-no-instr1  %S/Inputs/instrprof-shared-main.c
+RUN: %clang_profgen -o %t-instr-no-instr2 -L%t.d -rpath %t.d -lt-no-instr2  %S/Inputs/instrprof-shared-main.c
+RUN: %clang -o %t-no-instr1-instr -L%t.d -rpath %t.d -lt-instr  %S/Inputs/instrprof-shared-main.c
+RUN: %clang -o %t-no-instr1-no-instr1 -L%t.d -rpath %t.d -lt-no-instr1  %S/Inputs/instrprof-shared-main.c
+RUN: %clang -o %t-no-instr1-no-instr2 -L%t.d -rpath %t.d -lt-no-instr2  %S/Inputs/instrprof-shared-main.c
+RUN: %clang -c -o %t.d/instrprof-shared-main-no-instr2.o  %S/Inputs/instrprof-shared-main.c
+RUN: %clang -o %t-no-instr2-instr -L%t.d -rpath %t.d -lt-instr  %t.d/instrprof-shared-main-no-instr2.o
+RUN: %clang -o %t-no-instr2-no-instr1 -L%t.d -rpath %t.d -lt-no-instr1  %t.d/instrprof-shared-main-no-instr2.o
+RUN: %clang -o %t-no-instr2-no-instr2 -L%t.d -rpath %t.d -lt-no-instr2  %t.d/instrprof-shared-main-no-instr2.o
+
+RUN: env LLVM_PROFILE_FILE=%t-instr-instr.profraw %run %t-instr-instr
+RUN: env LLVM_PROFILE_FILE=%t-instr-no-instr1.profraw %run %t-instr-no-instr1
+RUN: env LLVM_PROFILE_FILE=%t-instr-no-instr2.profraw %run %t-instr-no-instr2
+RUN: env LLVM_PROFILE_FILE=%t-no-instr1-instr.profraw %run %t-no-instr1-instr
+RUN: env LLVM_PROFILE_FILE=%t-no-instr2-instr.profraw %run %t-no-instr2-instr
+RUN: env LLVM_PROFILE_FILE=%t-no-instr1-no-instr1.profraw %run %t-no-instr1-no-instr1
+RUN: env LLVM_PROFILE_FILE=%t-no-instr1-no-instr2.profraw %run %t-no-instr1-no-instr2
+RUN: env LLVM_PROFILE_FILE=%t-no-instr2-no-instr1.profraw %run %t-no-instr2-no-instr1
+RUN: env LLVM_PROFILE_FILE=%t-no-instr2-no-instr2.profraw %run %t-no-instr2-no-instr2
+
+RUN: llvm-profdata merge -o %t-instr-instr.profdata %t-instr-instr.profraw
+RUN: llvm-profdata merge -o %t-instr-no-instr1.profdata %t-instr-no-instr1.profraw
+RUN: llvm-profdata merge -o %t-instr-no-instr2.profdata %t-instr-no-instr2.profraw
+RUN: llvm-profdata merge -o %t-no-instr1-instr.profdata %t-no-instr1-instr.profraw
+RUN: llvm-profdata merge -o %t-no-instr2-instr.profdata %t-no-instr2-instr.profraw
+
+RUN: not llvm-profdata merge -o %t-no-instr1-no-instr1.profdata %t-no-instr1-no-instr1.profraw 2>&1 | FileCheck %s --check-prefix=MISSING-FILE
+RUN: not llvm-profdata merge -o %t-no-instr2-no-instr1.profdata %t-no-instr2-no-instr1.profraw 2>&1 | FileCheck %s --check-prefix=MISSING-FILE
+MISSING-FILE: profraw
+
+RUN: llvm-profdata show -counts --function main %t-instr-instr.profdata | grep -v 'Total\|Maximum' > %t-main-1
+RUN: llvm-profdata show -counts --function main %t-instr-no-instr1.profdata | grep -v 'Total\|Maximum' > %t-main-2
+RUN: llvm-profdata show -counts --function main %t-instr-no-instr2.profdata | grep -v 'Total\|Maximum' > %t-main-3
+RUN: llvm-profdata show -counts --function foo %t-instr-instr.profdata | grep -v 'Total\|Maximum' > %t-foo-1
+RUN: llvm-profdata show -counts --function foo %t-no-instr1-instr.profdata | grep -v 'Total\|Maximum' > %t-foo-2
+RUN: llvm-profdata show -counts --function foo %t-no-instr2-instr.profdata | grep -v 'Total\|Maximum'  > %t-foo-3
+
+RUN: %clang_profuse=%t-instr-instr.profdata -o %t-main-instr-instr.ll -S -emit-llvm %S/Inputs/instrprof-shared-main.c
+RUN: %clang_profuse=%t-instr-no-instr1.profdata -o %t-main-instr-no-instr1.ll -S -emit-llvm %S/Inputs/instrprof-shared-main.c
+RUN: %clang_profuse=%t-instr-no-instr2.profdata -o %t-main-instr-no-instr2.ll -S -emit-llvm %S/Inputs/instrprof-shared-main.c
+RUN: %clang_profuse=%t-instr-instr.profdata -o %t-lib-instr-instr.ll -S -emit-llvm %S/Inputs/instrprof-shared-lib.c
+RUN: %clang_profuse=%t-no-instr1-instr.profdata -o %t-lib-no-instr1-instr.ll -S -emit-llvm %S/Inputs/instrprof-shared-lib.c
+RUN: %clang_profuse=%t-no-instr2-instr.profdata -o %t-lib-no-instr2-instr.ll -S -emit-llvm %S/Inputs/instrprof-shared-lib.c
+RUN: %clang_profuse=%t-instr-instr.profdata -o %t-lib-instr-instr.ll -S -emit-llvm %S/Inputs/instrprof-shared-lib.c
+
+RUN: diff %t-main-instr-no-instr1.ll %t-main-instr-no-instr2.ll
+RUN: diff %t-lib-no-instr1-instr.ll %t-lib-no-instr2-instr.ll
+
+RUN: diff %t-main-1 %t-main-2
+RUN: diff %t-main-1 %t-main-3
+RUN: diff %t-foo-1 %t-foo-2
+RUN: diff %t-foo-1 %t-foo-3
+
diff --git a/src/compiler-rt/test/profile/instrprof-value-prof-2.c b/src/compiler-rt/test/profile/instrprof-value-prof-2.c
new file mode 100644 (file)
index 0000000..989353e
--- /dev/null
@@ -0,0 +1,135 @@
+// RUN: %clang_profgen -O2 -o %t %s
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t
+// RUN: llvm-profdata merge -o %t.profdata %t.profraw
+// RUN: llvm-profdata show --all-functions -ic-targets  %t.profdata |  FileCheck  %s 
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+typedef struct __llvm_profile_data __llvm_profile_data;
+const __llvm_profile_data *__llvm_profile_begin_data(void);
+const __llvm_profile_data *__llvm_profile_end_data(void);
+void __llvm_profile_set_num_value_sites(__llvm_profile_data *Data,
+                                        uint32_t ValueKind,
+                                        uint16_t NumValueSites);
+__llvm_profile_data *
+__llvm_profile_iterate_data(const __llvm_profile_data *Data);
+void *__llvm_get_function_addr(const __llvm_profile_data *Data);
+void __llvm_profile_instrument_target(uint64_t TargetValue, void *Data,
+                                      uint32_t CounterIndex);
+void callee1() {}
+void callee2() {}
+
+void caller_without_value_site1() {}
+void caller_with_value_site_never_called1() {}
+void caller_with_vp1() {}
+void caller_with_value_site_never_called2() {}
+void caller_without_value_site2() {}
+void caller_with_vp2() {}
+
+int main(int argc, const char *argv[]) {
+  unsigned S, NS = 10, V;
+  const __llvm_profile_data *Data, *DataEnd;
+
+  Data = __llvm_profile_begin_data();
+  DataEnd = __llvm_profile_end_data();
+  for (; Data < DataEnd; Data = __llvm_profile_iterate_data(Data)) {
+    void *func = __llvm_get_function_addr(Data);
+    if (func == caller_without_value_site1 ||
+        func == caller_without_value_site2 ||
+        func == callee1 || func == callee2 || func == main)
+      continue;
+
+    __llvm_profile_set_num_value_sites((__llvm_profile_data *)Data,
+                                       0 /*IPVK_IndirectCallTarget */, 10);
+
+    if (func == caller_with_value_site_never_called1 ||
+        func == caller_with_value_site_never_called2)
+      continue;
+    for (S = 0; S < NS; S++) {
+      unsigned C;
+      for (C = 0; C < S + 1; C++) {
+        __llvm_profile_instrument_target((uint64_t)&callee1, (void *)Data, S);
+        if (C % 2 == 0)
+          __llvm_profile_instrument_target((uint64_t)&callee2, (void *)Data, S);
+      }
+    }
+  }
+}
+
+// CHECK-LABEL:   caller_with_value_site_never_called2:
+// CHECK-NEXT:    Hash: 0x0000000000000000
+// CHECK-NEXT:    Counters:
+// CHECK-NEXT:    Function count
+// CHECK-NEXT:    Indirect Call Site Count: 10
+// CHECK-NEXT:    Indirect Target Results: 
+// CHECK-LABEL:   caller_with_vp2:
+// CHECK-NEXT:    Hash: 0x0000000000000000
+// CHECK-NEXT:    Counters:
+// CHECK-NEXT:    Function count:
+// CHECK-NEXT:    Indirect Call Site Count: 10
+// CHECK-NEXT:    Indirect Target Results: 
+// CHECK-NEXT: [ 0, callee1, 1 ]
+// CHECK-NEXT: [ 0, callee2, 1 ]
+// CHECK-NEXT: [ 1, callee1, 2 ]
+// CHECK-NEXT: [ 1, callee2, 1 ]
+// CHECK-NEXT: [ 2, callee1, 3 ]
+// CHECK-NEXT: [ 2, callee2, 2 ]
+// CHECK-NEXT: [ 3, callee1, 4 ]
+// CHECK-NEXT: [ 3, callee2, 2 ]
+// CHECK-NEXT: [ 4, callee1, 5 ]
+// CHECK-NEXT: [ 4, callee2, 3 ]
+// CHECK-NEXT: [ 5, callee1, 6 ]
+// CHECK-NEXT: [ 5, callee2, 3 ]
+// CHECK-NEXT: [ 6, callee1, 7 ]
+// CHECK-NEXT: [ 6, callee2, 4 ]
+// CHECK-NEXT: [ 7, callee1, 8 ]
+// CHECK-NEXT: [ 7, callee2, 4 ]
+// CHECK-NEXT: [ 8, callee1, 9 ]
+// CHECK-NEXT: [ 8, callee2, 5 ]
+// CHECK-NEXT: [ 9, callee1, 10 ]
+// CHECK-NEXT: [ 9, callee2, 5 ]
+// CHECK-LABEL:   caller_with_vp1:
+// CHECK-NEXT:    Hash: 0x0000000000000000
+// CHECK-NEXT:    Counters:
+// CHECK-NEXT:    Function count
+// CHECK-NEXT:    Indirect Call Site Count: 10
+// CHECK-NEXT:    Indirect Target Results: 
+// CHECK-NEXT: [ 0, callee1, 1 ]
+// CHECK-NEXT: [ 0, callee2, 1 ]
+// CHECK-NEXT: [ 1, callee1, 2 ]
+// CHECK-NEXT: [ 1, callee2, 1 ]
+// CHECK-NEXT: [ 2, callee1, 3 ]
+// CHECK-NEXT: [ 2, callee2, 2 ]
+// CHECK-NEXT: [ 3, callee1, 4 ]
+// CHECK-NEXT: [ 3, callee2, 2 ]
+// CHECK-NEXT: [ 4, callee1, 5 ]
+// CHECK-NEXT: [ 4, callee2, 3 ]
+// CHECK-NEXT: [ 5, callee1, 6 ]
+// CHECK-NEXT: [ 5, callee2, 3 ]
+// CHECK-NEXT: [ 6, callee1, 7 ]
+// CHECK-NEXT: [ 6, callee2, 4 ]
+// CHECK-NEXT: [ 7, callee1, 8 ]
+// CHECK-NEXT: [ 7, callee2, 4 ]
+// CHECK-NEXT: [ 8, callee1, 9 ]
+// CHECK-NEXT: [ 8, callee2, 5 ]
+// CHECK-NEXT: [ 9, callee1, 10 ]
+// CHECK-NEXT: [ 9, callee2, 5 ]
+// CHECK-LABEL:   caller_with_value_site_never_called1:
+// CHECK-NEXT:    Hash: 0x0000000000000000
+// CHECK-NEXT:    Counters:
+// CHECK-NEXT:    Function count:
+// CHECK-NEXT:    Indirect Call Site Count: 10
+// CHECK-NEXT:    Indirect Target Results: 
+// CHECK-LABEL:   caller_without_value_site2:
+// CHECK-NEXT:    Hash: 0x0000000000000000
+// CHECK-NEXT:    Counters:
+// CHECK-NEXT:    Function count:
+// CHECK-NEXT:    Indirect Call Site Count: 0
+// CHECK-NEXT:    Indirect Target Results: 
+// CHECK-LABEL:   caller_without_value_site1:
+// CHECK-NEXT:    Hash: 0x0000000000000000
+// CHECK-NEXT:    Counters:
+// CHECK-NEXT:    Function count:
+// CHECK-NEXT:    Indirect Call Site Count: 0
+// CHECK-NEXT:    Indirect Target Results: 
diff --git a/src/compiler-rt/test/profile/instrprof-value-prof.c b/src/compiler-rt/test/profile/instrprof-value-prof.c
new file mode 100644 (file)
index 0000000..f09e1ac
--- /dev/null
@@ -0,0 +1,225 @@
+// RUN: %clang_profgen -O2 -o %t %s
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t 1
+// RUN: env LLVM_PROFILE_FILE=%t-2.profraw %run %t
+// RUN: llvm-profdata merge -o %t.profdata %t.profraw
+// RUN: llvm-profdata merge -o %t-2.profdata %t-2.profraw
+// RUN: llvm-profdata merge -o %t-merged.profdata %t.profraw %t-2.profdata
+// RUN: llvm-profdata show --all-functions -ic-targets  %t-2.profdata | FileCheck  %s -check-prefix=NO-VALUE
+// RUN: llvm-profdata show --all-functions -ic-targets  %t.profdata | FileCheck  %s
+// RUN: llvm-profdata show --all-functions -ic-targets  %t-merged.profdata | FileCheck  %s
+//
+// RUN: env LLVM_PROFILE_FILE=%t-3.profraw LLVM_VP_BUFFER_SIZE=1 %run %t 1
+// RUN: env LLVM_PROFILE_FILE=%t-4.profraw LLVM_VP_BUFFER_SIZE=8 %run %t 1
+// RUN: env LLVM_PROFILE_FILE=%t-5.profraw LLVM_VP_BUFFER_SIZE=128 %run %t 1
+// RUN: env LLVM_PROFILE_FILE=%t-6.profraw LLVM_VP_BUFFER_SIZE=1024 %run %t 1
+// RUN: env LLVM_PROFILE_FILE=%t-7.profraw LLVM_VP_BUFFER_SIZE=102400 %run %t 1
+// RUN: llvm-profdata merge -o %t-3.profdata %t-3.profraw
+// RUN: llvm-profdata merge -o %t-4.profdata %t-4.profraw
+// RUN: llvm-profdata merge -o %t-5.profdata %t-5.profraw
+// RUN: llvm-profdata merge -o %t-6.profdata %t-6.profraw
+// RUN: llvm-profdata merge -o %t-7.profdata %t-7.profraw
+// RUN: llvm-profdata show --all-functions -ic-targets  %t-3.profdata | FileCheck  %s
+// RUN: llvm-profdata show --all-functions -ic-targets  %t-4.profdata | FileCheck  %s
+// RUN: llvm-profdata show --all-functions -ic-targets  %t-5.profdata | FileCheck  %s
+// RUN: llvm-profdata show --all-functions -ic-targets  %t-6.profdata | FileCheck  %s
+// RUN: llvm-profdata show --all-functions -ic-targets  %t-7.profdata | FileCheck  %s
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+typedef struct __llvm_profile_data __llvm_profile_data;
+const __llvm_profile_data *__llvm_profile_begin_data(void);
+const __llvm_profile_data *__llvm_profile_end_data(void);
+void __llvm_profile_set_num_value_sites(__llvm_profile_data *Data,
+                                        uint32_t ValueKind,
+                                        uint16_t NumValueSites);
+__llvm_profile_data *
+__llvm_profile_iterate_data(const __llvm_profile_data *Data);
+void *__llvm_get_function_addr(const __llvm_profile_data *Data);
+void __llvm_profile_instrument_target(uint64_t TargetValue, void *Data,
+                                      uint32_t CounterIndex);
+
+#define DEF_FUNC(x)                                                            \
+  void x() {}
+#define DEF_2_FUNCS(x) DEF_FUNC(x##_1) DEF_FUNC(x##_2)
+#define DEF_4_FUNCS(x) DEF_2_FUNCS(x##_1) DEF_2_FUNCS(x##_2)
+#define DEF_8_FUNCS(x) DEF_4_FUNCS(x##_1) DEF_4_FUNCS(x##_2)
+#define DEF_16_FUNCS(x) DEF_8_FUNCS(x##_1) DEF_8_FUNCS(x##_2)
+#define DEF_32_FUNCS(x) DEF_16_FUNCS(x##_1) DEF_16_FUNCS(x##_2)
+#define DEF_64_FUNCS(x) DEF_32_FUNCS(x##_1) DEF_32_FUNCS(x##_2)
+#define DEF_128_FUNCS(x) DEF_64_FUNCS(x##_1) DEF_64_FUNCS(x##_2)
+
+#define FUNC_ADDR(x) &x,
+#define FUNC_2_ADDRS(x) FUNC_ADDR(x##_1) FUNC_ADDR(x##_2)
+#define FUNC_4_ADDRS(x) FUNC_2_ADDRS(x##_1) FUNC_2_ADDRS(x##_2)
+#define FUNC_8_ADDRS(x) FUNC_4_ADDRS(x##_1) FUNC_4_ADDRS(x##_2)
+#define FUNC_16_ADDRS(x) FUNC_8_ADDRS(x##_1) FUNC_8_ADDRS(x##_2)
+#define FUNC_32_ADDRS(x) FUNC_16_ADDRS(x##_1) FUNC_16_ADDRS(x##_2)
+#define FUNC_64_ADDRS(x) FUNC_32_ADDRS(x##_1) FUNC_32_ADDRS(x##_2)
+#define FUNC_128_ADDRS(x) FUNC_64_ADDRS(x##_1) FUNC_64_ADDRS(x##_2)
+
+DEF_8_FUNCS(callee)
+DEF_128_FUNCS(caller)
+
+void *CallerAddrs[] = {FUNC_128_ADDRS(caller)};
+void *CalleeAddrs[] = {FUNC_8_ADDRS(callee)};
+typedef struct CallerInfo {
+    void *CallerAddr;
+    uint32_t NS; /* Number value sites. */
+} CallerInfo;
+
+CallerInfo CallerInfos[128];
+
+int cmpaddr(const void *p1, const void *p2) {
+  CallerInfo *addr1 = (CallerInfo *)p1;
+  CallerInfo *addr2 = (CallerInfo *)p2;
+  return (intptr_t)addr2->CallerAddr - (intptr_t)addr1->CallerAddr;
+}
+
+int main(int argc, const char *argv[]) {
+  unsigned S, NS = 0, I, V, doInstrument = 1;
+  const __llvm_profile_data *Data, *DataEnd;
+
+  if (argc < 2)
+    doInstrument = 0;
+
+  for (I = 0; I < 128; I++) {
+     CallerInfos[I].CallerAddr = CallerAddrs[I];
+     CallerInfos[I].NS = I;
+  }
+  qsort(CallerInfos, sizeof(CallerInfos) / sizeof(CallerInfo), sizeof(CallerInfo),
+        cmpaddr);
+
+  /* We will synthesis value profile data for 128 callers functions.
+   * The number of * value sites. The number values for each value site
+   * ranges from 0 to 8.  */
+
+  Data = __llvm_profile_begin_data();
+  DataEnd = __llvm_profile_end_data();
+
+  for (; Data < DataEnd; Data = __llvm_profile_iterate_data(Data)) {
+    void *func = __llvm_get_function_addr(Data);
+    CallerInfo Key, *Res;
+    Key.CallerAddr = func;
+    Res = (CallerInfo *) bsearch(&Key, CallerInfos, sizeof(CallerInfos) / sizeof(CallerInfo),
+                                 sizeof(CallerInfo), cmpaddr);
+    if (Res) {
+      NS = Res->NS;
+      __llvm_profile_set_num_value_sites((__llvm_profile_data *)Data,
+                                         0 /*IPVK_IndirectCallTarget */, NS);
+      if (!doInstrument) {
+        continue;
+      }
+      for (S = 0; S < NS; S++) {
+        for (V = 0; V < S % 8; V++) {
+          unsigned C;
+          for (C = 0; C < V + 1; C++)
+            __llvm_profile_instrument_target((uint64_t)CalleeAddrs[V],
+                                             (void *)Data, S);
+        }
+      }
+    }
+  }
+}
+
+// NO-VALUE: Indirect Call Site Count: 127
+// NO-VALUE-NEXT: Indirect Target Results:
+// CHECK-LABEL: caller_1_1_1_1_2_2_1:
+// CHECK: Indirect Call Site Count: 6
+// CHECK-NEXT: Indirect Target Results:
+// CHECK-NEXT: [ 1, callee_1_1_1, 1 ]
+// CHECK-NEXT: [ 2, callee_1_1_2, 2 ]
+// CHECK-NEXT: [ 2, callee_1_1_1, 1 ]
+// CHECK-NEXT: [ 3, callee_1_2_1, 3 ]
+// CHECK-NEXT: [ 3, callee_1_1_2, 2 ]
+// CHECK-NEXT: [ 3, callee_1_1_1, 1 ]
+// CHECK-NEXT: [ 4, callee_1_2_2, 4 ]
+// CHECK-NEXT: [ 4, callee_1_2_1, 3 ]
+// CHECK-NEXT: [ 4, callee_1_1_2, 2 ]
+// CHECK-NEXT: [ 4, callee_1_1_1, 1 ]
+// CHECK-NEXT: [ 5, callee_2_1_1, 5 ]
+// CHECK-NEXT: [ 5, callee_1_2_2, 4 ]
+// CHECK-NEXT: [ 5, callee_1_2_1, 3 ]
+// CHECK-NEXT: [ 5, callee_1_1_2, 2 ]
+// CHECK-NEXT: [ 5, callee_1_1_1, 1 ]
+// CHECK-LABEL: caller_2_2_2_2_2_2_2:
+// CHECK: Indirect Call Site Count: 127
+// CHECK-NEXT: Indirect Target Results:
+// CHECK-NEXT:  [ 1, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 2, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 2, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 3, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 3, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 3, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 4, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 4, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 4, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 4, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 5, callee_2_1_1, 5 ]
+// CHECK-NEXT:  [ 5, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 5, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 5, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 5, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 6, callee_2_1_2, 6 ]
+// CHECK-NEXT:  [ 6, callee_2_1_1, 5 ]
+// CHECK-NEXT:  [ 6, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 6, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 6, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 6, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 7, callee_2_2_1, 7 ]
+// CHECK-NEXT:  [ 7, callee_2_1_2, 6 ]
+// CHECK-NEXT:  [ 7, callee_2_1_1, 5 ]
+// CHECK-NEXT:  [ 7, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 7, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 7, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 7, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 9, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 10, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 10, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 11, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 11, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 11, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 12, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 12, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 12, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 12, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 13, callee_2_1_1, 5 ]
+// CHECK-NEXT:  [ 13, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 13, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 13, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 13, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 14, callee_2_1_2, 6 ]
+// CHECK-NEXT:  [ 14, callee_2_1_1, 5 ]
+// CHECK-NEXT:  [ 14, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 14, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 14, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 14, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 15, callee_2_2_1, 7 ]
+// CHECK-NEXT:  [ 15, callee_2_1_2, 6 ]
+// CHECK-NEXT:  [ 15, callee_2_1_1, 5 ]
+// CHECK-NEXT:  [ 15, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 15, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 15, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 15, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 17, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 18, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 18, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 19, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 19, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 19, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 20, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 20, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 20, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 20, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 21, callee_2_1_1, 5 ]
+// CHECK-NEXT:  [ 21, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 21, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 21, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 21, callee_1_1_1, 1 ]
+// CHECK-NEXT:  [ 22, callee_2_1_2, 6 ]
+// CHECK-NEXT:  [ 22, callee_2_1_1, 5 ]
+// CHECK-NEXT:  [ 22, callee_1_2_2, 4 ]
+// CHECK-NEXT:  [ 22, callee_1_2_1, 3 ]
+// CHECK-NEXT:  [ 22, callee_1_1_2, 2 ]
+// CHECK-NEXT:  [ 22, callee_1_1_1, 1 ]
+
diff --git a/src/compiler-rt/test/profile/instrprof-version-mismatch.c b/src/compiler-rt/test/profile/instrprof-version-mismatch.c
new file mode 100644 (file)
index 0000000..49ce411
--- /dev/null
@@ -0,0 +1,11 @@
+// RUN: %clang_profgen -o %t -O3 %s
+// RUN: LLVM_PROFILE_VERBOSE_ERRORS=1 %run %t 1 2>&1 | FileCheck %s
+
+// override the version variable with a bogus version:
+unsigned long long __llvm_profile_raw_version = 10000;
+int main(int argc, const char *argv[]) {
+  if (argc < 2)
+    return 1;
+  return 0;
+}
+// CHECK: LLVM Profile: runtime and instrumentation version mismatch
index fc6c9b25b3ba579696b4dc3f060af3f9bb7d265b..eb0a76ded39ff5aa303426a8a63f76c259f98156 100644 (file)
@@ -56,5 +56,11 @@ int main(int argc, const char *argv[]) {
 // CHECK-SYMBOLS-NOT: _fopen
 // CHECK-SYMBOLS-NOT: _fwrite
 // CHECK-SYMBOLS-NOT: _getenv
+// CHECK-SYMBOLS-NOT: getenv
 // CHECK-SYMBOLS-NOT: _malloc
+// CHECK-SYMBOLS-NOT: malloc
+// CHECK-SYMBOLS-NOT: _calloc
+// CHECK-SYMBOLS-NOT: calloc
+// CHECK-SYMBOLS-NOT: _free
+// CHECK-SYMBOLS-NOT: free
 // CHECK-SYMBOLS-NOT: _open
index 13fc92fa4b2bc1a9afc2cf541fabdbe8c6eca718..535c09742ca932a119265e49d4ac45e4ed2a810a 100644 (file)
@@ -22,3 +22,8 @@ if config.lto_supported:
 # SafeStack tests are currently supported on Linux, FreeBSD and Darwin only.
 if config.host_os not in ['Linux', 'FreeBSD', 'Darwin']:
    config.unsupported = True
+
+# Allow tests to use REQUIRES=stable-runtime.  For use when you cannot use XFAIL
+# because the test fail due some runtime issue.
+if config.target_arch != 'aarch64':
+  config.available_features.add('stable-runtime')
index 14e29823cd9941ced4962f01a38556a9813a5f43..62f86536916ef391d5c40e3b0658d16ef33d31ad 100644 (file)
@@ -7,6 +7,8 @@
 // Test that buffer overflows on the unsafe stack do not affect variables on the
 // safe stack.
 
+// REQUIRES: stable-runtime
+
 __attribute__((noinline))
 void fct(volatile int *buffer)
 {
@@ -15,9 +17,13 @@ void fct(volatile int *buffer)
 
 int main(int argc, char **argv)
 {
+  int prebuf[7];
   int value1 = 42;
   int buffer[5];
   int value2 = 42;
+  int postbuf[7];
+  fct(prebuf + 1);
+  fct(postbuf + 1);
   fct(buffer);
   return value1 != 42 || value2 != 42;
 }
diff --git a/src/compiler-rt/test/sanitizer_common/TestCases/Linux/closedir.c b/src/compiler-rt/test/sanitizer_common/TestCases/Linux/closedir.c
new file mode 100644 (file)
index 0000000..990628d
--- /dev/null
@@ -0,0 +1,5 @@
+// Check that closedir(NULL) is ok.
+// RUN: %clang -O2 %s -o %t && %run %t
+#include <sys/types.h>
+#include <dirent.h>
+int main() { closedir(0); }
diff --git a/src/compiler-rt/test/sanitizer_common/TestCases/Linux/ill.cc b/src/compiler-rt/test/sanitizer_common/TestCases/Linux/ill.cc
new file mode 100644 (file)
index 0000000..1edad48
--- /dev/null
@@ -0,0 +1,27 @@
+// Test the handle_sigill option.
+// RUN: %clang %s -o %t -O1
+// RUN:                                not --crash %run %t 2>&1 | FileCheck --check-prefix=CHECK0 %s
+// RUN: %env_tool_opts=handle_sigill=0 not --crash %run %t 2>&1 | FileCheck --check-prefix=CHECK0 %s
+// RUN: %env_tool_opts=handle_sigill=1 not         %run %t 2>&1 | FileCheck --check-prefix=CHECK1 %s
+// FIXME: implement in other sanitizers, not just asan.
+// XFAIL: msan
+// XFAIL: lsan
+// XFAIL: tsan
+//
+// FIXME: seems to fail on ARM
+// REQUIRES: x86_64-supported-target
+#include <assert.h>
+#include <stdio.h>
+#include <sanitizer/asan_interface.h>
+
+void death() {
+  fprintf(stderr, "DEATH CALLBACK\n");
+}
+
+int main(int argc, char **argv) {
+  __sanitizer_set_death_callback(death);
+  __builtin_trap();
+}
+// CHECK1: ERROR: {{.*}}Sanitizer:
+// CHECK1: DEATH CALLBACK
+// CHECK0-NOT: Sanitizer
diff --git a/src/compiler-rt/test/sanitizer_common/TestCases/fopen_nullptr.c b/src/compiler-rt/test/sanitizer_common/TestCases/fopen_nullptr.c
new file mode 100644 (file)
index 0000000..960dda3
--- /dev/null
@@ -0,0 +1,6 @@
+// Check that fopen(NULL, "r") is ok.
+// RUN: %clang -O2 %s -o %t && %run %t
+#include <stdio.h>
+const char *fn = NULL;
+FILE *f;
+int main() { f = fopen(fn, "r"); }
index 1492ddb64f80ffa39370a255e0b940a164c9657b..a058602659c1d943e9f49d4eca23c8fe9860d879 100644 (file)
@@ -1,30 +1,56 @@
 set(TSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
-if(NOT ${COMPILER_RT_DEFAULT_TARGET_ARCH} MATCHES "mips" AND NOT APPLE)
+if(${COMPILER_RT_DEFAULT_TARGET_ARCH} MATCHES "x86_64")
   list(APPEND TSAN_TEST_DEPS GotsanRuntimeCheck)
 endif()
 if(NOT COMPILER_RT_STANDALONE_BUILD)
   list(APPEND TSAN_TEST_DEPS tsan)
 endif()
 if(COMPILER_RT_HAS_LIBCXX_SOURCES AND
-   COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang")
+   COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang"
+   AND NOT APPLE)
   list(APPEND TSAN_TEST_DEPS libcxx_tsan)
   set(TSAN_HAS_LIBCXX True)
 else()
   set(TSAN_HAS_LIBCXX False)
 endif()
 
-configure_lit_site_cfg(
-  ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
-  ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
+set(TSAN_TESTSUITES)
+
+set(TSAN_TEST_ARCH ${TSAN_SUPPORTED_ARCH})
+if(APPLE)
+  darwin_filter_host_archs(TSAN_SUPPORTED_ARCH TSAN_TEST_ARCH)
+endif()
+
+foreach(arch ${TSAN_TEST_ARCH})
+  string(TOLOWER "-${arch}" TSAN_TEST_CONFIG_SUFFIX)
+  if(ANDROID OR ${arch} MATCHES "arm|aarch64")
+    # This is only true if we are cross-compiling.
+    # Build all tests with host compiler and use host tools.
+    set(TSAN_TEST_TARGET_CC ${COMPILER_RT_TEST_COMPILER})
+    set(TSAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS})
+  else()
+    get_target_flags_for_arch(${arch} TSAN_TEST_TARGET_CFLAGS)
+    string(REPLACE ";" " " TSAN_TEST_TARGET_CFLAGS "${TSAN_TEST_TARGET_CFLAGS}")
+  endif()
+
+  string(TOUPPER ${arch} ARCH_UPPER_CASE)
+  set(CONFIG_NAME ${ARCH_UPPER_CASE}Config)
+
+  configure_lit_site_cfg(
+    ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+    ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg)
+  list(APPEND TSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
+endforeach()
 
 if(COMPILER_RT_INCLUDE_TESTS)
   configure_lit_site_cfg(
     ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
     ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg)
   list(APPEND TSAN_TEST_DEPS TsanUnitTests)
+  list(APPEND TSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit)
 endif()
 
 add_lit_testsuite(check-tsan "Running ThreadSanitizer tests"
-  ${CMAKE_CURRENT_BINARY_DIR}
+  ${TSAN_TESTSUITES}
   DEPENDS ${TSAN_TEST_DEPS})
 set_target_properties(check-tsan PROPERTIES FOLDER "TSan tests")
diff --git a/src/compiler-rt/test/tsan/Darwin/gcd-async-norace.mm b/src/compiler-rt/test/tsan/Darwin/gcd-async-norace.mm
new file mode 100644 (file)
index 0000000..b987e00
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+long global;
+
+int main() {
+  NSLog(@"Hello world.");
+
+  global = 42;
+  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+    global = 43;
+
+    dispatch_sync(dispatch_get_main_queue(), ^{
+      CFRunLoopStop(CFRunLoopGetCurrent());
+    });
+  });
+
+  CFRunLoopRun();
+  NSLog(@"Done.");
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
diff --git a/src/compiler-rt/test/tsan/Darwin/gcd-async-race.mm b/src/compiler-rt/test/tsan/Darwin/gcd-async-race.mm
new file mode 100644 (file)
index 0000000..31163f9
--- /dev/null
@@ -0,0 +1,38 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %deflake %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+#import "../test.h"
+
+long global;
+
+int main() {
+  NSLog(@"Hello world.");
+  NSLog(@"addr=%p\n", &global);
+  barrier_init(&barrier, 2);
+
+  global = 42;
+  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+    global = 43;
+    barrier_wait(&barrier);
+  });
+
+  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+    barrier_wait(&barrier);
+    global = 44;
+
+    dispatch_sync(dispatch_get_main_queue(), ^{
+      CFRunLoopStop(CFRunLoopGetCurrent());
+    });
+  });
+
+  CFRunLoopRun();
+  NSLog(@"Done.");
+}
+
+// CHECK: Hello world.
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is global 'global' at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}})
+// CHECK: Done.
diff --git a/src/compiler-rt/test/tsan/Darwin/gcd-groups-norace.mm b/src/compiler-rt/test/tsan/Darwin/gcd-groups-norace.mm
new file mode 100644 (file)
index 0000000..fb4d804
--- /dev/null
@@ -0,0 +1,53 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+#import "../test.h"
+
+long global;
+
+int main() {
+  NSLog(@"Hello world.");
+  NSLog(@"addr=%p\n", &global);
+
+  dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+  global = 42;
+
+  dispatch_group_t g = dispatch_group_create();
+  dispatch_group_async(g, q, ^{
+    global = 43;
+  });
+  dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
+
+  global = 44;
+
+  dispatch_group_enter(g);
+  dispatch_async(q, ^{
+    global = 45;
+    dispatch_group_leave(g);
+  });
+  dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
+
+  global = 46;
+
+  dispatch_group_enter(g);
+  dispatch_async(q, ^{
+    global = 47;
+    dispatch_group_leave(g);
+  });
+  dispatch_group_notify(g, q, ^{
+    global = 48;
+
+    dispatch_sync(dispatch_get_main_queue(), ^{
+      CFRunLoopStop(CFRunLoopGetCurrent());
+    });
+  });
+
+  CFRunLoopRun();
+  NSLog(@"Done.");
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
diff --git a/src/compiler-rt/test/tsan/Darwin/gcd-groups-stress.mm b/src/compiler-rt/test/tsan/Darwin/gcd-groups-stress.mm
new file mode 100644 (file)
index 0000000..62a8008
--- /dev/null
@@ -0,0 +1,43 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+void notify_callback(void *context) {
+  // Do nothing.
+}
+
+int main() {
+  NSLog(@"Hello world.");
+
+  dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+  
+  for (int i = 0; i < 300000; i++) {
+    dispatch_group_t g = dispatch_group_create();
+    dispatch_group_enter(g);
+    dispatch_async(q, ^{
+      dispatch_group_leave(g);
+    });
+    dispatch_group_notify(g, q, ^{
+      // Do nothing.
+    });
+    dispatch_release(g);
+  }
+
+  for (int i = 0; i < 300000; i++) {
+    dispatch_group_t g = dispatch_group_create();
+    dispatch_group_enter(g);
+    dispatch_async(q, ^{
+      dispatch_group_leave(g);
+    });
+    dispatch_group_notify_f(g, q, nullptr, &notify_callback);
+    dispatch_release(g);
+  }
+
+  NSLog(@"Done.");
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK-NOT: CHECK failed
diff --git a/src/compiler-rt/test/tsan/Darwin/gcd-once.mm b/src/compiler-rt/test/tsan/Darwin/gcd-once.mm
new file mode 100644 (file)
index 0000000..17757d2
--- /dev/null
@@ -0,0 +1,55 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+#import "../test.h"
+
+static const long kNumThreads = 4;
+
+long global;
+long global2;
+
+static dispatch_once_t once_token;
+static dispatch_once_t once_token2;
+
+void f(void *) {
+  global2 = 42;
+  usleep(100000);
+}
+
+void *Thread(void *a) {
+  barrier_wait(&barrier);
+
+  dispatch_once(&once_token, ^{
+    global = 42;
+    usleep(100000);
+  });
+  long x = global;
+
+  dispatch_once_f(&once_token2, NULL, f);
+  long x2 = global2;
+
+  fprintf(stderr, "global = %ld\n", x);
+  fprintf(stderr, "global2 = %ld\n", x2);
+  return 0;
+}
+
+int main() {
+  fprintf(stderr, "Hello world.\n");
+  barrier_init(&barrier, kNumThreads);
+
+  pthread_t t[kNumThreads];
+  for (int i = 0; i < kNumThreads; i++) {
+    pthread_create(&t[i], 0, Thread, 0);
+  }
+  for (int i = 0; i < kNumThreads; i++) {
+    pthread_join(t[i], 0);
+  }
+
+  fprintf(stderr, "Done.\n");
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
diff --git a/src/compiler-rt/test/tsan/Darwin/gcd-semaphore-norace.mm b/src/compiler-rt/test/tsan/Darwin/gcd-semaphore-norace.mm
new file mode 100644 (file)
index 0000000..cd52a79
--- /dev/null
@@ -0,0 +1,29 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+long global;
+
+int main() {
+    NSLog(@"Hello world.");
+
+    global = 42;
+    
+    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        
+        global = 43;
+        dispatch_semaphore_signal(sem);
+    });
+    
+    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+    global = 44;
+
+    NSLog(@"Done.");
+    return 0;
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
diff --git a/src/compiler-rt/test/tsan/Darwin/gcd-serial-queue-norace.mm b/src/compiler-rt/test/tsan/Darwin/gcd-serial-queue-norace.mm
new file mode 100644 (file)
index 0000000..8f6de27
--- /dev/null
@@ -0,0 +1,40 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+#import "../test.h"
+
+long global;
+
+int main() {
+  NSLog(@"Hello world.");
+  NSLog(@"addr=%p\n", &global);
+
+  dispatch_queue_t q1 = dispatch_queue_create("my.queue1", DISPATCH_QUEUE_CONCURRENT);
+  dispatch_queue_t q2 = dispatch_queue_create("my.queue2", DISPATCH_QUEUE_SERIAL);
+
+  global = 42;
+  for (int i = 0; i < 10; i++) {
+    dispatch_async(q1, ^{
+      for (int i = 0; i < 100; i++) {
+        dispatch_sync(q2, ^{
+          global++;
+        });
+      }
+    });
+  }
+
+  dispatch_barrier_async(q1, ^{
+    dispatch_sync(dispatch_get_main_queue(), ^{
+      CFRunLoopStop(CFRunLoopGetCurrent());
+    });
+  });
+
+  CFRunLoopRun();
+  NSLog(@"Done.");
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
diff --git a/src/compiler-rt/test/tsan/Darwin/gcd-sync-norace.mm b/src/compiler-rt/test/tsan/Darwin/gcd-sync-norace.mm
new file mode 100644 (file)
index 0000000..f21cfde
--- /dev/null
@@ -0,0 +1,32 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+long global;
+
+static const long nIter = 1000;
+
+int main() {
+  NSLog(@"Hello world.");
+
+  global = 42;
+  for (int i = 0; i < nIter; i++) {
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+      dispatch_sync(dispatch_get_main_queue(), ^{
+        global = i;
+
+        if (i == nIter - 1) {
+          CFRunLoopStop(CFRunLoopGetCurrent());
+        }
+      });
+    });
+  }
+
+  CFRunLoopRun();
+  NSLog(@"Done.");
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
diff --git a/src/compiler-rt/test/tsan/Darwin/gcd-sync-race.mm b/src/compiler-rt/test/tsan/Darwin/gcd-sync-race.mm
new file mode 100644 (file)
index 0000000..62901d9
--- /dev/null
@@ -0,0 +1,44 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %deflake %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+#import "../test.h"
+
+long global;
+
+int main() {
+  NSLog(@"Hello world.");
+  NSLog(@"addr=%p\n", &global);
+  barrier_init(&barrier, 2);
+
+  dispatch_queue_t q1 = dispatch_queue_create("my.queue1", DISPATCH_QUEUE_CONCURRENT);
+  dispatch_queue_t q2 = dispatch_queue_create("my.queue2", DISPATCH_QUEUE_CONCURRENT);
+
+  global = 42;
+  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+    dispatch_sync(q1, ^{
+      global = 43;
+      barrier_wait(&barrier);
+    });
+  });
+  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+    dispatch_sync(q2, ^{
+      barrier_wait(&barrier);
+      global = 44;
+
+      dispatch_sync(dispatch_get_main_queue(), ^{
+        CFRunLoopStop(CFRunLoopGetCurrent());
+      });
+    });
+  });
+
+  CFRunLoopRun();
+  NSLog(@"Done.");
+}
+
+// CHECK: Hello world.
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is global 'global' at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}})
+// CHECK: Done.
diff --git a/src/compiler-rt/test/tsan/Darwin/lit.local.cfg b/src/compiler-rt/test/tsan/Darwin/lit.local.cfg
new file mode 100644 (file)
index 0000000..a85dfcd
--- /dev/null
@@ -0,0 +1,9 @@
+def getRoot(config):
+  if not config.parent:
+    return config
+  return getRoot(config.parent)
+
+root = getRoot(config)
+
+if root.host_os not in ['Darwin']:
+  config.unsupported = True
diff --git a/src/compiler-rt/test/tsan/Darwin/objc-race.mm b/src/compiler-rt/test/tsan/Darwin/objc-race.mm
new file mode 100644 (file)
index 0000000..bd93d2f
--- /dev/null
@@ -0,0 +1,55 @@
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %deflake %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+#import "../test.h"
+
+@interface MyClass : NSObject {
+  long instance_variable;
+}
+- (void)method:(long)value;
+@end
+
+@implementation MyClass
+
+- (void)method:(long)value {
+  self->instance_variable = value;
+}
+
+@end
+
+int main() {
+  NSLog(@"Hello world.");
+  barrier_init(&barrier, 2);
+  
+  MyClass *my_object = [MyClass new];
+  [my_object method:42];
+  
+  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+    [my_object method:43];
+    barrier_wait(&barrier);
+  });
+  
+  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+    barrier_wait(&barrier);
+    [my_object method:44];
+
+    dispatch_sync(dispatch_get_main_queue(), ^{
+      CFRunLoopStop(CFRunLoopGetCurrent());
+    });
+  });
+  
+  CFRunLoopRun();
+  NSLog(@"Done.");
+  return 0;
+}
+
+// CHECK: Hello world.
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK:   Write of size 8
+// CHECK:     #0 -[MyClass method:]
+// CHECK:   Write of size 8
+// CHECK:     #0 -[MyClass method:]
+// CHECK:   Location is heap block
+// CHECK: Done.
diff --git a/src/compiler-rt/test/tsan/Darwin/objc-simple.mm b/src/compiler-rt/test/tsan/Darwin/objc-simple.mm
new file mode 100644 (file)
index 0000000..a4bf1f1
--- /dev/null
@@ -0,0 +1,13 @@
+// Test that a simple Obj-C program runs and exits without any warnings.
+
+// RUN: %clang_tsan %s -o %t -framework Foundation
+// RUN: %run %t 2>&1
+
+#import <Foundation/Foundation.h>
+
+int main() {
+  NSLog(@"Hello world");
+}
+
+// CHECK: Hello world
+// CHECK-NOT: WARNING: ThreadSanitizer
diff --git a/src/compiler-rt/test/tsan/Darwin/osspinlock-norace.cc b/src/compiler-rt/test/tsan/Darwin/osspinlock-norace.cc
new file mode 100644 (file)
index 0000000..2ac3989
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <libkern/OSAtomic.h>
+#include <pthread.h>
+#include <stdio.h>
+
+int Global;
+OSSpinLock lock;
+
+void *Thread(void *x) {
+  OSSpinLockLock(&lock);
+  Global++;
+  OSSpinLockUnlock(&lock);
+  return NULL;
+}
+
+int main() {
+  fprintf(stderr, "Hello world.\n");
+
+  pthread_t t[2];
+  pthread_create(&t[0], NULL, Thread, NULL);
+  pthread_create(&t[1], NULL, Thread, NULL);
+  pthread_join(t[0], NULL);
+  pthread_join(t[1], NULL);
+
+  fprintf(stderr, "Done.\n");
+}
+
+// CHECK: Hello world.
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
diff --git a/src/compiler-rt/test/tsan/Darwin/symbolizer-atos.cc b/src/compiler-rt/test/tsan/Darwin/symbolizer-atos.cc
new file mode 100644 (file)
index 0000000..960fecc
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %env_tsan_opts=verbosity=2:external_symbolizer_path=/usr/bin/atos %deflake %run %t | FileCheck %s
+#include "../test.h"
+
+int GlobalData[10];
+
+void *Thread(void *a) {
+  barrier_wait(&barrier);
+  GlobalData[2] = 42;
+  return 0;
+}
+
+int main() {
+  barrier_init(&barrier, 2);
+  print_address("addr=", 1, GlobalData);
+  pthread_t t;
+  pthread_create(&t, 0, Thread, 0);
+  GlobalData[2] = 43;
+  barrier_wait(&barrier);
+  pthread_join(t, 0);
+}
+
+// CHECK: Using atos at user-specified path: /usr/bin/atos
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is global 'GlobalData' at [[ADDR]] ({{.*}}+0x{{[0-9,a-f]+}})
diff --git a/src/compiler-rt/test/tsan/Darwin/symbolizer-dladdr.cc b/src/compiler-rt/test/tsan/Darwin/symbolizer-dladdr.cc
new file mode 100644 (file)
index 0000000..3b213dd
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan %s -o %t
+// RUN: %env_tsan_opts=verbosity=2:external_symbolizer_path= %deflake %run %t | FileCheck %s
+#include "../test.h"
+
+int GlobalData[10];
+
+void *Thread(void *a) {
+  barrier_wait(&barrier);
+  GlobalData[2] = 42;
+  return 0;
+}
+
+int main() {
+  barrier_init(&barrier, 2);
+  print_address("addr=", 1, GlobalData);
+  pthread_t t;
+  pthread_create(&t, 0, Thread, 0);
+  GlobalData[2] = 43;
+  barrier_wait(&barrier);
+  pthread_join(t, 0);
+}
+
+// CHECK: External symbolizer is explicitly disabled.
+// CHECK: Using dladdr symbolizer.
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is global 'GlobalData' at [[ADDR]] ({{.*}}+0x{{[0-9,a-f]+}})
diff --git a/src/compiler-rt/test/tsan/Linux/check_memcpy.cc b/src/compiler-rt/test/tsan/Linux/check_memcpy.cc
new file mode 100644 (file)
index 0000000..8ad04c0
--- /dev/null
@@ -0,0 +1,15 @@
+// Test that verifies TSan runtime doesn't contain compiler-emitted
+// memcpy/memmove calls. It builds the binary with TSan and passes it to
+// check_memcpy.sh script.
+
+// RUN: %clangxx_tsan -O1 %s -o %t
+// RUN: llvm-objdump -d %t | FileCheck %s
+
+int main() {
+  return 0;
+}
+
+// CHECK-NOT: callq {{.*<(__interceptor_)?mem(cpy|set)>}}
+// tail calls:
+// CHECK-NOT: jmpq {{.*<(__interceptor_)?mem(cpy|set)>}}
+
index cde706bc8a1d8bdad2ac543d5b2de4ac5e717314..66930076ac3ace31e07fb5130fac98b2c19f0602 100644 (file)
@@ -4,11 +4,11 @@
 //
 // RUN: %clangxx_tsan -O0 %s -o %t
 // RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
-// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
-// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH
-// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH
-// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH
-// RUN: TSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH
+// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH
 
 #include <limits.h>
 #include <stdlib.h>
index d8c2b6ffe5144f5ea1c6bcaf0a392204eb299a1b..de2756de2a12c8d800445244aab740a2203eeb40 100644 (file)
@@ -2,6 +2,9 @@
 // CHECK-NOT: ThreadSanitizer: data race
 // CHECK: DONE
 
+// pthread barriers are not available on OS X
+// UNSUPPORTED: darwin
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <pthread.h>
index 5cd6bd74ebee5b5ceacd96a40b731a339814c9e8..0ed21b4612ab2cef224cf5e3691451c6eae67c6f 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: %clangxx_tsan %s -o %t
 // RUN: %run %t 2>&1 | FileCheck %s
 
+// bench.h needs pthread barriers which are not available on OS X
+// UNSUPPORTED: darwin
+
 #include "bench.h"
 
 int x;
index 9e53a7b26efabbf4a730f5d3d8966655738c85f2..3799452a1629a35383ebb29385b3deaaa3c4152c 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: %clangxx_tsan %s -o %t
 // RUN: %run %t 2>&1 | FileCheck %s
 
+// bench.h needs pthread barriers which are not available on OS X
+// UNSUPPORTED: darwin
+
 #include "bench.h"
 
 int x;
index 0fa1db0c883c1d9e55092ce19467db1217009278..15f83bc8b282f4c4b21226fad914024e9a4afbf4 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: %clangxx_tsan %s -o %t
 // RUN: %run %t 2>&1 | FileCheck %s
 
+// bench.h needs pthread barriers which are not available on OS X
+// UNSUPPORTED: darwin
+
 #include "bench.h"
 
 pthread_mutex_t *mtx;
index 324d53fd7f28d725c94e56f5ed69f8d77dbc7f05..58aa86a787d32b8686a9c6ef1621340c0ced21da 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: %clangxx_tsan %s -o %t
 // RUN: %run %t 2>&1 | FileCheck %s
 
+// bench.h needs pthread barriers which are not available on OS X
+// UNSUPPORTED: darwin
+
 #include "bench.h"
 
 pthread_mutex_t mtx;
index 0a86f73f249e2bc635e395725ffb91967089cd79..7f26041afc49f69bfc945a73c898035e70f26be4 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: %clangxx_tsan %s -o %t
 // RUN: %run %t 2>&1 | FileCheck %s
 
+// bench.h needs pthread barriers which are not available on OS X
+// UNSUPPORTED: darwin
+
 #include "bench.h"
 
 int *x;
index 818ee8c82bc113da4c75e1e43f5f983a59fc5750..2b3dcb012e506502e23fdf71f03e350ba7e9122b 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: %clangxx_tsan %s -o %t
 // RUN: %run %t 2>&1 | FileCheck %s
 
+// bench.h needs pthread barriers which are not available on OS X
+// UNSUPPORTED: darwin
+
 #include "bench.h"
 
 pthread_rwlock_t mtx;
index 0d3810a03ad01f9644328c4dd579f8bb04f1aabb..3d2ea150b5fbc814b35ccba7ea02876573c8fe8b 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: %clangxx_tsan %s -o %t
 // RUN: %run %t 2>&1 | FileCheck %s
 
+// bench.h needs pthread barriers which are not available on OS X
+// UNSUPPORTED: darwin
+
 #include "bench.h"
 
 int x;
index 876f1365ee4321cd49acced1617ecf42e533fd00..e7fa05ea82039c55b5b58d2b54171662f38c490c 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: %clangxx_tsan %s -o %t
 // RUN: %run %t 2>&1 | FileCheck %s
 
+// bench.h needs pthread barriers which are not available on OS X
+// UNSUPPORTED: darwin
+
 #include "bench.h"
 
 const int kMutex = 10;
index ddfb745174f63e1fdd8371d364e02c887140bec9..fb6a66136b8af6354673c949c81abc69411ac345 100644 (file)
@@ -1,6 +1,14 @@
 // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
 // CHECK-NOT: WARNING
 // CHECK: OK
+// This test is failing on powerpc64 (VMA=44). After calling pthread_cancel,
+// the Thread-specific data destructors are not called, so the destructor 
+// "thread_finalize" (defined in tsan_interceptors.cc) can not set the status
+// of the thread to "ThreadStatusFinished" failing a check in "SetJoined" 
+// (defined in sanitizer_thread_registry.cc). It might seem a bug on glibc,
+// however the same version GLIBC-2.17 will not make fail the test on 
+// powerpc64 BE (VMA=46)
+// XFAIL: powerpc64-unknown-linux-gnu
 
 #include "test.h"
 
index 2282c3ad738d578937110bab4940fb9393f96bc6..6bae776e6a4e3ec1b0a0ea5f5dd79fda6c257382 100644 (file)
@@ -3,6 +3,9 @@
 // previously there were issues with versioned symbols.
 // CHECK: OK
 
+// OS X doesn't have pthread_condattr_setclock.
+// UNSUPPORTED: darwin
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <pthread.h>
index efc13ae2624a4b1896e72d8f4d275f45582f3cc3..bbaaabbb3c14db44228c4c394ada6c99920e7e30 100644 (file)
@@ -1,12 +1,12 @@
 // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadMutex
-// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOT-SECOND
-// RUN: TSAN_OPTIONS="detect_deadlocks=1 second_deadlock_stack=1" %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND
+// RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOT-SECOND
+// RUN: %env_tsan_opts=detect_deadlocks=1:second_deadlock_stack=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND
 // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadSpinLock
-// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s
+// RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s
 // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRWLock
-// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RD
+// RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RD
 // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRecursiveMutex
-// RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-REC
+// RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-REC
 #include "test.h"
 #undef NDEBUG
 #include <assert.h>
@@ -56,6 +56,7 @@ class PthreadRecursiveMutex : public PthreadMutex {
   static bool supports_recursive_lock() { return true; }
 };
 
+#ifndef __APPLE__
 class PthreadSpinLock {
  public:
   PthreadSpinLock() { assert(0 == pthread_spin_init(&mu_, 0)); }
@@ -76,6 +77,9 @@ class PthreadSpinLock {
   pthread_spinlock_t mu_;
   char padding_[64 - sizeof(pthread_spinlock_t)];
 };
+#else
+class PthreadSpinLock : public PthreadMutex { };
+#endif
 
 class PthreadRWLock {
  public:
@@ -95,7 +99,7 @@ class PthreadRWLock {
 
  private:
   pthread_rwlock_t mu_;
-  char padding_[64 - sizeof(pthread_rwlock_t)];
+  char padding_[256 - sizeof(pthread_rwlock_t)];
 };
 
 class LockTest {
@@ -148,7 +152,7 @@ class LockTest {
     fprintf(stderr, "Starting Test1\n");
     // CHECK: Starting Test1
     Init(5);
-    fprintf(stderr, "Expecting lock inversion: %p %p\n", A(0), A(1));
+    print_address("Expecting lock inversion: ", 2, A(0), A(1));
     // CHECK: Expecting lock inversion: [[A1:0x[a-f0-9]*]] [[A2:0x[a-f0-9]*]]
     Lock_0_1();
     Lock_1_0();
@@ -174,7 +178,7 @@ class LockTest {
     fprintf(stderr, "Starting Test2\n");
     // CHECK: Starting Test2
     Init(5);
-    fprintf(stderr, "Expecting lock inversion: %p %p %p\n", A(0), A(1), A(2));
+    print_address("Expecting lock inversion: ", 3, A(0), A(1), A(2));
     // CHECK: Expecting lock inversion: [[A1:0x[a-f0-9]*]] [[A2:0x[a-f0-9]*]] [[A3:0x[a-f0-9]*]]
     Lock2(0, 1);
     Lock2(1, 2);
index b230a920ac4fbc8f81b489d49f99bba866acaddc..b9ce615f82fe520fb00a85419246d80b478453ae 100644 (file)
@@ -1,7 +1,8 @@
 // RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
 
-// If we mention TSAN_OPTIONS, the test won't run from test_output.sh script.
+// dl_iterate_phdr doesn't exist on OS X.
+// UNSUPPORTED: darwin
 
 #ifdef BUILD_SO
 
index 1a93fe6617e183c5f4dc520fa6dd6b92c34f82d9..d497fd704e4f11fdd573210a6fe2b52a345921bd 100644 (file)
@@ -1,10 +1,8 @@
 // RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
 
-// If we mention TSAN_OPTIONS, the test won't run from test_output.sh script.
-
 // Test case for
-// https://code.google.com/p/thread-sanitizer/issues/detail?id=80
+// https://github.com/google/sanitizers/issues/487
 
 #ifdef BUILD_SO
 
diff --git a/src/compiler-rt/test/tsan/fd_tid_recycled.cc b/src/compiler-rt/test/tsan/fd_tid_recycled.cc
new file mode 100644 (file)
index 0000000..d314787
--- /dev/null
@@ -0,0 +1,54 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+#include "test.h"
+
+int fds[2];
+
+void *ThreadCreatePipe(void *x) {
+  pipe(fds);
+  return NULL;
+}
+
+void *ThreadDummy(void *x) {
+  return NULL;
+}
+
+void *ThreadWrite(void *x) {
+  write(fds[1], "a", 1);
+  barrier_wait(&barrier);
+  return NULL;
+}
+
+void *ThreadClose(void *x) {
+  barrier_wait(&barrier);
+  close(fds[0]);
+  close(fds[1]);
+  return NULL;
+}
+
+int main() {
+  barrier_init(&barrier, 2);
+  pthread_t t_create;
+  pthread_create(&t_create, NULL, ThreadCreatePipe, NULL);
+  pthread_join(t_create, NULL);
+
+  for (int i = 0; i < 100; i++) {
+    pthread_t t_dummy;
+    pthread_create(&t_dummy, NULL, ThreadDummy, NULL);
+    pthread_join(t_dummy, NULL);
+  }
+
+  pthread_t t[2];
+  pthread_create(&t[0], NULL, ThreadWrite, NULL);
+  pthread_create(&t[1], NULL, ThreadClose, NULL);
+  pthread_join(t[0], NULL);
+  pthread_join(t[1], NULL);
+}
+
+// CHECK-NOT: CHECK failed
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK:   Write of size 8
+// CHECK:     #0 close
+// CHECK:     #1 ThreadClose
+// CHECK:   Previous read of size 8
+// CHECK:     #0 write
+// CHECK:     #1 ThreadWrite
index 6801d3ffff7e4d1833084d950a75c3d8550dba54..51a64fc264d170860884268f606439fd116ccc74 100644 (file)
@@ -1,4 +1,5 @@
-// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="atexit_sleep_ms=50" %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=atexit_sleep_ms=50 %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: darwin
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
index 9418800bd336aeaae025cf9e61fa88e86adaa2c2..22bed086f7d086b6cb89c854221528efaae73dc1 100644 (file)
@@ -1,4 +1,5 @@
-// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="atexit_sleep_ms=50" %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=atexit_sleep_ms=50 %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: darwin
 #include "test.h"
 #include <errno.h>
 #include <sys/types.h>
index 3ddb417c7cb5f109b06494fb90810717ace3cab8..b345f58ad0c3da854ddd3c38971619f6452040e3 100644 (file)
@@ -1,5 +1,6 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-DIE
-// RUN: %clangxx_tsan -O1 %s -o %t && TSAN_OPTIONS="die_after_fork=0" %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-NODIE
+// RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=die_after_fork=0 %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-NODIE
+// UNSUPPORTED: darwin
 #include "test.h"
 #include <errno.h>
 #include <sys/types.h>
index a651b3c18b4e7af4befa1bf2c7ccd2969373e33f..5b8c13eb8b857bc94de0cb0f05d4a23ec41761c1 100644 (file)
@@ -1,4 +1,5 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: darwin
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
index 63cee8c4adc570ad1e6537fae614f6a3043958c5..d508552c980139580660a7e3fa002fbfb75b0931 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: %clang_tsan -O1 %s -o %t
 // RUN: %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOZUPP
-// RUN: TSAN_OPTIONS="suppressions='%s.supp' print_suppressions=1" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUPP
+// RUN: %env_tsan_opts=suppressions='%s.supp':print_suppressions=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUPP
 
 #include "test.h"
 
index 89afbe1a66a8f6a0a89b83b2e056eeedb167ac59..d103839b8bd0268c3b548f786ce56d75247194fa 100644 (file)
@@ -1,7 +1,7 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t
 
 // Make sure TSan doesn't deadlock on a file stream lock at program shutdown.
-// See https://code.google.com/p/thread-sanitizer/issues/detail?id=47
+// See https://github.com/google/sanitizers/issues/454
 #ifdef __FreeBSD__
 #define _WITH_GETLINE  // to declare getline()
 #endif
index 3128ec411749e898608c995fa75307cde7592b56..a35299619e9d959ca16195807ca32ea070aac006 100644 (file)
@@ -11,9 +11,7 @@ void *Thread(void *a) {
 
 int main() {
   barrier_init(&barrier, 2);
-  fprintf(stderr, "addr=");
-  print_address(GlobalData);
-  fprintf(stderr, "\n");
+  print_address("addr=", 1, GlobalData);
   pthread_t t;
   pthread_create(&t, 0, Thread, 0);
   GlobalData[2] = 43;
@@ -23,5 +21,5 @@ int main() {
 
 // CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
 // CHECK: WARNING: ThreadSanitizer: data race
-// CHECK: Location is global 'GlobalData' of size 40 at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}})
+// CHECK: Location is global 'GlobalData' {{(of size 40 )?}}at [[ADDR]] (global_race.cc.exe+0x{{[0-9,a-f]+}})
 
index 4ab2842e7eef474431e3c5617490ff6642af8521..95dff19978085af30644b2bb0fe160bff786cb01 100644 (file)
@@ -11,9 +11,7 @@ void *Thread(void *a) {
 
 int main() {
   barrier_init(&barrier, 2);
-  fprintf(stderr, "addr2=");
-  print_address(&x);
-  fprintf(stderr, "\n");
+  print_address("addr2=", 1, &x);
   pthread_t t;
   pthread_create(&t, 0, Thread, 0);
   x = 0;
@@ -23,5 +21,5 @@ int main() {
 
 // CHECK: addr2=[[ADDR2:0x[0-9,a-f]+]]
 // CHECK: WARNING: ThreadSanitizer: data race
-// CHECK: Location is global 'x' of size 4 at [[ADDR2]] ({{.*}}+0x{{[0-9,a-f]+}})
+// CHECK: Location is global 'x' {{(of size 4 )?}}at [[ADDR2]] ({{.*}}+0x{{[0-9,a-f]+}})
 
index 1531d7830f7894c32e1d1d426c005a3ca3852629..e0d59d284420b651853bf7d4973c19f4eaa433f6 100644 (file)
@@ -16,9 +16,7 @@ void *Thread(void *a) {
 
 int main() {
   barrier_init(&barrier, 2);
-  fprintf(stderr, "addr3=");
-  print_address(XXX::YYY::ZZZ);
-  fprintf(stderr, "\n");
+  print_address("addr3=", 1, XXX::YYY::ZZZ);
   pthread_t t;
   pthread_create(&t, 0, Thread, 0);
   XXX::YYY::ZZZ[0] = 0;
@@ -28,4 +26,4 @@ int main() {
 
 // CHECK: addr3=[[ADDR3:0x[0-9,a-f]+]]
 // CHECK: WARNING: ThreadSanitizer: data race
-// CHECK: Location is global 'XXX::YYY::ZZZ' of size 40 at [[ADDR3]] ({{.*}}+0x{{[0-9,a-f]+}})
+// CHECK: Location is global 'XXX::YYY::ZZZ' {{(of size 40 )?}}at [[ADDR3]] ({{.*}}+0x{{[0-9,a-f]+}})
index e55454b57c1d7c9be0df69023f245e920ad0a6b4..5d481c3cbcd6a9a988713da622a14c22a54f5f07 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS halt_on_error=1" %deflake %run %t | FileCheck %s
+// RUN: %clang_tsan -O1 %s -o %t && %env_tsan_opts=halt_on_error=1 %deflake %run %t | FileCheck %s
 #include "test.h"
 
 int X;
index 122ec1f4562035fc3c40aef141656e5f1452c576..d6ae72f31638e895f18b5ae1d44596a38af860bd 100644 (file)
@@ -1,9 +1,9 @@
 // RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib0.so
 // RUN: %clangxx_tsan -O1 %s -L%T -lignore_lib0 -o %t
 // RUN: echo running w/o suppressions:
-// RUN: LD_LIBRARY_PATH=%T${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOSUPP
+// RUN: env LD_LIBRARY_PATH=%T${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOSUPP
 // RUN: echo running with suppressions:
-// RUN: LD_LIBRARY_PATH=%T${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%s.supp'" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
+// RUN: env LD_LIBRARY_PATH=%T${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} %env_tsan_opts=suppressions='%s.supp' %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
 
 // Tests that interceptors coming from a library specified in called_from_lib
 // suppression are ignored.
index 1283cfdb4ef9838c4b2a1a0f41d25eee4ac1106b..e6a13a3943955475e94bb77e428f6f4c62aa5afc 100644 (file)
@@ -3,7 +3,7 @@
 // RUN: echo running w/o suppressions:
 // RUN: %deflake %run %t | FileCheck %s --check-prefix=CHECK-NOSUPP
 // RUN: echo running with suppressions:
-// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%s.supp'" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
+// RUN: %env_tsan_opts=suppressions='%s.supp' %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WITHSUPP
 
 // Tests that interceptors coming from a dynamically loaded library specified
 // in called_from_lib suppression are ignored.
index ad3107cf53a2fe3e2ac9f508471cec4c7dc2bcfa..4f584b14664ac9a1d629f4c71af44521b8aa0a55 100644 (file)
@@ -1,7 +1,7 @@
 // RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_0.so
 // RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib2_1.so
 // RUN: %clangxx_tsan -O1 %s -o %t
-// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%s.supp'" %deflake %run %t | FileCheck %s
+// RUN: %env_tsan_opts=suppressions='%s.supp' %deflake %run %t | FileCheck %s
 
 // Tests that called_from_lib suppression matched against 2 libraries
 // causes program crash (this is not supported).
index 79e4ba18128308c9b6a25c4e07fbd9955ca75a65..3f7be5cf823314afe918e4298345ddc157801f71 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -fno-sanitize=thread -shared -o %T/libignore_lib3.so
 // RUN: %clangxx_tsan -O1 %s -o %t
-// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%s.supp'" %deflake %run %t | FileCheck %s
+// RUN: %env_tsan_opts=suppressions='%s.supp' %deflake %run %t | FileCheck %s
 
 // Tests that unloading of a library matched against called_from_lib suppression
 // causes program crash (this is not supported).
index e3ed07abcf890e4be710c22d4488f7e917fba736..720f2bfcac8c00abef78c1adcca781a49e174663 100644 (file)
@@ -32,6 +32,6 @@ int main() {
 // CHECK:   #0 memset
 // CHECK:   #1 MemSetThread
 // CHECK:  Previous write
-// CHECK:   #0 memcpy
+// CHECK:   #0 {{(memcpy|memmove)}}
 // CHECK:   #1 MemCpyThread
 
index 015a0b1f43c6371f80c1ce179c37d491f1245efa..0745ade6c479888f6969c8ad88a10b1f77f66f6c 100644 (file)
@@ -1,4 +1,8 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// This test fails on powerpc64 on both VMA (44 and 46).
+// The Tsan report is returning wrong information about
+// the location of the race.
+// XFAIL: powerpc64
 #include "java.h"
 
 void foobar() {
index b0c6671832d8e8cac5d20bba0a30ae7eabbc7a20..d141fc2285128d3644e9a8b8575b93ee308a97a7 100644 (file)
@@ -12,20 +12,25 @@ def get_required_attr(config, attr_name):
   return attr_value
 
 # Setup config name.
-config.name = 'ThreadSanitizer'
+config.name = 'ThreadSanitizer' + config.name_suffix
 
 # Setup source root.
 config.test_source_root = os.path.dirname(__file__)
 
 # Setup environment variables for running ThreadSanitizer.
-tsan_options = "atexit_sleep_ms=0"
+default_tsan_opts = "atexit_sleep_ms=0"
 
 if config.host_os == 'Darwin':
   # On Darwin, we default to `abort_on_error=1`, which would make tests run
   # much slower. Let's override this and run lit tests with 'abort_on_error=0'.
-  tsan_options += ':abort_on_error=0'
+  default_tsan_opts += ':abort_on_error=0'
 
-config.environment['TSAN_OPTIONS'] = tsan_options
+# Platform-specific default TSAN_OPTIONS for lit tests.
+if default_tsan_opts:
+  config.environment['TSAN_OPTIONS'] = default_tsan_opts
+  default_tsan_opts += ':'
+config.substitutions.append(('%env_tsan_opts=',
+                             'env TSAN_OPTIONS=' + default_tsan_opts))
 
 # GCC driver doesn't add necessary compile/link flags with -fsanitize=thread.
 if config.compiler_id == 'GNU':
@@ -34,16 +39,18 @@ else:
   extra_cflags = []
 
 # Setup default compiler flags used with -fsanitize=thread option.
-clang_tsan_cflags = ["-fsanitize=thread",
-                     "-Wall",
-                     "-m64"] + config.debug_info_flags + extra_cflags
+clang_tsan_cflags = (["-fsanitize=thread",
+                      "-Wall"] +
+                      [config.target_cflags] +
+                      config.debug_info_flags +
+                      extra_cflags)
 clang_tsan_cxxflags = config.cxx_mode_flags + clang_tsan_cflags
 # Add additional flags if we're using instrumented libc++.
 # Instrumented libcxx currently not supported on Darwin.
 if config.has_libcxx and config.host_os != 'Darwin':
   # FIXME: Dehardcode this path somehow.
   libcxx_path = os.path.join(config.compiler_rt_obj_root, "lib",
-                             "tsan", "libcxx_tsan")
+                             "tsan", "libcxx_tsan_" + config.arch)
   libcxx_incdir = os.path.join(libcxx_path, "include", "c++", "v1")
   libcxx_libdir = os.path.join(libcxx_path, "lib")
   libcxx_so = os.path.join(libcxx_libdir, "libc++.so")
@@ -64,7 +71,7 @@ config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os)))
 config.substitutions.append( ("%deflake ", os.path.join(os.path.dirname(__file__), "deflake.bash")) )
 
 # Default test suffixes.
-config.suffixes = ['.c', '.cc', '.cpp']
+config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm']
 
 # ThreadSanitizer tests are currently supported on FreeBSD, Linux and Darwin.
 if config.host_os not in ['FreeBSD', 'Linux', 'Darwin']:
index 5190b211177de457abd71c93a2f404375a6fa2d8..08d4c1e00c7b09e47544aa11a3380a630d8929cd 100644 (file)
@@ -1,7 +1,10 @@
 ## Autogenerated by LLVM/Clang configuration.
 # Do not edit!
 
+config.name_suffix = "@TSAN_TEST_CONFIG_SUFFIX@"
+config.arch = "@arch@"
 config.has_libcxx = @TSAN_HAS_LIBCXX@
+config.target_cflags = "@TSAN_TEST_TARGET_CFLAGS@"
 
 # Load common config for all compiler-rt lit tests.
 lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
index b7934b82df73a034b2e5ecc2974853de495d053b..f02280895f8363c3c258d4c468e5d49deaa65b55 100644 (file)
@@ -3,7 +3,7 @@
 // symbolized correctly.
 
 // RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so
-// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %t -rdynamic && %deflake %run %t | FileCheck %s
 
 #ifdef BUILD_SO
 
index 9ac6b99c2b5506b53b83ce71726423e29e7a0c1f..d642067391fd63a3d5e3283206f5b7d9dc1ad64d 100644 (file)
@@ -1,8 +1,7 @@
 // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
 
-// Longjmp assembly has not been implemented for mips64 or aarch64 yet
+// Longjmp assembly has not been implemented for mips64 yet
 // XFAIL: mips64
-// XFAIL: aarch64
 
 #include <stdio.h>
 #include <stdlib.h>
index dfbd84b495f5cfcd182fa7f671ee788bc34bc0c2..eee423dc5fbe0be9118e392612eddd318d6fdfa2 100644 (file)
@@ -1,8 +1,7 @@
 // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
 
-// Longjmp assembly has not been implemented for mips64 or aarch64 yet
+// Longjmp assembly has not been implemented for mips64 yet
 // XFAIL: mips64
-// XFAIL: aarch64
 
 #include <stdio.h>
 #include <stdlib.h>
index df426fac286f928b082b4aa526977020caee26c8..79965c4193d319f8ab09bbb2a94d3e37da4703e5 100644 (file)
@@ -1,8 +1,7 @@
 // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
 
-// Longjmp assembly has not been implemented for mips64 or aarch64 yet
+// Longjmp assembly has not been implemented for mips64 yet
 // XFAIL: mips64
-// XFAIL: aarch64
 
 #include <pthread.h>
 #include <stdio.h>
index 9f5d6d1a83336b6a85af44c183db369e953909a5..c8583997331e24b09229dffb0cfead60b6bba680 100644 (file)
@@ -1,8 +1,7 @@
 // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
 
-// Longjmp assembly has not been implemented for mips64 or aarch64 yet
+// Longjmp assembly has not been implemented for mips64 yet
 // XFAIL: mips64
-// XFAIL: aarch64
 
 #include <pthread.h>
 #include <stdio.h>
index dadc94484f01e179210d4eb596e23f8733176daf..b2f9b0f57798a36b027103cfbc4fd57a4d102d37 100644 (file)
@@ -1,5 +1,5 @@
 // RUN: %clangxx_tsan -O1 %s -o %t
-// RUN: TSAN_OPTIONS=allocator_may_return_null=1 %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t 2>&1 | FileCheck %s
 #include <stdio.h>
 #include <stdlib.h>
 
index f23d7f76e604c9f01cac27e7a84d14741a2fab8f..0411f29a9504e78c4ac00663f5ba63db2b247873 100644 (file)
@@ -5,11 +5,15 @@
 #include <sys/mman.h>
 
 // Test for issue:
-// https://code.google.com/p/thread-sanitizer/issues/detail?id=5
+// https://github.com/google/sanitizers/issues/412
 
 // MAP_32BIT flag for mmap is supported only for x86_64.
 // XFAIL: mips64
 // XFAIL: aarch64
+// XFAIL: powerpc64
+
+// MAP_32BIT doesn't exist on OS X.
+// UNSUPPORTED: darwin
 
 void *Thread(void *ptr) {
   *(int*)ptr = 42;
index 465d1d020802b75f9c577c6932d8d30cb5820fc3..b76f427e121c1e7af690c87c9cc4f60aecc3ddc2 100644 (file)
@@ -23,7 +23,7 @@ void *Thread2(void *x) {
 
 int main() {
   barrier_init(&barrier, 2);
-  fprintf(stderr, "addr=%p\n", &data0[5]);
+  print_address("addr=", 1, &data0[5]);
   pthread_t t[2];
   pthread_create(&t[0], NULL, Thread1, NULL);
   pthread_create(&t[1], NULL, Thread2, NULL);
@@ -35,7 +35,7 @@ int main() {
 // CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
 // CHECK: WARNING: ThreadSanitizer: data race
 // CHECK:   Write of size 1 at [[ADDR]] by thread T2:
-// CHECK:     #0 memcpy
+// CHECK:     #0 {{(memcpy|memmove)}}
 // CHECK:     #1 Thread2
 // CHECK:   Previous read of size 1 at [[ADDR]] by thread T1:
 // CHECK:     #0 memcmp
index d49577306d6c3092479debf5ccfe08da745b2b3a..4a098c0405fc09a536339a126bbd05bfcd5047b2 100644 (file)
@@ -22,7 +22,7 @@ void *Thread2(void *x) {
 
 int main() {
   barrier_init(&barrier, 2);
-  fprintf(stderr, "addr=%p\n", &data[5]);
+  print_address("addr=", 1, &data[5]);
   pthread_t t[2];
   pthread_create(&t[0], NULL, Thread1, NULL);
   pthread_create(&t[1], NULL, Thread2, NULL);
@@ -34,8 +34,8 @@ int main() {
 // CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
 // CHECK: WARNING: ThreadSanitizer: data race
 // CHECK:   Write of size 1 at [[ADDR]] by thread T2:
-// CHECK:     #0 memcpy
+// CHECK:     #0 {{(memcpy|memmove)}}
 // CHECK:     #1 Thread2
 // CHECK:   Previous write of size 1 at [[ADDR]] by thread T1:
-// CHECK:     #0 memcpy
+// CHECK:     #0 {{(memcpy|memmove)}}
 // CHECK:     #1 Thread1
index d31ea3603dc553a8ce16689d57e1b4a84473d6c9..764e954f2b8eeb56a94505bcbb869c3b134a9c0b 100644 (file)
@@ -16,13 +16,15 @@ int main() {
   const size_t kLog2Size = 39;
 #elif defined(__mips64) || defined(__aarch64__)
   const size_t kLog2Size = 32;
+#elif defined(__powerpc64__)
+  const size_t kLog2Size = 39;
 #endif
   const uintptr_t kLocation = 0x40ULL << kLog2Size;
   void *p = mmap(
       reinterpret_cast<void*>(kLocation),
       1ULL << kLog2Size,
       PROT_READ|PROT_WRITE,
-      MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,
+      MAP_PRIVATE|MAP_ANON|MAP_NORESERVE,
       -1, 0);
   fprintf(stderr, "DONE %p %d\n", p, errno);
   return p == MAP_FAILED;
index 5e3904adf90bacead489d8d6bad1aa115707b290..e01e7e92b8f93662b10c776aa6d3e13e92fb428d 100644 (file)
@@ -9,8 +9,11 @@ void *SubWorker(void *arg) {
   for (int i = 0; i < 500; i++) {
     int *ptr = (int*)mmap(0, kMmapSize, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (ptr == MAP_FAILED)
+      exit(printf("mmap failed: %d\n", errno));
     *ptr = 42;
-    munmap(ptr, kMmapSize);
+    if (munmap(ptr, kMmapSize))
+      exit(printf("munmap failed: %d\n", errno));
   }
   return 0;
 }
@@ -18,29 +21,41 @@ void *SubWorker(void *arg) {
 void *Worker1(void *arg) {
   (void)arg;
   pthread_t th[4];
-  for (int i = 0; i < 4; i++)
-    pthread_create(&th[i], 0, SubWorker, 0);
-  for (int i = 0; i < 4; i++)
-    pthread_join(th[i], 0);
+  for (int i = 0; i < 4; i++) {
+    if (pthread_create(&th[i], 0, SubWorker, 0))
+      exit(printf("pthread_create failed: %d\n", errno));
+  }
+  for (int i = 0; i < 4; i++) {
+    if (pthread_join(th[i], 0))
+      exit(printf("pthread_join failed: %d\n", errno));
+  }
   return 0;
 }
 
 void *Worker(void *arg) {
   (void)arg;
   pthread_t th[4];
-  for (int i = 0; i < 4; i++)
-    pthread_create(&th[i], 0, Worker1, 0);
-  for (int i = 0; i < 4; i++)
-    pthread_join(th[i], 0);
+  for (int i = 0; i < 4; i++) {
+    if (pthread_create(&th[i], 0, Worker1, 0))
+      exit(printf("pthread_create failed: %d\n", errno));
+  }
+  for (int i = 0; i < 4; i++) {
+    if (pthread_join(th[i], 0))
+      exit(printf("pthread_join failed: %d\n", errno));
+  }
   return 0;
 }
 
 int main() {
   pthread_t th[4];
-  for (int i = 0; i < 4; i++)
-    pthread_create(&th[i], 0, Worker, 0);
-  for (int i = 0; i < 4; i++)
-    pthread_join(th[i], 0);
+  for (int i = 0; i < 4; i++) {
+    if (pthread_create(&th[i], 0, Worker, 0))
+      exit(printf("pthread_create failed: %d\n", errno));
+  }
+  for (int i = 0; i < 4; i++) {
+    if (pthread_join(th[i], 0))
+      exit(printf("pthread_join failed: %d\n", errno));
+  }
   fprintf(stderr, "DONE\n");
 }
 
index c67e81e09cda9a1d765b247fe9b266ebfdbb113a..e2496d099ef1e98af87cec6c55d5909943201c8c 100644 (file)
@@ -18,8 +18,8 @@ void *Thread2(void *x) {
 int main() {
   barrier_init(&barrier, 2);
   int *data = new int(42);
-  fprintf(stderr, "ptr1=%p\n", data);
-  fprintf(stderr, "ptr2=%p\n", (char*)data + 2);
+  print_address("ptr1=", 1, data);
+  print_address("ptr2=", 1, (char*)data + 2);
   pthread_t t[2];
   pthread_create(&t[0], NULL, Thread1, data);
   pthread_create(&t[1], NULL, Thread2, data);
index 460267359cec0f6d96e58a7cf6c3959dc4e16ba0..73c53f51233afb3bedc6f84062b92ce21a6c23cf 100644 (file)
@@ -18,8 +18,8 @@ void *Thread2(void *x) {
 int main() {
   barrier_init(&barrier, 2);
   int *data = new int(42);
-  fprintf(stderr, "ptr1=%p\n", data);
-  fprintf(stderr, "ptr2=%p\n", (char*)data + 2);
+  print_address("ptr1=", 1, data);
+  print_address("ptr2=", 1, (char*)data + 2);
   pthread_t t[2];
   pthread_create(&t[0], NULL, Thread1, data);
   pthread_create(&t[1], NULL, Thread2, data);
index 85d19a0d0c3543f27478941cd6c5abd3a0310fc1..32659d4eec0db7e78b996a2ef2f2e22798bed646 100644 (file)
@@ -1,11 +1,11 @@
 // RUN: %clangxx_tsan %s -o %t
 // RUN:                                 not %run %t 2>&1 | FileCheck %s
-// RUN: TSAN_OPTIONS=detect_deadlocks=1 not %run %t 2>&1 | FileCheck %s
-// RUN: TSAN_OPTIONS=detect_deadlocks=0     %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED
+// RUN: %env_tsan_opts=detect_deadlocks=1 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=detect_deadlocks=0     %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED
 // RUN: echo "deadlock:main" > %t.supp
-// RUN: TSAN_OPTIONS="suppressions='%t.supp'" %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED
+// RUN: %env_tsan_opts=suppressions='%t.supp' %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED
 // RUN: echo "deadlock:zzzz" > %t.supp
-// RUN: TSAN_OPTIONS="suppressions='%t.supp'" not %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=suppressions='%t.supp' not %run %t 2>&1 | FileCheck %s
 #include <pthread.h>
 #include <stdio.h>
 
index 407cfe5bd9f10fc14b74a7aaae3ad991c6bdf7ed..8403b3401743a14631a8ec98c53af7389153ab4e 100644 (file)
@@ -26,7 +26,7 @@ int main() {
   // CHECK:   Previous write of size 4 at {{.*}} by thread T2:
   // CHECK:   Mutex [[M1]] (0x{{.*}}) created at:
   // CHECK:     #0 pthread_mutex_init
-  // CHECK:     #1 main {{.*}}/mutexset1.cc:[[@LINE+1]]
+  // CHECK:     #1 main {{.*}}mutexset1.cc:[[@LINE+1]]
   pthread_mutex_init(&mtx, 0);
   pthread_t t[2];
   pthread_create(&t[0], NULL, Thread1, NULL);
index 2a3e5bb95e875074f9991ba39494c661f90fc1e2..5f7c0c41bb06a35f3613fa5e33c0d0ca0f427124 100644 (file)
@@ -26,7 +26,7 @@ int main() {
   // CHECK:                     (mutexes: write [[M1:M[0-9]+]]):
   // CHECK:   Mutex [[M1]] (0x{{.*}}) created at:
   // CHECK:     #0 pthread_mutex_init
-  // CHECK:     #1 main {{.*}}/mutexset2.cc:[[@LINE+1]]
+  // CHECK:     #1 main {{.*}}mutexset2.cc:[[@LINE+1]]
   pthread_mutex_init(&mtx, 0);
   pthread_t t[2];
   pthread_create(&t[0], NULL, Thread1, NULL);
index ce64cf86e37c8c4eacdc82aaf1ef26ea4f4d0ca9..24a9d9bf01ce2d6d00f80d346debc65ba7707652 100644 (file)
@@ -29,10 +29,10 @@ int main() {
   // CHECK:   Previous write of size 4 at {{.*}} by thread T2:
   // CHECK:   Mutex [[M1]] (0x{{.*}}) created at:
   // CHECK:     #0 pthread_mutex_init
-  // CHECK:     #1 main {{.*}}/mutexset3.cc:[[@LINE+4]]
+  // CHECK:     #1 main {{.*}}mutexset3.cc:[[@LINE+4]]
   // CHECK:   Mutex [[M2]] (0x{{.*}}) created at:
   // CHECK:     #0 pthread_mutex_init
-  // CHECK:     #1 main {{.*}}/mutexset3.cc:[[@LINE+2]]
+  // CHECK:     #1 main {{.*}}mutexset3.cc:[[@LINE+2]]
   pthread_mutex_init(&mtx1, 0);
   pthread_mutex_init(&mtx2, 0);
   pthread_t t[2];
index b961efd2136c78dccddab24a0e7f4afba8097c24..5d8ea9e400da0190844ef7f2b4759bab2c7cfc8e 100644 (file)
@@ -29,10 +29,10 @@ int main() {
   // CHECK:                 (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]):
   // CHECK:   Mutex [[M1]] (0x{{.*}}) created at:
   // CHECK:     #0 pthread_mutex_init
-  // CHECK:     #1 main {{.*}}/mutexset4.cc:[[@LINE+4]]
+  // CHECK:     #1 main {{.*}}mutexset4.cc:[[@LINE+4]]
   // CHECK:   Mutex [[M2]] (0x{{.*}}) created at:
   // CHECK:     #0 pthread_mutex_init
-  // CHECK:     #1 main {{.*}}/mutexset4.cc:[[@LINE+2]]
+  // CHECK:     #1 main {{.*}}mutexset4.cc:[[@LINE+2]]
   pthread_mutex_init(&mtx1, 0);
   pthread_mutex_init(&mtx2, 0);
   pthread_t t[2];
index 8ef9af0ced52440f5a37e07b1fc61fa6351de187..b5f4e7794929dd201480c1f5624dd4572d27a8ec 100644 (file)
@@ -30,10 +30,10 @@ int main() {
   // CHECK:                              (mutexes: write [[M2:M[0-9]+]]):
   // CHECK:   Mutex [[M1]] (0x{{.*}}) created at:
   // CHECK:     #0 pthread_mutex_init
-  // CHECK:     #1 main {{.*}}/mutexset5.cc:[[@LINE+4]]
+  // CHECK:     #1 main {{.*}}mutexset5.cc:[[@LINE+4]]
   // CHECK:   Mutex [[M2]] (0x{{.*}}) created at:
   // CHECK:     #0 pthread_mutex_init
-  // CHECK:     #1 main {{.*}}/mutexset5.cc:[[@LINE+5]]
+  // CHECK:     #1 main {{.*}}mutexset5.cc:[[@LINE+5]]
   pthread_mutex_init(&mtx1, 0);
   pthread_mutex_init(&mtx2, 0);
   pthread_t t[2];
index f4251db6970e8ed92fc4a176d2f69f84ca7e1644..ca349aaeee7a8cafa5cafc1c833af9b6fff9f272 100644 (file)
@@ -3,7 +3,7 @@
 
 int Global;
 pthread_mutex_t mtx1;
-pthread_spinlock_t mtx2;
+pthread_mutex_t mtx2;
 pthread_rwlock_t mtx3;
 
 void *Thread1(void *x) {
@@ -17,10 +17,10 @@ void *Thread1(void *x) {
 void *Thread2(void *x) {
   pthread_mutex_lock(&mtx1);
   pthread_mutex_unlock(&mtx1);
-  pthread_spin_lock(&mtx2);
+  pthread_mutex_lock(&mtx2);
   pthread_rwlock_rdlock(&mtx3);
   Global--;
-  pthread_spin_unlock(&mtx2);
+  pthread_mutex_unlock(&mtx2);
   pthread_rwlock_unlock(&mtx3);
   barrier_wait(&barrier);
   return NULL;
@@ -34,13 +34,13 @@ int main() {
   // CHECK:   Previous write of size 4 at {{.*}} by thread T2
   // CHECK:               (mutexes: write [[M2:M[0-9]+]], read [[M3:M[0-9]+]]):
   // CHECK:   Mutex [[M1]] (0x{{.*}}) created at:
-  // CHECK:     #1 main {{.*}}/mutexset6.cc:[[@LINE+5]]
+  // CHECK:     #1 main {{.*}}mutexset6.cc:[[@LINE+5]]
   // CHECK:   Mutex [[M2]] (0x{{.*}}) created at:
-  // CHECK:     #1 main {{.*}}/mutexset6.cc:[[@LINE+4]]
+  // CHECK:     #1 main {{.*}}mutexset6.cc:[[@LINE+4]]
   // CHECK:   Mutex [[M3]] (0x{{.*}}) created at:
-  // CHECK:     #1 main {{.*}}/mutexset6.cc:[[@LINE+3]]
+  // CHECK:     #1 main {{.*}}mutexset6.cc:[[@LINE+3]]
   pthread_mutex_init(&mtx1, 0);
-  pthread_spin_init(&mtx2, 0);
+  pthread_mutex_init(&mtx2, 0);
   pthread_rwlock_init(&mtx3, 0);
   pthread_t t[2];
   pthread_create(&t[0], NULL, Thread1, NULL);
@@ -48,6 +48,6 @@ int main() {
   pthread_join(t[0], NULL);
   pthread_join(t[1], NULL);
   pthread_mutex_destroy(&mtx1);
-  pthread_spin_destroy(&mtx2);
+  pthread_mutex_destroy(&mtx2);
   pthread_rwlock_destroy(&mtx3);
 }
index 40d5d043dedd36ac74b289fe969bb209e3145837..69854e2ffa0a8bb3ed7ccf442fe2e2e89b7e9647 100644 (file)
@@ -26,7 +26,7 @@ int main() {
   // CHECK:   Previous write of size 4 at {{.*}} by thread T2:
   // CHECK:   Mutex [[M1]] (0x{{.*}}) created at:
   // CHECK:     #0 pthread_mutex_init
-  // CHECK:     #1 main {{.*}}/mutexset8.cc
+  // CHECK:     #1 main {{.*}}mutexset8.cc
   mtx = new pthread_mutex_t;
   pthread_mutex_init(mtx, 0);
   pthread_t t[2];
diff --git a/src/compiler-rt/test/tsan/pie_test.cc b/src/compiler-rt/test/tsan/pie_test.cc
new file mode 100644 (file)
index 0000000..8635f9c
--- /dev/null
@@ -0,0 +1,12 @@
+// Check if tsan work with PIE binaries.
+// RUN: %clang_tsan %s -pie -fpic -o %t && %run %t
+
+// Some kernels might map PIE segments outside the current segment
+// mapping defined for x86 [1].
+// [1] https://git.kernel.org/linus/d1fd836dcf00d2028c700c7e44d2c23404062c90
+
+// UNSUPPORTED: x86
+
+int main(void) {
+  return 0;
+}
index 9116c956e30e7a8a1041b7ccb314d54c058df822..c8414f834b44ff3146cdeb0558f5cff0bca2eb84 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: %clang_tsan -O2 %s -o %t
-// RUN: ASAN_OPTIONS=check_printf=1 %run %t 2>&1 | FileCheck %s
-// RUN: ASAN_OPTIONS=check_printf=0 %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=check_printf=1 %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=check_printf=0 %run %t 2>&1 | FileCheck %s
 // RUN: %run %t 2>&1 | FileCheck %s
 
 #include <stdio.h>
index 4aeec82b6850820cf8f0789b158ed271206c9626..01107ee6692c1d78d5c1d3f0ba9bbdf1b4f12770 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: %clang_tsan -O1 %s -lpthread -o %t && %deflake %run %t | FileCheck %s
 // Regression test for
-// https://code.google.com/p/thread-sanitizer/issues/detail?id=61
+// https://github.com/google/sanitizers/issues/468
 // When the data race was reported, pthread_atfork() handler used to be
 // executed which caused another race report in the same thread, which resulted
 // in a deadlock.
index cf8a4cb992748af8945ee4f79ba3943f00c911da..66fd339eb95bdc9b58ba18bbf5dd0ac55f247d1c 100644 (file)
@@ -1,4 +1,8 @@
 // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+
+// pthread barriers are not available on OS X
+// UNSUPPORTED: darwin
+
 #include "test.h"
 
 pthread_barrier_t B;
index 98c028e19fdd28d7b4f4ac543e0e3234ce6b5876..49adb62312309c104578ef438b2bcf5e6066bd9a 100644 (file)
@@ -1,4 +1,8 @@
 // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+
+// pthread barriers are not available on OS X
+// UNSUPPORTED: darwin
+
 #include <pthread.h>
 #include <stdio.h>
 #include <stddef.h>
index a66e0c4f93f76a7d99c7b5cf5c141e277973c731..6c2defc835a633062f385720c3d11ba5871c267e 100644 (file)
@@ -2,6 +2,7 @@
 #include <pthread.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include "test.h"
 
 void *Thread1(void *p) {
   *(int*)p = 42;
@@ -26,7 +27,7 @@ int main() {
   pthread_t t[2];
   pthread_create(&t[0], 0, AllocThread, 0);
   pthread_join(t[0], &p);
-  fprintf(stderr, "addr=%p\n", p);
+  print_address("addr=", 1, p);
   pthread_create(&t[0], 0, Thread1, (char*)p + 16);
   pthread_create(&t[1], 0, Thread2, (char*)p + 16);
   pthread_join(t[0], 0);
index 7bd461bf37311a043d099079a9684e95617237d7..d998fdca2df31f30af0c2b86e11fd85c42a24aab 100644 (file)
@@ -1,4 +1,7 @@
 // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// This test fails on powerpc64 (VMA=46).
+// The size of the write reported by Tsan for T1 is 8 instead of 1.
+// XFAIL: powerpc64-unknown-linux-gnu
 #include "test.h"
 
 pthread_mutex_t Mtx;
@@ -35,7 +38,7 @@ int main() {
 // CHECK:      WARNING: ThreadSanitizer: data race
 // CHECK-NEXT:   Atomic read of size 1 at {{.*}} by thread T2:
 // CHECK-NEXT:     #0 pthread_mutex_lock
-// CHECK-NEXT:     #1 Thread2{{.*}} {{.*}}race_on_mutex.c:18{{(:3)?}} ({{.*}})
+// CHECK-NEXT:     #1 Thread2{{.*}} {{.*}}race_on_mutex.c:21{{(:3)?}} ({{.*}})
 // CHECK:        Previous write of size 1 at {{.*}} by thread T1:
 // CHECK-NEXT:     #0 pthread_mutex_init {{.*}} ({{.*}})
-// CHECK-NEXT:     #1 Thread1{{.*}} {{.*}}race_on_mutex.c:8{{(:3)?}} ({{.*}})
+// CHECK-NEXT:     #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}})
index b50b69677d4b71b9d0d52f5c4ce5ec0f474e6aec..dd40daeb5c196bcce3f36409f97b7c8c5ab53172 100644 (file)
@@ -1,5 +1,5 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t | FileCheck %s
-// Regtest for https://code.google.com/p/thread-sanitizer/issues/detail?id=40
+// Regtest for https://github.com/google/sanitizers/issues/447
 // This is a correct program and tsan should not report a race.
 #include "test.h"
 
index 7d42dbf3b4bf1511b2b805ada3fcdf085b4fa6ec..bd5c1bd5f44526a43884c6c2452d84eb2d4e1506 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: echo "race_top:TopFunction" > %t.supp
 // RUN: %clangxx_tsan -O1 %s -o %t
-// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%t.supp'" %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=suppressions='%t.supp' %run %t 2>&1 | FileCheck %s
 // RUN: rm %t.supp
 #include "test.h"
 
index 881e661ba789edcef0e812131b04a29db2c81294..e34385a9b59c642df6510461ee242efe9618fc4e 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: echo "race_top:TopFunction" > %t.supp
 // RUN: %clangxx_tsan -O1 %s -o %t
-// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%t.supp'" %deflake %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=suppressions='%t.supp' %deflake %run %t 2>&1 | FileCheck %s
 // RUN: rm %t.supp
 #include "test.h"
 
index 5576f1258970fed322452c3f0e87256b1770fa76..feb1117e80abddd115b611ca8081d44bd7379f3a 100644 (file)
@@ -8,6 +8,7 @@
 #include <errno.h>
 #include <vector>
 #include <algorithm>
+#include <sys/time.h>
 
 const int kThreads = 4;
 const int kMutexes = 16 << 10;
@@ -165,9 +166,9 @@ void *Thread(void *seed) {
 }
 
 int main() {
-  timespec ts;
-  clock_gettime(CLOCK_MONOTONIC, &ts);
-  unsigned s = (unsigned)ts.tv_nsec;
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  unsigned s = tv.tv_sec + tv.tv_usec;
   fprintf(stderr, "seed %d\n", s);
   srand(s);
   for (int i = 0; i < kMutexes; i++)
index 67a6fd14dbcb98ef11d13e0520e25a88f71a71d8..9dbb6577e1c60532e47078eb78c00286e76b3174 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="flush_memory_ms=1 memory_limit_mb=1" %run %t 2>&1 | FileCheck %s
+// RUN: %clang_tsan -O1 %s -o %t && %env_tsan_opts=flush_memory_ms=1:memory_limit_mb=1 %run %t 2>&1 | FileCheck %s
 #include "test.h"
 #include <sys/types.h>
 #include <unistd.h>
@@ -7,11 +7,11 @@
 // Test that setuid call works in presence of stoptheworld.
 
 int main() {
-  struct timespec tp0, tp1;
-  clock_gettime(CLOCK_MONOTONIC, &tp0);
-  clock_gettime(CLOCK_MONOTONIC, &tp1);
-  while (tp1.tv_sec - tp0.tv_sec < 3) {
-    clock_gettime(CLOCK_MONOTONIC, &tp1);
+  unsigned long long tp0, tp1;
+  tp0 = monotonic_clock_ns();
+  tp1 = monotonic_clock_ns();
+  while (tp1 - tp0 < 3 * 1000000000ull) {
+    tp1 = monotonic_clock_ns();
     setuid(0);
   }
   fprintf(stderr, "DONE\n");
index f5eae745d4072a7f6dc55cb485147467686ece85..beb2e0266e508b2a793dc267077e07b5fc3502fb 100644 (file)
@@ -6,17 +6,16 @@
 #include <semaphore.h>
 
 // Test that signals can be delivered to blocked pthread_cond_wait.
-// https://code.google.com/p/thread-sanitizer/issues/detail?id=91
+// https://github.com/google/sanitizers/issues/498
 
 int g_thread_run = 1;
 pthread_mutex_t mutex;
 pthread_cond_t cond;
-sem_t sem;
 
 void sig_handler(int sig) {
   (void)sig;
   write(1, "SIGNAL\n", sizeof("SIGNAL\n") - 1);
-  sem_post(&sem);
+  barrier_wait(&barrier);
 }
 
 void* my_thread(void* arg) {
@@ -28,7 +27,11 @@ void* my_thread(void* arg) {
 }
 
 int main() {
-  sem_init(&sem, 0, 0);
+  barrier_init(&barrier, 2);
+
+  pthread_mutex_init(&mutex, 0);
+  pthread_cond_init(&cond, 0);
+
   signal(SIGUSR1, &sig_handler);
   pthread_t thr;
   pthread_create(&thr, 0, &my_thread, 0);
@@ -36,8 +39,7 @@ int main() {
   // (can't use barrier_wait for that)
   sleep(1);
   pthread_kill(thr, SIGUSR1);
-  while (sem_wait(&sem) == -1 && errno == EINTR) {
-  }
+  barrier_wait(&barrier);
   pthread_mutex_lock(&mutex);
   g_thread_run = 0;
   pthread_cond_signal(&cond);
index 5354cdeeb77786ebdc87f7be7806947f74c51369..e13e156fdf663e19e9a8a74a53d9a7b7ddcbc36a 100644 (file)
@@ -1,4 +1,8 @@
 // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// This test fails on powerpc64 BE (VMA=44), it does not appear to be
+// a functional problem, but the Tsan report is missing some info.
+// XFAIL: powerpc64-unknown-linux-gnu
+
 #include "test.h"
 #include <signal.h>
 #include <sys/types.h>
index c02917f49f11fac4d74462107d0149f87ab3a1b9..45e24626cbfab4f66d50a38e2cb3f205e9f6bc1c 100644 (file)
@@ -1,11 +1,14 @@
 // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
 
 // Test case for longjumping out of signal handler:
-// https://code.google.com/p/thread-sanitizer/issues/detail?id=75
+// https://github.com/google/sanitizers/issues/482
 
-// Longjmp assembly has not been implemented for mips64 or aarch64 yet
+// Longjmp assembly has not been implemented for mips64 yet
 // XFAIL: mips64
-// XFAIL: aarch64
+// This test fails on powerpc64 BE (VMA=44), a segmentation fault
+// error happens at the second assignment
+// "((volatile int *volatile)mem)[1] = 1".
+// XFAIL: powerpc64-unknown-linux-gnu
 
 #include <setjmp.h>
 #include <signal.h>
 #include <stdio.h>
 #include <sys/mman.h>
 
+#ifdef __APPLE__
+#define SIGNAL_TO_HANDLE SIGBUS
+#else
+#define SIGNAL_TO_HANDLE SIGSEGV
+#endif
+
 sigjmp_buf fault_jmp;
 volatile int fault_expected;
 
@@ -45,7 +54,7 @@ int main() {
     exit(1);
   }
 
-  if (sigaction(SIGSEGV, &act, NULL)) {
+  if (sigaction(SIGNAL_TO_HANDLE, &act, NULL)) {
     perror("sigaction");
     exit(1);
   }
index 67fc9c0ec9a3259f147afb51661b9b9e109e9aee..40be2d01502b6f5c973247351f8c3cb96e3fa06f 100644 (file)
@@ -1,7 +1,7 @@
 // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
 
 // Test case for recursive signal handlers, adopted from:
-// https://code.google.com/p/thread-sanitizer/issues/detail?id=71
+// https://github.com/google/sanitizers/issues/478
 
 // REQUIRES: disabled
 
index aec98dc399e95bae5fed09916f76db008026c4ed..82758d882382f6b8c0720185fa13e3942a94f3b8 100644 (file)
@@ -1,4 +1,5 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: darwin
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
index 6ff19d3bd12092afad8575d1c8f9cc19d271145e..b529a1859f52a6c160ba73dede9efeeb52d58222 100644 (file)
@@ -1,4 +1,5 @@
 // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: darwin
 #include "test.h"
 #include <signal.h>
 #include <sys/types.h>
index 8eda80a522642deba3dcce6e459e472770e38c6d..aa91d1ddeb101cc01aa4e4a4db21fd61479e2de5 100644 (file)
@@ -1,4 +1,5 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: darwin
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
index 5ea9e84b085a5632186adbc832b3bb39e9336b29..d2bc5cb1b2827691e0f5447200cb11481a6a3bcc 100644 (file)
@@ -1,7 +1,7 @@
 // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
 #include "test.h"
 
-// Test case https://code.google.com/p/thread-sanitizer/issues/detail?id=87
+// Test case https://github.com/google/sanitizers/issues/494
 // Tsan sees false HB edge on address pointed to by syncp variable.
 // It is false because when acquire is done syncp points to a var in one frame,
 // and during release it points to a var in a different frame.
@@ -31,7 +31,8 @@ void *Thread(void *x) {
 }
 
 void __attribute__((noinline)) foobar() {
-  long s;
+  __attribute__((aligned(64))) long s;
+
   addr = &s;
   __atomic_store_n(&s, 0, __ATOMIC_RELAXED);
   __atomic_store_n(&syncp, &s, __ATOMIC_RELEASE);
@@ -40,7 +41,8 @@ void __attribute__((noinline)) foobar() {
 }
 
 void __attribute__((noinline)) barfoo() {
-  long s;
+  __attribute__((aligned(64))) long s;
+
   if (addr != &s) {
     printf("address mismatch addr=%p &s=%p\n", addr, &s);
     exit(1);
index c7b9bb99eb1755b9428a72c19dc558abf52206a1..8928162cfb8a1a69aad121e20f85dd5f5329460e 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%s.supp'" %run %t 2>&1 | FileCheck %s
+// RUN: %clang_tsan -O1 %s -o %t && %env_tsan_opts=suppressions='%s.supp' %run %t 2>&1 | FileCheck %s
 #include <pthread.h>
 #include <stdio.h>
 
index 45c30481f0cd1210b02ee36862409b1095fb3a9a..7a88434db820505ee4b95f4985b9b8e983793560 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%s.supp'" %run %t 2>&1 | FileCheck %s
+// RUN: %clang_tsan -O1 %s -o %t && %env_tsan_opts=suppressions='%s.supp' %run %t 2>&1 | FileCheck %s
 #include "test.h"
 
 int Global;
index 24ecd8ef119fd85ddf7ac0321b5c9db5052d1faf..b6566a80178d193bc6375d68ad75c3db3f05d804 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%s.supp'" %run %t 2>&1 | FileCheck %s
+// RUN: %clang_tsan -O1 %s -o %t && %env_tsan_opts=suppressions='%s.supp' %run %t 2>&1 | FileCheck %s
 #include "test.h"
 
 int Global;
index adc44bbe9608e0fd398af1aecc96fb9aeb86740a..a681daa32906f9f480a6ac0ca161882ee965e1e9 100644 (file)
@@ -4,56 +4,66 @@
 #include <unistd.h>
 #include <dlfcn.h>
 #include <stddef.h>
+#include <sched.h>
+#include <stdarg.h>
+
+#ifdef __APPLE__
+#include <mach/mach_time.h>
+#endif
 
 // TSan-invisible barrier.
 // Tests use it to establish necessary execution order in a way that does not
 // interfere with tsan (does not establish synchronization between threads).
-// 8 lsb is thread count, the remaining are count of entered threads.
 typedef unsigned long long invisible_barrier_t;
 
-void barrier_init(invisible_barrier_t *barrier, unsigned count) {
-  if (count >= (1 << 8))
-      exit(fprintf(stderr, "barrier_init: count is too large (%d)\n", count));
-  *barrier = count;
+#ifdef __cplusplus
+extern "C" {
+#endif
+void __tsan_testonly_barrier_init(invisible_barrier_t *barrier,
+    unsigned count);
+void __tsan_testonly_barrier_wait(invisible_barrier_t *barrier);
+#ifdef __cplusplus
 }
+#endif
 
-void barrier_wait(invisible_barrier_t *barrier) {
-  unsigned old = __atomic_fetch_add(barrier, 1 << 8, __ATOMIC_RELAXED);
-  unsigned old_epoch = (old >> 8) / (old & 0xff);
-  for (;;) {
-    unsigned cur = __atomic_load_n(barrier, __ATOMIC_RELAXED);
-    unsigned cur_epoch = (cur >> 8) / (cur & 0xff);
-    if (cur_epoch != old_epoch)
-      return;
-    usleep(1000);
-  }
+static inline void barrier_init(invisible_barrier_t *barrier, unsigned count) {
+  __tsan_testonly_barrier_init(barrier, count);
+}
+
+static inline void barrier_wait(invisible_barrier_t *barrier) {
+  __tsan_testonly_barrier_wait(barrier);
 }
 
 // Default instance of the barrier, but a test can declare more manually.
 invisible_barrier_t barrier;
 
-void print_address(void *address) {
-// On FreeBSD, the %p conversion specifier works as 0x%x and thus does not match
-// to the format used in the diagnotic message.
-#ifdef __x86_64__
-  fprintf(stderr, "0x%012lx", (unsigned long) address);
+void print_address(const char *str, int n, ...) {
+  fprintf(stderr, "%s", str);
+  va_list ap;
+  va_start(ap, n);
+  while (n--) {
+    void *p = va_arg(ap, void *);
+#if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__)
+    // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
+    // match to the format used in the diagnotic message.
+    fprintf(stderr, "0x%012lx ", (unsigned long) p);
 #elif defined(__mips64)
-  fprintf(stderr, "0x%010lx", (unsigned long) address);
-#elif defined(__aarch64__)
-  // AArch64 currently has 3 different VMA (39, 42, and 48 bits) and it requires
-  // different pointer size to match the diagnostic message.
-  const char *format = 0;
-  unsigned long vma = (unsigned long)__builtin_frame_address(0);
-  vma = 64 - __builtin_clzll(vma);
-  if (vma == 39)
-    format = "0x%010lx";
-  else if (vma == 42)
-    format = "0x%011lx";
-  else {
-    fprintf(stderr, "unsupported vma: %ul\n", vma);
-    exit(1);
+    fprintf(stderr, "0x%010lx ", (unsigned long) p);
+#endif
   }
+  fprintf(stderr, "\n");
+}
 
-  fprintf(stderr, format, (unsigned long) address);
-#endif
+#ifdef __APPLE__
+unsigned long long monotonic_clock_ns() {
+  static mach_timebase_info_data_t timebase_info;
+  if (timebase_info.denom == 0) mach_timebase_info(&timebase_info);
+  return (mach_absolute_time() * timebase_info.numer) / timebase_info.denom;
+}
+#else
+unsigned long long monotonic_clock_ns() {
+  struct timespec t;
+  clock_gettime(CLOCK_MONOTONIC, &t);
+  return (unsigned long long)t.tv_sec * 1000000000ull + t.tv_nsec;
 }
+#endif
diff --git a/src/compiler-rt/test/tsan/test_output.sh b/src/compiler-rt/test/tsan/test_output.sh
deleted file mode 100755 (executable)
index bce0fe8..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/bash
-
-ulimit -s 8192
-set -e # fail on any error
-
-HERE=$(dirname $0)
-TSAN_DIR=$(dirname $0)/../../lib/tsan
-
-# Assume clang and clang++ are in path.
-: ${CC:=clang}
-: ${CXX:=clang++}
-: ${FILECHECK:=FileCheck}
-
-# TODO: add testing for all of -O0...-O3
-CFLAGS="-fsanitize=thread -O2 -g -Wall"
-LDFLAGS="-pthread -ldl -lrt -lm -Wl,--whole-archive $TSAN_DIR/rtl/libtsan.a -Wl,--no-whole-archive"
-
-test_file() {
-  SRC=$1
-  COMPILER=$2
-  echo ----- TESTING $(basename $1)
-  OBJ=$SRC.o
-  EXE=$SRC.exe
-  $COMPILER $SRC $CFLAGS -c -o $OBJ
-  $COMPILER $OBJ $LDFLAGS -o $EXE
-  RES=$($EXE 2>&1 || true)
-  printf "%s\n" "$RES" | $FILECHECK $SRC
-  if [ "$3" == "" ]; then
-    rm -f $EXE $OBJ
-  fi
-}
-
-if [ "$1" == "" ]; then
-  for c in $HERE/*.{c,cc}; do
-    if [[ $c == */failing_* ]]; then
-      echo SKIPPING FAILING TEST $c
-      continue
-    fi
-    if [[ $c == */load_shared_lib.cc ]]; then
-      echo TEST $c is not supported
-      continue
-    fi
-    if [[ $c == */*blacklist*.cc ]]; then
-      echo TEST $c is not supported
-      continue
-    fi
-    if [ "`grep "TSAN_OPTIONS" $c`" ]; then
-      echo SKIPPING $c -- requires TSAN_OPTIONS
-      continue
-    fi
-    if [ "`grep "XFAIL" $c`" ]; then
-      echo SKIPPING $c -- has XFAIL
-      continue
-    fi
-    COMPILER=$CXX
-    case $c in
-      *.c) COMPILER=$CC
-    esac
-    test_file $c $COMPILER &
-  done
-  for job in `jobs -p`; do
-    wait $job || exit 1
-  done
-else
-  test_file $HERE/$1 $CXX "DUMP"
-fi
index 93387dab2b0b59ea9bcd81ef3c7089164c8e7dc6..d7ed0f0d1952f9dc7f05ecb6fa8a1aebbe2b751f 100644 (file)
@@ -1,6 +1,9 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
 #include "test.h"
 
+// OS X doesn't have pthread_setname_np(tid, name).
+// UNSUPPORTED: darwin
+
 #if defined(__FreeBSD__)
 #include <pthread_np.h>
 #define pthread_setname_np pthread_set_name_np
index 5e81722767089d913515b832328f8524bcf0894f..b43a514cc8aa8a1a6660bd2e38de2fc047aae93f 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
 #include "test.h"
 
 void *Thread(void *a) {
@@ -18,4 +18,6 @@ int main() {
 }
 
 // CHECK: WARNING: ThreadSanitizer: data race
-// CHECK:   Location is TLS of main thread.
+// CHECK-Linux:   Location is TLS of main thread.
+// CHECK-FreeBSD:   Location is TLS of main thread.
+// CHECK-Darwin:   Location is heap block of size 4
index d0f7b03e09a1d6cf02e68f95e2ed6eeeff7d92df..b04ff67881009beaa162a3c091b03f84012521ce 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
+// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK
 #include "test.h"
 
 void *Thread2(void *a) {
@@ -25,5 +25,6 @@ int main() {
 }
 
 // CHECK: WARNING: ThreadSanitizer: data race
-// CHECK:   Location is TLS of thread T1.
-
+// CHECK-Linux:   Location is TLS of thread T1.
+// CHECK-FreeBSD:   Location is TLS of thread T1.
+// CHECK-Darwin:   Location is heap block of size 4
index 5ae1dd1ababd5cba32aa609c1501ea559a14bd7f..98a82623ee6513b22d533581f380c8b1ac688f78 100644 (file)
@@ -1,4 +1,5 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+// UNSUPPORTED: darwin
 #include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
index 2a50c2e88d0136d04f116187e638aa8038059a87..7da581d80601bae620a3d3443856b258941bef89 100644 (file)
@@ -1,4 +1,4 @@
-// Regression test for http://code.google.com/p/thread-sanitizer/issues/detail?id=3.
+// Regression test for https://github.com/google/sanitizers/issues/410.
 // The C++ variant is much more compact that the LLVM IR equivalent.
 
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
index 92a2b326e717c1f313da04c3a07de22709eda90c..c0068955129f9c1c3de2d94a05bb1336f15bc4b9 100644 (file)
@@ -1,28 +1,36 @@
 // RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
 #include <pthread.h>
-#include <semaphore.h>
 #include <stdio.h>
 
 struct A {
   A() {
-    sem_init(&sem_, 0, 0);
+    pthread_mutex_init(&m, 0);
+    pthread_cond_init(&c, 0);
+    signaled = false;
   }
   virtual void F() {
   }
   void Done() {
-    sem_post(&sem_);
+    pthread_mutex_lock(&m);
+    signaled = true;
+    pthread_cond_signal(&c);
+    pthread_mutex_unlock(&m);
   }
   virtual ~A() {
   }
-  sem_t sem_;
+  pthread_mutex_t m;
+  pthread_cond_t c;
+  bool signaled;
 };
 
 struct B : A {
   virtual void F() {
   }
   virtual ~B() {
-    sem_wait(&sem_);
-    sem_destroy(&sem_);
+    pthread_mutex_lock(&m);
+    while (!signaled)
+      pthread_cond_wait(&c, &m);
+    pthread_mutex_unlock(&m);
   }
 };
 
index 58432369226d4b679f6fbb744eeb49791bbf38b9..e687afab4354ac672bb256f9e02623f923adae73 100644 (file)
@@ -1,5 +1,5 @@
 // RUN: %clangxx -fsanitize=integer %s -o %t
-// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE
+// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE
 // RUN: %env_ubsan_opts=report_error_type=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TYPE
 // REQUIRES: ubsan-asan
 
diff --git a/src/compiler-rt/test/ubsan/TestCases/Integer/suppressions.cpp b/src/compiler-rt/test/ubsan/TestCases/Integer/suppressions.cpp
new file mode 100644 (file)
index 0000000..e6ae626
--- /dev/null
@@ -0,0 +1,33 @@
+// RUN: %clangxx -fsanitize=integer -g0 %s -o %t
+
+// Fails without any suppression.
+// RUN: %env_ubsan_opts=halt_on_error=1 not %run %t 2>&1 | FileCheck %s
+
+// RUN: echo "signed-integer-overflow:%t" > %t.wrong-supp
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.wrong-supp"' not %run %t 2>&1 | FileCheck %s
+
+// RUN: echo "unsigned-integer-overflow:do_overflow" > %t.func-supp
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.func-supp"' %run %t
+// RUN: echo "unsigned-integer-overflow:%t" > %t.module-supp
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.module-supp"' %run %t
+
+// Note: file-level suppressions should work even without debug info.
+// RUN: echo "unsigned-integer-overflow:%s" > %t.file-supp
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.file-supp"' %run %t
+
+// Suppressions don't work for unrecoverable kinds.
+// RUN: %clangxx -fsanitize=integer -fno-sanitize-recover=integer %s -o %t-norecover
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.module-supp"' not %run %t-norecover 2>&1 | FileCheck %s
+
+#include <stdint.h>
+
+extern "C" void do_overflow() {
+  (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull));
+  // CHECK: runtime error: unsigned integer overflow
+}
+
+int main() {
+  do_overflow();
+  return 0;
+}
+
index 2be8792cce960cf3795c6191acda9e1624ad41a5..eac4c32a2839646d871205bd893f5b454e6b3e71 100644 (file)
@@ -1,5 +1,5 @@
 // RUN: %clangxx -fsanitize=integer -fsanitize-recover=integer %s -o %t
-// RUN: not %t 2>&1 | FileCheck %s
+// RUN: not %run %t 2>&1 | FileCheck %s
 
 // __ubsan_default_options() doesn't work on Darwin.
 // XFAIL: darwin
index 8e84406664240ced3e8453ad1341588d42bc675d..e50862983d62c8593e477f1698b38e79ec918021 100644 (file)
@@ -42,6 +42,7 @@ if config.host_os == 'Darwin':
   # On Darwin, we default to `abort_on_error=1`, which would make tests run
   # much slower. Let's override this and run lit tests with 'abort_on_error=0'.
   default_ubsan_opts += ['abort_on_error=0']
+  default_ubsan_opts += ['log_to_syslog=0']
 default_ubsan_opts_str = ':'.join(default_ubsan_opts)
 if default_ubsan_opts_str:
   config.environment['UBSAN_OPTIONS'] = default_ubsan_opts_str
index c8a9082947eea8ca4a9ae92973a25835c8214d19..ddaebaf98d99d421f5a5b2790eca8b45923fdeee 100644 (file)
@@ -134,7 +134,7 @@ $ ldd example
         not a dynamic executable
 $ ./example
 hi!
-thread '<main>' panicked at 'failed', example.rs:1
+thread 'main' panicked at 'failed', example.rs:1
 ```
 
 Success! This binary can be copied to almost any Linux machine with the same
index 50350213074bf3a2cd2368db61d0d7acce5c7a23..d88f619260ac0ca4d95a3edc5a6a12b63480991a 100644 (file)
@@ -232,7 +232,7 @@ indicator (one word in size) along with the data.
 
 At runtime each borrow causes a modification/check of the refcount.
 
-[cell-mod]: ../std/cell/
+[cell-mod]: ../std/cell/index.html
 [cell]: ../std/cell/struct.Cell.html
 [refcell]: ../std/cell/struct.RefCell.html
 
index dedf9d5c28abd4d85ece1d4a13651ad3ac6c6dc8..a6b4e9492181c790fe5a3c040ca6e02397824d8f 100644 (file)
@@ -322,7 +322,7 @@ to our closure when we pass it to `call_with_one`, so we use `&||`.
 A quick note about closures that use explicit lifetimes. Sometimes you might have a closure
 that takes a reference like so:
 
-```
+```rust
 fn call_with_ref<F>(some_closure:F) -> i32
     where F: Fn(&i32) -> i32 {
 
@@ -334,8 +334,8 @@ fn call_with_ref<F>(some_closure:F) -> i32
 Normally you can specify the lifetime of the parameter to our closure. We
 could annotate it on the function declaration:
 
-```ignore
-fn call_with_ref<'a, F>(some_closure:F) -> i32 
+```rust,ignore
+fn call_with_ref<'a, F>(some_closure:F) -> i32
     where F: Fn(&'a 32) -> i32 {
 ```
 
@@ -353,11 +353,11 @@ fn call_with_ref<F>(some_closure:F) -> i32
     where F: for<'a> Fn(&'a 32) -> i32 {
 ```
 
-This lets the Rust compiler find the minimum lifetime to invoke our closure and 
+This lets the Rust compiler find the minimum lifetime to invoke our closure and
 satisfy the borrow checker's rules. Our function then compiles and excutes as we
 expect.
 
-```
+```rust
 fn call_with_ref<F>(some_closure:F) -> i32
     where F: for<'a> Fn(&'a i32) -> i32 {
 
index 2d0cc61fb11d67252755377599065b210f8b27b5..8426d5a626549b8ea22b33f3598a78090491e63f 100644 (file)
@@ -34,7 +34,7 @@ code that manipulates syntax trees at
 compile time.
 
 Let's write a plugin
-[`roman_numerals.rs`](https://github.com/rust-lang/rust/tree/master/src/test/auxiliary/roman_numerals.rs)
+[`roman_numerals.rs`](https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs)
 that implements Roman numeral integer literals.
 
 ```rust,ignore
@@ -45,11 +45,11 @@ extern crate syntax;
 extern crate rustc;
 extern crate rustc_plugin;
 
-use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::ast::TokenTree;
 use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
 use syntax::ext::build::AstBuilder;  // trait for expr_usize
+use syntax_pos::Span;
 use rustc_plugin::Registry;
 
 fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
@@ -166,7 +166,8 @@ quasiquote as an ordinary plugin library.
 
 Plugins can extend [Rust's lint
 infrastructure](../reference.html#lint-check-attributes) with additional checks for
-code style, safety, etc. Now let's write a plugin [`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/auxiliary/lint_plugin_test.rs)
+code style, safety, etc. Now let's write a plugin
+[`lint_plugin_test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/lint_plugin_test.rs)
 that warns about any item named `lintme`.
 
 ```rust,ignore
index 43ac30c35c6c61d4c738f2161f40bf9755e04833..67fe8ba2c11a48e0d5bde95d9a769caeef0a4541 100644 (file)
@@ -22,12 +22,10 @@ As an example, let’s make a *phrases* crate, which will give us various phrase
 in different languages. To keep things simple, we’ll stick to ‘greetings’ and
 ‘farewells’ as two kinds of phrases, and use English and Japanese (日本語) as
 two languages for those phrases to be in. We’ll use this module layout:
-
 ```text
                                     +-----------+
                                 +---| greetings |
-                                |   +-----------+
-                  +---------+   |
+                  +---------+   |   +-----------+
               +---| english |---+
               |   +---------+   |   +-----------+
               |                 +---| farewells |
@@ -37,8 +35,7 @@ two languages for those phrases to be in. We’ll use this module layout:
               |                 +---| greetings |
               |   +----------+  |   +-----------+
               +---| japanese |--+
-                  +----------+  |
-                                |   +-----------+
+                  +----------+  |   +-----------+
                                 +---| farewells |
                                     +-----------+
 ```
index 4a41bb7b7f37eee595870bb26f5680de4122587b..3c6643fbfe1554e0ae02c5bc551f0a04353715c2 100644 (file)
@@ -76,7 +76,7 @@ This [unfortunate error](https://github.com/rust-lang/rust/issues/22547) is
 correct; documentation comments apply to the thing after them, and there's
 nothing after that last comment.
 
-[rc-new]: https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new
+[rc-new]: ../std/rc/struct.Rc.html#method.new
 
 ### Writing documentation comments
 
index c914c33a5a4b989e369e01489ff8aca199130c00..544f837d69b264171df652134267d1f5f1551491 100644 (file)
@@ -81,7 +81,7 @@ fn main() {
 If you try running this code, the program will crash with a message like this:
 
 ```text
-thread '<main>' panicked at 'Invalid number: 11', src/bin/panic-simple.rs:5
+thread 'main' panicked at 'Invalid number: 11', src/bin/panic-simple.rs:5
 ```
 
 Here's another example that is slightly less contrived. A program that accepts
@@ -498,7 +498,7 @@ At this point, you should be skeptical of calling `unwrap`. For example, if
 the string doesn't parse as a number, you'll get a panic:
 
 ```text
-thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', /home/rustbuild/src/rust-buildbot/slave/beta-dist-rustc-linux/build/src/libcore/result.rs:729
+thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', /home/rustbuild/src/rust-buildbot/slave/beta-dist-rustc-linux/build/src/libcore/result.rs:729
 ```
 
 This is rather unsightly, and if this happened inside a library you're
@@ -1829,7 +1829,7 @@ use std::error::Error;
 
 fn search<P: AsRef<Path>>
          (file_path: P, city: &str)
-         -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
+         -> Result<Vec<PopulationCount>, Box<Error>> {
     let mut found = vec![];
     let file = try!(File::open(file_path));
     let mut rdr = csv::Reader::from_reader(file);
@@ -1858,20 +1858,17 @@ Instead of `x.unwrap()`, we now have `try!(x)`. Since our function returns a
 `Result<T, E>`, the `try!` macro will return early from the function if an
 error occurs.
 
-There is one big gotcha in this code: we used `Box<Error + Send + Sync>`
-instead of `Box<Error>`. We did this so we could convert a plain string to an
-error type. We need these extra bounds so that we can use the
-[corresponding `From`
-impls](../std/convert/trait.From.html):
+At the end of `search` we also convert a plain string to an error type 
+by using the [corresponding `From` impls](../std/convert/trait.From.html):
 
 ```rust,ignore
 // We are making use of this impl in the code above, since we call `From::from`
 // on a `&'static str`.
-impl<'a, 'b> From<&'b str> for Box<Error + Send + Sync + 'a>
+impl<'a> From<&'a str> for Box<Error>
 
 // But this is also useful when you need to allocate a new string for an
 // error message, usually with `format!`.
-impl From<String> for Box<Error + Send + Sync>
+impl From<String> for Box<Error>
 ```
 
 Since `search` now returns a `Result<T, E>`, `main` should use case analysis
@@ -1964,7 +1961,7 @@ use std::io;
 
 fn search<P: AsRef<Path>>
          (file_path: &Option<P>, city: &str)
-         -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
+         -> Result<Vec<PopulationCount>, Box<Error>> {
     let mut found = vec![];
     let input: Box<io::Read> = match *file_path {
         None => Box::new(io::stdin()),
@@ -2175,9 +2172,8 @@ heuristics!
   `unwrap`. Be warned: if it winds up in someone else's hands, don't be
   surprised if they are agitated by poor error messages!
 * If you're writing a quick 'n' dirty program and feel ashamed about panicking
-  anyway, then use either a `String` or a `Box<Error + Send + Sync>` for your
-  error type (the `Box<Error + Send + Sync>` type is because of the
-  [available `From` impls](../std/convert/trait.From.html)).
+  anyway, then use either a `String` or a `Box<Error>` for your
+  error type.
 * Otherwise, in a program, define your own error types with appropriate
   [`From`](../std/convert/trait.From.html)
   and
@@ -2205,7 +2201,7 @@ heuristics!
 [3]: ../std/option/enum.Option.html#method.unwrap_or
 [4]: ../std/option/enum.Option.html#method.unwrap_or_else
 [5]: ../std/option/enum.Option.html
-[6]: ../std/result/
+[6]: ../std/result/index.html
 [7]: ../std/result/enum.Result.html#method.unwrap
 [8]: ../std/fmt/trait.Debug.html
 [9]: ../std/primitive.str.html#method.parse
index 3a10d2aecc25e3e3fbe364f50759629c50887ad0..b040684d05f7eaca4259b2cd95f433087eedbf7c 100644 (file)
@@ -221,7 +221,7 @@ If you add a main function that calls `diverges()` and run it, you’ll get
 some output that looks like this:
 
 ```text
-thread ‘<main>’ panicked at ‘This function never returns!’, hello.rs:2
+thread ‘main’ panicked at ‘This function never returns!’, hello.rs:2
 ```
 
 If you want more information, you can get a backtrace by setting the
@@ -229,7 +229,7 @@ If you want more information, you can get a backtrace by setting the
 
 ```text
 $ RUST_BACKTRACE=1 ./diverges
-thread '<main>' panicked at 'This function never returns!', hello.rs:2
+thread 'main' panicked at 'This function never returns!', hello.rs:2
 stack backtrace:
    1:     0x7f402773a829 - sys::backtrace::write::h0942de78b6c02817K8r
    2:     0x7f402773d7fc - panicking::on_panic::h3f23f9d0b5f4c91bu9w
@@ -249,13 +249,13 @@ stack backtrace:
 If you need to override an already set `RUST_BACKTRACE`, 
 in cases when you cannot just unset the variable, 
 then set it to `0` to avoid getting a backtrace. 
-Any other value(even no value at all) turns on backtrace.
+Any other value (even no value at all) turns on backtrace.
 
 ```text
 $ export RUST_BACKTRACE=1
 ...
 $ RUST_BACKTRACE=0 ./diverges 
-thread '<main>' panicked at 'This function never returns!', hello.rs:2
+thread 'main' panicked at 'This function never returns!', hello.rs:2
 note: Run with `RUST_BACKTRACE=1` for a backtrace.
 ```
 
@@ -264,7 +264,7 @@ note: Run with `RUST_BACKTRACE=1` for a backtrace.
 ```text
 $ RUST_BACKTRACE=1 cargo run
      Running `target/debug/diverges`
-thread '<main>' panicked at 'This function never returns!', hello.rs:2
+thread 'main' panicked at 'This function never returns!', hello.rs:2
 stack backtrace:
    1:     0x7f402773a829 - sys::backtrace::write::h0942de78b6c02817K8r
    2:     0x7f402773d7fc - panicking::on_panic::h3f23f9d0b5f4c91bu9w
index 0956580ade0bb0ea3f88fc27a64e22c2a4342549..8aa7fdff94803735f355f8944c60dc0de0c1bdfa 100644 (file)
@@ -46,6 +46,12 @@ must abide by that constraint.
 
 [traits]: traits.html
 
+### Combinators
+
+Combinators are higher-order functions that apply only functions and
+earlier defined combinators to provide a result from its arguments. 
+They can be used to manage control flow in a modular fashion.
+
 ### DST (Dynamically Sized Type)
 
 A type without a statically known size or alignment. ([more info][link])
index a2067e33a60aaad69e60294004e2c79713c194b7..c759ff9bdbde48e845a64dfbc158f198bd8178ff 100644 (file)
@@ -806,7 +806,7 @@ You guessed: 59
 You win!
 Please input your guess.
 quit
-thread '<main>' panicked at 'Please type a number!'
+thread 'main' panicked at 'Please type a number!'
 ```
 
 Ha! `quit` actually quits. As does any other non-number input. Well, this is
index cb0757318984db19015035c4d91d3efd7e057d57..f7d9c94bc454f31a052f828a7385623801f7ac5c 100644 (file)
@@ -290,7 +290,7 @@ lifetime parameters using three easily memorizable and unambiguous rules. This m
 acts as a shorthand for writing an item signature, while not hiding
 away the actual types involved as full local inference would if applied to it.
 
-When talking about lifetime elision, we use the term *input lifetime* and
+When talking about lifetime elision, we use the terms *input lifetime* and
 *output lifetime*. An *input lifetime* is a lifetime associated with a parameter
 of a function, and an *output lifetime* is a lifetime associated with the return
 value of a function. For example, this function has an input lifetime:
@@ -335,11 +335,13 @@ fn print<'a>(s: &'a str); // expanded
 
 fn debug(lvl: u32, s: &str); // elided
 fn debug<'a>(lvl: u32, s: &'a str); // expanded
+```
 
-// In the preceding example, `lvl` doesn’t need a lifetime because it’s not a
-// reference (`&`). Only things relating to references (such as a `struct`
-// which contains a reference) need lifetimes.
+In the preceding example, `lvl` doesn’t need a lifetime because it’s not a
+reference (`&`). Only things relating to references (such as a `struct`
+which contains a reference) need lifetimes.
 
+```rust,ignore
 fn substr(s: &str, until: u32) -> &str; // elided
 fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded
 
index 97ca2e3e702f6cf926e54e88679beb1eff7dfe1a..e23e6f3a786a5c0e816edb6f27370f3aaf20caf7 100644 (file)
@@ -178,7 +178,7 @@ loop {
 
 We now loop forever with `loop` and use `break` to break out early. Issuing an explicit `return` statement will also serve to terminate the loop early.
 
-`continue` is similar, but instead of ending the loop, goes to the next
+`continue` is similar, but instead of ending the loop, it goes to the next
 iteration. This will only print the odd numbers:
 
 ```rust
index 43bd0507ebbb6f02e7354942af18330a593e6e2d..c5c139e6580b015b91d49bd6d90a66d037b04f41 100644 (file)
@@ -12,9 +12,26 @@ don’t want to use the standard library via an attribute: `#![no_std]`.
 > `#![no_std]`](using-rust-without-the-standard-library.html)
 
 Obviously there's more to life than just libraries: one can use
-`#[no_std]` with an executable, controlling the entry point is
-possible in two ways: the `#[start]` attribute, or overriding the
-default shim for the C `main` function with your own.
+`#[no_std]` with an executable.
+
+### Using libc
+
+In order to build a `#[no_std]` executable we will need libc as a dependency. We can specify
+this using our `Cargo.toml` file:
+
+```toml
+[dependencies]
+libc = { version = "0.2.11", default-features = false }
+```
+
+Note that the default features have been disabled. This is a critical step -
+**the default features of libc include the standard library and so must be
+disabled.**
+
+### Writing an executable without stdlib
+
+Controlling the entry point is possible in two ways: the `#[start]` attribute,
+or overriding the default shim for the C `main` function with your own.
 
 The function marked `#[start]` is passed the command line parameters
 in the same format as C:
@@ -72,7 +89,6 @@ pub extern fn main(argc: i32, argv: *const *const u8) -> i32 {
 # // fn main() {} tricked you, rustdoc!
 ```
 
-
 The compiler currently makes a few assumptions about symbols which are available
 in the executable to call. Normally these functions are provided by the standard
 library, but without it you must define your own.
@@ -84,4 +100,4 @@ which do not trigger a panic can be assured that this function is never
 called. The second function, `panic_fmt`, is also used by the failure
 mechanisms of the compiler.
 
-[unwind]: https://github.com/rust-lang/rust/blob/master/src/libstd/sys/common/unwind/gcc.rs
+[unwind]: https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs
index f445bed015c00d85c32e8a5a4d6b4e0d992ae7e7..23ca21b3b49923f01ed930a2dbb4acac7d81ff3b 100644 (file)
@@ -67,7 +67,7 @@ Vectors have a [generic type][generics] `Vec<T>`, so in this example `v` will ha
 
 [arrays]: primitive-types.html#arrays
 [vectors]: vectors.html
-[heap]: the-stack-and-the-heap.html
+[heap]: the-stack-and-the-heap.html#the-heap
 [stack]: the-stack-and-the-heap.html#the-stack
 [bindings]: variable-bindings.html
 [generics]: generics.html
@@ -136,6 +136,8 @@ Rust allocates memory for an integer [i32] on the [stack][sh], copies the bit
 pattern representing the value of 10 to the allocated memory and binds the
 variable name x to this memory region for future reference.
 
+[i32]: primitive-types.html#numeric-types
+
 Now consider the following code fragment:
 
 ```rust
@@ -212,7 +214,7 @@ But, unlike a move, we can still use `v` afterward. This is because an `i32`
 has no pointers to data somewhere else, copying it is a full copy.
 
 All primitive types implement the `Copy` trait and their ownership is
-therefore not moved like one would assume, following the ´ownership rules´.
+therefore not moved like one would assume, following the ‘ownership rules’.
 To give an example, the two following snippets of code only compile because the
 `i32` and `bool` types implement the `Copy` trait.
 
@@ -288,6 +290,6 @@ let (v1, v2, answer) = foo(v1, v2);
 Ugh! The return type, return line, and calling the function gets way more
 complicated.
 
-Luckily, Rust offers a feature, borrowing, which helps us solve this problem.
-It’s the topic of the next section!
+Luckily, Rust offers a feature which helps us solve this problem.
+It’s called borrowing and is the topic of the next section!
 
index 7ecfdcfcc1e025d78fea05f938405de33fb1d201..a0245d4c7b163f724157366db677946dbdd1d8f3 100644 (file)
@@ -174,7 +174,7 @@ Here, we bind the first and last element of the tuple to `x` and `z`, but
 ignore the middle element.
 
 It’s worth noting that using `_` never binds the value in the first place,
-which means a value may not move:
+which means that the value does not move:
 
 ```rust
 let tuple: (u32, String) = (5, String::from("five"));
index 2a4b7ba37f25bc79d6cc9a494311c04ca2993138..ea0bdf29fcc41d7a34be7da8e72d4fb38c934180 100644 (file)
@@ -163,7 +163,7 @@ A ‘slice’ is a reference to (or “view” into) another data structure. The
 useful for allowing safe, efficient access to a portion of an array without
 copying. For example, you might want to reference only one line of a file read
 into memory. By nature, a slice is not created directly, but from an existing
-variable binding. Slices have a defined length, can be mutable or immutable.
+variable binding. Slices have a defined length, and can be mutable or immutable.
 
 Internally, slices are represented as a pointer to the beginning of the data
 and a length.
@@ -175,8 +175,6 @@ You can use a combo of `&` and `[]` to create a slice from various things. The
 detail later in this section. The `[]`s, with a range, let you define the
 length of the slice:
 
-[references]: references-and-borrowing.html
-
 ```rust
 let a = [0, 1, 2, 3, 4];
 let complete = &a[..]; // A slice containing all of the elements in a
index a28f450c942af04cf36302c15483bb13df3a0a1c..57bfbce8b84debab2f26a638ae1aed85ecc491ed 100644 (file)
@@ -85,7 +85,7 @@ fn main() {
     fn sum_vec(v: &Vec<i32>) -> i32 {
         return v.iter().fold(0, |a, &b| a + b);
     }
-    // Borrow two vectors and and sum them.
+    // Borrow two vectors and sum them.
     // This kind of borrowing does not allow mutation to the borrowed.
     fn foo(v1: &Vec<i32>, v2: &Vec<i32>) -> i32 {
         // do stuff with v1 and v2
@@ -123,7 +123,7 @@ let v = vec![];
 foo(&v);
 ```
 
-errors with:
+will give us this error:
 
 ```text
 error: cannot borrow immutable borrowed content `*v` as mutable
@@ -152,8 +152,8 @@ the thing `y` points at. You’ll notice that `x` had to be marked `mut` as well
 If it wasn’t, we couldn’t take a mutable borrow to an immutable value.
 
 You'll also notice we added an asterisk (`*`) in front of `y`, making it `*y`,
-this is because `y` is a `&mut` reference. You'll also need to use them for
-accessing the contents of a reference as well.
+this is because `y` is a `&mut` reference. You'll need to use astrisks to
+access the contents of a reference as well.
 
 Otherwise, `&mut` references are like references. There _is_ a large
 difference between the two, and how they interact, though. You can tell
@@ -179,7 +179,7 @@ As it turns out, there are rules.
 
 # The Rules
 
-Here’s the rules about borrowing in Rust:
+Here are the rules for borrowing in Rust:
 
 First, any borrow must last for a scope no greater than that of the owner.
 Second, you may have one or the other of these two kinds of borrows, but not
@@ -208,12 +208,14 @@ With this in mind, let’s consider our example again.
 Here’s the code:
 
 ```rust,ignore
-let mut x = 5;
-let y = &mut x;
+fn main() {
+    let mut x = 5;
+    let y = &mut x;
 
-*y += 1;
+    *y += 1;
 
-println!("{}", x);
+    println!("{}", x);
+}
 ```
 
 This code gives us this error:
@@ -225,7 +227,7 @@ error: cannot borrow `x` as immutable because it is also borrowed as mutable
 ```
 
 This is because we’ve violated the rules: we have a `&mut T` pointing to `x`,
-and so we aren’t allowed to create any `&T`s. One or the other. The note
+and so we aren’t allowed to create any `&T`s. It's one or the other. The note
 hints at how to think about this problem:
 
 ```text
@@ -243,14 +245,16 @@ In Rust, borrowing is tied to the scope that the borrow is valid for. And our
 scopes look like this:
 
 ```rust,ignore
-let mut x = 5;
-
-let y = &mut x;    // -+ &mut borrow of x starts here
-                   //  |
-*y += 1;           //  |
-                   //  |
-println!("{}", x); // -+ - try to borrow x here
-                   // -+ &mut borrow of x ends here
+fn main() {
+    let mut x = 5;
+
+    let y = &mut x;    // -+ &mut borrow of x starts here
+                       //  |
+    *y += 1;           //  |
+                       //  |
+    println!("{}", x); // -+ - try to borrow x here
+}                      // -+ &mut borrow of x ends here
+                       
 ```
 
 The scopes conflict: we can’t make an `&x` while `y` is in scope.
@@ -269,12 +273,12 @@ println!("{}", x);  // <- try to borrow x here
 ```
 
 There’s no problem. Our mutable borrow goes out of scope before we create an
-immutable one. But scope is the key to seeing how long a borrow lasts for.
+immutable one. So scope is the key to seeing how long a borrow lasts for.
 
 ## Issues borrowing prevents
 
 Why have these restrictive rules? Well, as we noted, these rules prevent data
-races. What kinds of issues do data races cause? Here’s a few.
+races. What kinds of issues do data races cause? Here are a few.
 
 ### Iterator invalidation
 
@@ -323,7 +327,7 @@ for i in &v {
 
 We can’t modify `v` because it’s borrowed by the loop.
 
-### use after free
+### Use after free
 
 References must not live longer than the resource they refer to. Rust will
 check the scopes of your references to ensure that this is true.
index de165b70fc4028d5846fa4bc8384bc21dea0d966..fcedf0c994f9c6e0e98be4ee689ce8a71ec0e4f7 100644 (file)
@@ -10,7 +10,7 @@ fn main() {
     let v = vec!["match_this", "1"];
 
     match &v[..] {
-        ["match_this", second] => println!("The second element is {}", second),
+        &["match_this", second] => println!("The second element is {}", second),
         _ => {},
     }
 }
@@ -26,8 +26,8 @@ slice will be bound to that name. For example:
 
 fn is_symmetric(list: &[u32]) -> bool {
     match list {
-        [] | [_] => true,
-        [x, inside.., y] if x == y => is_symmetric(inside),
+        &[] | &[_] => true,
+        &[x, ref inside.., y] if x == y => is_symmetric(inside),
         _ => false
     }
 }
index 008644ec9a3e2821d68cfd9dad94319fbbb001cb..7be90e785b02b1772570a3cea24df89e83e09987 100644 (file)
@@ -163,7 +163,7 @@ let hachi = &dog[0..2];
 with this error:
 
 ```text
-thread '<main>' panicked at 'index 0 and/or 2 in `忠犬ハチ公` do not lie on
+thread 'main' panicked at 'index 0 and/or 2 in `忠犬ハチ公` do not lie on
 character boundary'
 ```
 
index 4ea114c4bee68996c8c2658ae50778e80de27ed3..7954085472e503dc64eceb946a71b89a11e80ab6 100644 (file)
@@ -107,7 +107,7 @@ failures:
 
 test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
 
-thread '<main>' panicked at 'Some tests failed', /home/steve/src/rust/src/libtest/lib.rs:247
+thread 'main' panicked at 'Some tests failed', /home/steve/src/rust/src/libtest/lib.rs:247
 ```
 
 Rust indicates that our test failed:
@@ -380,8 +380,9 @@ the `tests` directory.
 
 # The `tests` directory
 
-To write an integration test, let's make a `tests` directory, and
-put a `tests/lib.rs` file inside, with this as its contents:
+Each file in `tests/*.rs` directory is treated as individual crate.
+So, to write an integration test, let's make a `tests` directory, and
+put a `tests/integration_test.rs` file inside, with this as its contents:
 
 ```rust,ignore
 extern crate adder;
@@ -394,8 +395,8 @@ fn it_works() {
 ```
 
 This looks similar to our previous tests, but slightly different. We now have
-an `extern crate adder` at the top. This is because the tests in the `tests`
-directory are an entirely separate crate, and so we need to import our library.
+an `extern crate adder` at the top. This is because each test in the `tests`
+directory is an entirely separate crate, and so we need to import our library.
 This is also why `tests` is a suitable place to write integration-style tests:
 they use the library like any other consumer of it would.
 
@@ -428,6 +429,11 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
 Now we have three sections: our previous test is also run, as well as our new
 one.
 
+Cargo will ignore files in subdirectories of the `tests/` directory.
+Therefore shared modules in integrations tests are possible.
+For example `tests/common/mod.rs` is not seperatly compiled by cargo but can 
+be imported in every test with `mod common;`
+
 That's all there is to the `tests` directory. The `tests` module isn't needed
 here, since the whole thing is focused on tests.
 
index 107ef2b44d5eb07017ba08455f553239bf2e26eb..e685cb129b939669a235ec14ddb2091c9d06f086 100644 (file)
@@ -397,10 +397,10 @@ fn normal<T: ConvertTo<i64>>(x: &T) -> i64 {
 }
 
 // can be called with T == i64
-fn inverse<T>() -> T
+fn inverse<T>(x: i32) -> T
         // this is using ConvertTo as if it were "ConvertTo<i64>"
         where i32: ConvertTo<T> {
-    42.convert()
+    x.convert()
 }
 ```
 
index 1179aebe54c552462099174392100468f29a1608..69958dd3e68a41c717527cc05347eb9d064705d1 100644 (file)
@@ -22,11 +22,12 @@ fn plus_one(x: i32) -> i32 {
 ```
 
 Much of the functionality that’s exposed in the standard library is also
-available via the [`core` crate](../core/). When we’re using the standard
-library, Rust automatically brings `std` into scope, allowing you to use
-its features without an explicit import. By the same token, when using
+available via the [`core` crate](../core/index.html). When we’re using the
+standard library, Rust automatically brings `std` into scope, allowing you to
+use its features without an explicit import. By the same token, when using
 `#![no_std]`, Rust will bring `core` into scope for you, as well as [its
-prelude](../core/prelude/v1/). This means that a lot of code will Just Work:
+prelude](../core/prelude/v1/index.html). This means that a lot of code will Just
+Work:
 
 ```rust
 #![no_std]
index 1c8c03cf6793773acc04fa5d8c8718d5b8d54c16..b6751f57a972146a8bbefc936ed1cee0d3fba26c 100644 (file)
@@ -37,8 +37,8 @@ of our minds as we go forward.
 Rust is a statically typed language, which means that we specify our types up
 front, and they’re checked at compile time. So why does our first example
 compile? Well, Rust has this thing called ‘type inference’. If it can figure
-out what the type of something is, Rust doesn’t require you to actually type it
-out.
+out what the type of something is, Rust doesn’t require you to explicitly type
+it out.
 
 We can add the type if we want to, though. Types come after a colon (`:`):
 
@@ -159,8 +159,9 @@ error: aborting due to previous error
 Could not compile `hello_world`.
 ```
 
-Rust will not let us use a value that has not been initialized. Next, let’s
-talk about this stuff we've added to `println!`.
+Rust will not let us use a value that has not been initialized.
+
+Let take a minute to talk about this stuff we've added to `println!`.
 
 If you include two curly braces (`{}`, some call them moustaches...) in your
 string to print, Rust will interpret this as a request to interpolate some sort
@@ -222,8 +223,8 @@ To learn more, run the command again with --verbose.
 ```
 
 Additionally, variable bindings can be shadowed. This means that a later
-variable binding with the same name as another binding, that's currently in
-scope, will override the previous binding.
+variable binding with the same name as another binding that is currently in
+scope will override the previous binding.
 
 ```rust
 let x: i32 = 8;
@@ -240,7 +241,10 @@ println!("{}", x); // Prints "42"
 Shadowing and mutable bindings may appear as two sides of the same coin, but
 they are two distinct concepts that can't always be used interchangeably. For
 one, shadowing enables us to rebind a name to a value of a different type. It
-is also possible to change the mutability of a binding.
+is also possible to change the mutability of a binding. Note that shadowing a 
+name does not alter or destroy the value it was bound to, and the value will
+continue to exist until it goes out of scope, even if it is no longer accessible
+by any means.
 
 ```rust
 let mut x: i32 = 1;
index 1c44af2f21a7198db6ff597fca3bec4188a27b85..cb6781cdf28fa42dc18cbf86f009faac14a1c2e6 100644 (file)
@@ -79,7 +79,7 @@ println!("Item 7 is {}", v[7]);
 then the current thread will [panic] with a message like this:
 
 ```text
-thread '<main>' panicked at 'index out of bounds: the len is 3 but the index is 7'
+thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 7'
 ```
 
 If you want to handle out-of-bounds errors without panicking, you can use
@@ -152,5 +152,5 @@ API documentation][vec].
 [box]: ../std/boxed/index.html
 [generic]: generics.html
 [panic]: concurrency.html#panics
-[get]: http://doc.rust-lang.org/std/vec/struct.Vec.html#method.get
-[get_mut]: http://doc.rust-lang.org/std/vec/struct.Vec.html#method.get_mut
+[get]: ../std/vec/struct.Vec.html#method.get
+[get_mut]: ../std/vec/struct.Vec.html#method.get_mut
index 952846dd5e25d629063b26e0550f87d33bd8a7f6..7513e524e73a1ef5499fd5b268572ba3b80961a2 100644 (file)
@@ -1,5 +1,5 @@
 <footer><p>
-Copyright &copy; 2011-2015 The Rust Project Developers. Licensed under the
+Copyright &copy; 2011 The Rust Project Developers. Licensed under the
 <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>
 or the <a href="https://opensource.org/licenses/MIT">MIT license</a>, at your option.
 </p><p>
index 4554652a17a2a40732f03e879a27bdc56e3a139e..b2e1eac5e0dccea7692c23a131faae5b288280d6 100644 (file)
@@ -35,4 +35,4 @@ exception-safety, pointer aliasing, memory models, and even some type-theory.
 We will also be spending a lot of time talking about the different kinds
 of safety and guarantees.
 
-[trpl]: ../book/
+[trpl]: ../book/index.html
index 5fd61eb51dd1c7f6d2b7d8ed6b6af5ec70dce106..c4f939a608b79ae49108db4caedc23f7ee1b0c60 100644 (file)
 % How Safe and Unsafe Interact
 
-So what's the relationship between Safe and Unsafe Rust? How do they interact?
-
-Rust models the separation between Safe and Unsafe Rust with the `unsafe`
-keyword, which can be thought as a sort of *foreign function interface* (FFI)
-between Safe and Unsafe Rust. This is the magic behind why we can say Safe Rust
-is a safe language: all the scary unsafe bits are relegated exclusively to FFI
-*just like every other safe language*.
-
-However because one language is a subset of the other, the two can be cleanly
-intermixed as long as the boundary between Safe and Unsafe Rust is denoted with
-the `unsafe` keyword. No need to write headers, initialize runtimes, or any of
-that other FFI boiler-plate.
-
-There are several places `unsafe` can appear in Rust today, which can largely be
-grouped into two categories:
-
-* There are unchecked contracts here. To declare you understand this, I require
-you to write `unsafe` elsewhere:
-    * On functions, `unsafe` is declaring the function to be unsafe to call.
-      Users of the function must check the documentation to determine what this
-      means, and then have to write `unsafe` somewhere to identify that they're
-      aware of the danger.
-    * On trait declarations, `unsafe` is declaring that *implementing* the trait
-      is an unsafe operation, as it has contracts that other unsafe code is free
-      to trust blindly. (More on this below.)
-
-* I am declaring that I have, to the best of my knowledge, adhered to the
-unchecked contracts:
-    * On trait implementations, `unsafe` is declaring that the contract of the
-      `unsafe` trait has been upheld.
-    * On blocks, `unsafe` is declaring any unsafety from an unsafe
-      operation within to be handled, and therefore the parent function is safe.
-
-There is also `#[unsafe_no_drop_flag]`, which is a special case that exists for
-historical reasons and is in the process of being phased out. See the section on
-[drop flags] for details.
-
-Some examples of unsafe functions:
-
-* `slice::get_unchecked` will perform unchecked indexing, allowing memory
-  safety to be freely violated.
-* every raw pointer to sized type has intrinsic `offset` method that invokes
-  Undefined Behavior if it is not "in bounds" as defined by LLVM.
-* `mem::transmute` reinterprets some value as having the given type,
-  bypassing type safety in arbitrary ways. (see [conversions] for details)
-* All FFI functions are `unsafe` because they can do arbitrary things.
-  C being an obvious culprit, but generally any language can do something
-  that Rust isn't happy about.
+What's the relationship between Safe Rust and Unsafe Rust? How do they
+interact?
+
+The separation between Safe Rust and Unsafe Rust is controlled with the
+`unsafe` keyword, which acts as an interface from one to the other. This is
+why we can say Safe Rust is a safe language: all the unsafe parts are kept
+exclusively behind the boundary.
+
+The `unsafe` keyword has two uses: to declare the existence of contracts the
+compiler can't check, and to declare that the adherence of some code to
+those contracts has been checked by the programmer.
+
+You can use `unsafe` to indicate the existence of unchecked contracts on
+_functions_ and on _trait declarations_. On functions, `unsafe` means that
+users of the function must check that function's documentation to ensure
+they are using it in a way that maintains the contracts the function
+requires. On trait declarations, `unsafe` means that implementors of the
+trait must check the trait documentation to ensure their implementation
+maintains the contracts the trait requires.
+
+You can use `unsafe` on a block to declare that all constraints required
+by an unsafe function within the block have been adhered to, and the code
+can therefore be trusted. You can use `unsafe` on a trait implementation
+to declare that the implementation of that trait has adhered to whatever
+contracts the trait's documentation requires.
+
+There is also the `#[unsafe_no_drop_flag]` attribute, which exists for
+historic reasons and is being phased out. See the section on [drop flags]
+for details.
+
+The standard library has a number of unsafe functions, including:
+
+* `slice::get_unchecked`, which performs unchecked indexing, allowing
+  memory safety to be freely violated.
+* `mem::transmute` reinterprets some value as having a given type, bypassing
+  type safety in arbitrary ways (see [conversions] for details).
+* Every raw pointer to a sized type has an intrinstic `offset` method that
+  invokes Undefined Behavior if the passed offset is not "in bounds" as
+  defined by LLVM.
+* All FFI functions are `unsafe` because the other language can do arbitrary
+  operations that the Rust compiler can't check.
 
 As of Rust 1.0 there are exactly two unsafe traits:
 
-* `Send` is a marker trait (it has no actual API) that promises implementors
-  are safe to send (move) to another thread.
-* `Sync` is a marker trait that promises that threads can safely share
-  implementors through a shared reference.
-
-The need for unsafe traits boils down to the fundamental property of safe code:
-
-**No matter how completely awful Safe code is, it can't cause Undefined
-Behavior.**
-
-This means that Unsafe Rust, **the royal vanguard of Undefined Behavior**, has to be
-*super paranoid* about generic safe code. To be clear, Unsafe Rust is totally free to trust
-specific safe code. Anything else would degenerate into infinite spirals of
-paranoid despair. In particular it's generally regarded as ok to trust the standard library
-to be correct. `std` is effectively an extension of the language, and you
-really just have to trust the language. If `std` fails to uphold the
-guarantees it declares, then it's basically a language bug.
-
-That said, it would be best to minimize *needlessly* relying on properties of
-concrete safe code. Bugs happen! Of course, I must reinforce that this is only
-a concern for Unsafe code. Safe code can blindly trust anyone and everyone
-as far as basic memory-safety is concerned.
-
-On the other hand, safe traits are free to declare arbitrary contracts, but because
-implementing them is safe, unsafe code can't trust those contracts to actually
-be upheld. This is different from the concrete case because *anyone* can
-randomly implement the interface. There is something fundamentally different
-about trusting a particular piece of code to be correct, and trusting *all the
-code that will ever be written* to be correct.
-
-For instance Rust has `PartialOrd` and `Ord` traits to try to differentiate
-between types which can "just" be compared, and those that actually implement a
-total ordering. Pretty much every API that wants to work with data that can be
-compared wants Ord data. For instance, a sorted map like BTreeMap
-*doesn't even make sense* for partially ordered types. If you claim to implement
-Ord for a type, but don't actually provide a proper total ordering, BTreeMap will
-get *really confused* and start making a total mess of itself. Data that is
-inserted may be impossible to find!
-
-But that's okay. BTreeMap is safe, so it guarantees that even if you give it a
-completely garbage Ord implementation, it will still do something *safe*. You
-won't start reading uninitialized or unallocated memory. In fact, BTreeMap
-manages to not actually lose any of your data. When the map is dropped, all the
-destructors will be successfully called! Hooray!
-
-However BTreeMap is implemented using a modest spoonful of Unsafe Rust (most collections
-are). That means that it's not necessarily *trivially true* that a bad Ord
-implementation will make BTreeMap behave safely. BTreeMap must be sure not to rely
-on Ord *where safety is at stake*. Ord is provided by safe code, and safety is not
-safe code's responsibility to uphold.
-
-But wouldn't it be grand if there was some way for Unsafe to trust some trait
-contracts *somewhere*? This is the problem that unsafe traits tackle: by marking
-*the trait itself* as unsafe to implement, unsafe code can trust the implementation
-to uphold the trait's contract. Although the trait implementation may be
-incorrect in arbitrary other ways.
-
-For instance, given a hypothetical UnsafeOrd trait, this is technically a valid
-implementation:
+* `Send` is a marker trait (a trait with no API) that promises implementors are
+  safe to send (move) to another thread.
+* `Sync` is a marker trait that promises threads can safely share implementors
+  through a shared reference.
+
+Much of the Rust standard library also uses Unsafe Rust internally, although
+these implementations are rigorously manually checked, and the Safe Rust
+interfaces provided on top of these implementations can be assumed to be safe.
+
+The need for all of this separation boils down a single fundamental property
+of Safe Rust:
+
+**No matter what, Safe Rust can't cause Undefined Behavior.**
+
+The design of the safe/unsafe split means that Safe Rust inherently has to
+trust that any Unsafe Rust it touches has been written correctly (meaning
+the Unsafe Rust actually maintains whatever contracts it is supposed to
+maintain). On the other hand, Unsafe Rust has to be very careful about
+trusting Safe Rust.
+
+As an example, Rust has the `PartialOrd` and `Ord` traits to differentiate
+between types which can "just" be compared, and those that provide a total
+ordering (where every value of the type is either equal to, greater than,
+or less than any other value of the same type). The sorted map type
+`BTreeMap` doesn't make sense for partially-ordered types, and so it
+requires that any key type for it implements the `Ord` trait. However,
+`BTreeMap` has Unsafe Rust code inside of its implementation, and this
+Unsafe Rust code cannot assume that any `Ord` implementation it gets makes
+sense. The unsafe portions of `BTreeMap`'s internals have to be careful to
+maintain all necessary contracts, even if a key type's `Ord` implementation
+does not implement a total ordering.
+
+Unsafe Rust cannot automatically trust Safe Rust. When writing Unsafe Rust,
+you must be careful to only rely on specific Safe Rust code, and not make
+assumptions about potential future Safe Rust code providing the same
+guarantees.
+
+This is the problem that `unsafe` traits exist to resolve. The `BTreeMap`
+type could theoretically require that keys implement a new trait called
+`UnsafeOrd`, rather than `Ord`, that might look like this:
 
 ```rust
-# use std::cmp::Ordering;
-# struct MyType;
-# unsafe trait UnsafeOrd { fn cmp(&self, other: &Self) -> Ordering; }
-unsafe impl UnsafeOrd for MyType {
-    fn cmp(&self, other: &Self) -> Ordering {
-        Ordering::Equal
-    }
+use std::cmp::Ordering;
+
+unsafe trait UnsafeOrd {
+    fn cmp(&self, other: &Self) -> Ordering;
 }
 ```
 
-But it's probably not the implementation you want.
-
-Rust has traditionally avoided making traits unsafe because it makes Unsafe
-pervasive, which is not desirable. The reason Send and Sync are unsafe is because thread
-safety is a *fundamental property* that unsafe code cannot possibly hope to defend
-against in the same way it would defend against a bad Ord implementation. The
-only way to possibly defend against thread-unsafety would be to *not use
-threading at all*. Making every load and store atomic isn't even sufficient,
-because it's possible for complex invariants to exist between disjoint locations
-in memory. For instance, the pointer and capacity of a Vec must be in sync.
-
-Even concurrent paradigms that are traditionally regarded as Totally Safe like
-message passing implicitly rely on some notion of thread safety -- are you
-really message-passing if you pass a pointer? Send and Sync therefore require
-some fundamental level of trust that Safe code can't provide, so they must be
-unsafe to implement. To help obviate the pervasive unsafety that this would
-introduce, Send (resp. Sync) is automatically derived for all types composed only
-of Send (resp. Sync) values. 99% of types are Send and Sync, and 99% of those
-never actually say it (the remaining 1% is overwhelmingly synchronization
-primitives).
-
-
-
+Then, a type would use `unsafe` to implement `UnsafeOrd`, indicating that
+they've ensured their implementation maintains whatever contracts the
+trait expects. In this situation, the Unsafe Rust in the internals of
+`BTreeMap` could trust that the key type's `UnsafeOrd` implementation is
+correct. If it isn't, it's the fault of the unsafe trait implementation
+code, which is consistent with Rust's safety guarantees.
+
+The decision of whether to mark a trait `unsafe` is an API design choice.
+Rust has traditionally avoided marking traits unsafe because it makes Unsafe
+Rust pervasive, which is not desirable. `Send` and `Sync` are marked unsafe
+because thread safety is a *fundamental property* that unsafe code can't
+possibly hope to defend against in the way it could defend against a bad
+`Ord` implementation. The decision of whether to mark your own traits `unsafe`
+depends on the same sort of consideration. If `unsafe` code cannot reasonably
+expect to defend against a bad implementation of the trait, then marking the
+trait `unsafe` is a reasonable choice.
+
+As an aside, while `Send` and `Sync` are `unsafe` traits, they are
+automatically implemented for types when such derivations are provably safe
+to do. `Send` is automatically derived for all types composed only of values
+whose types also implement `Send`. `Sync` is automatically derived for all
+types composed only of values whose types also implement `Sync`.
+
+This is the dance of Safe Rust and Unsafe Rust. It is designed to make using
+Safe Rust as ergonomic as possible, but requires extra effort and care when
+writing Unsafe Rust. The rest of the book is largely a discussion of the sort
+of care that must be taken, and what contracts it is expected of Unsafe Rust
+to uphold.
 
 [drop flags]: drop-flags.html
 [conversions]: conversions.html
+
index ebb111a2e2e7378cb06c890a998e24bc868144a8..59dbffd6e28e7e705f679cb59bd9454015c8c339 100644 (file)
@@ -114,12 +114,20 @@ Non-doc comments are interpreted as a form of whitespace.
 
 ## Whitespace
 
-Whitespace is any non-empty string containing only the following characters:
-
+Whitespace is any non-empty string containing only characters that have the
+`Pattern_White_Space` Unicode property, namely:
+
+- `U+0009` (horizontal tab, `'\t'`)
+- `U+000A` (line feed, `'\n'`)
+- `U+000B` (vertical tab)
+- `U+000C` (form feed)
+- `U+000D` (carriage return, `'\r'`)
 - `U+0020` (space, `' '`)
-- `U+0009` (tab, `'\t'`)
-- `U+000A` (LF, `'\n'`)
-- `U+000D` (CR, `'\r'`)
+- `U+0085` (next line)
+- `U+200E` (left-to-right mark)
+- `U+200F` (right-to-left mark)
+- `U+2028` (line separator)
+- `U+2029` (paragraph separator)
 
 Rust is a "free-form" language, meaning that all forms of whitespace serve only
 to separate _tokens_ in the grammar, and have no semantic significance.
@@ -1628,7 +1636,7 @@ Functions within external blocks may be called by Rust code, just like
 functions defined in Rust. The Rust compiler automatically translates between
 the Rust ABI and the foreign ABI.
 
-A number of [attributes](#attributes) control the behavior of external blocks.
+A number of [attributes](#ffi-attributes) control the behavior of external blocks.
 
 By default external blocks assume that the library they are calling uses the
 standard C "cdecl" ABI. Other ABIs may be specified using an `abi` string, as
@@ -1983,6 +1991,7 @@ macro scope.
 
 ### Miscellaneous attributes
 
+- `deprecated` - mark the item as deprecated; the full attribute is `#[deprecated(since = "crate version", note = "...")`, where both arguments are optional.
 - `export_name` - on statics and functions, this determines the name of the
   exported symbol.
 - `link_section` - on statics and functions, this specifies the section of the
@@ -2426,13 +2435,13 @@ The currently implemented features of the reference compiler are:
 * - `stmt_expr_attributes` - Allows attributes on expressions and
                              non-item statements.
 
-* - `deprecated` - Allows using the `#[deprecated]` attribute.
-
 * - `type_ascription` - Allows type ascription expressions `expr: Type`.
 
 * - `abi_vectorcall` - Allows the usage of the vectorcall calling convention
                              (e.g. `extern "vectorcall" func fn_();`)
 
+* - `dotdot_in_tuple_patterns` - Allows `..` in tuple (struct) patterns.
+
 If a feature is promoted to a language feature, then all existing programs will
 start to receive compilation warnings about `#![feature]` directives which enabled
 the new feature (because the directive is no longer necessary). However, if a
index 874f69766196fc589477d3e2d54a91fd45a38e44..9c1b3724d8d8168e6b4f6a98d52ff0c7d5e0210f 100644 (file)
@@ -229,7 +229,7 @@ a > code {
 pre.rust .kw { color: #8959A8; }
 pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
 pre.rust .number, pre.rust .string { color: #718C00; }
-pre.rust .self, pre.rust .boolval, pre.rust .prelude-val,
+pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val,
 pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; }
 pre.rust .comment { color: #8E908C; }
 pre.rust .doccomment { color: #4D4D4C; }
index 6fc7219cdb3683fb7c67b8aa84c5164ac430445f..15b3bfebfac2e24806d56360e55ea5e7fcb9decd 100644 (file)
@@ -56,7 +56,19 @@ Error explanations are long form descriptions of error messages provided with
 the compiler. They are accessible via the `--explain` flag. Each explanation
 comes with an example of how to trigger it and advice on how to fix it.
 
-* All of them are accessible [online](https://github.com/rust-lang/rust/blob/master/src/librustc/diagnostics.rs).
+* All of them are accessible [online](http://doc.rust-lang.org/error-index.html),
+  which are auto-generated from rustc source code in different places:
+  [librustc](https://github.com/rust-lang/rust/blob/master/src/librustc/diagnostics.rs),
+  [librustc_borrowck](https://github.com/rust-lang/rust/blob/master/src/librustc_borrowck/diagnostics.rs),
+  [librustc_const_eval](https://github.com/rust-lang/rust/blob/master/src/librustc_const_eval/diagnostics.rs),
+  [librustc_lint](https://github.com/rust-lang/rust/blob/master/src/librustc_lint/types.rs),
+  [librustc_metadata](https://github.com/rust-lang/rust/blob/master/src/librustc_metadata/diagnostics.rs),
+  [librustc_mir](https://github.com/rust-lang/rust/blob/master/src/librustc_mir/diagnostics.rs),
+  [librustc_passes](https://github.com/rust-lang/rust/blob/master/src/librustc_passes/diagnostics.rs),
+  [librustc_privacy](https://github.com/rust-lang/rust/blob/master/src/librustc_privacy/diagnostics.rs),
+  [librustc_resolve](https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/diagnostics.rs),
+  [librustc_trans](https://github.com/rust-lang/rust/blob/master/src/librustc_trans/diagnostics.rs),
+  [librustc_typeck](https://github.com/rust-lang/rust/blob/master/src/librustc_typeck/diagnostics.rs).
 * Explanations have full markdown support. Use it, especially to highlight
 code with backticks.
 * When talking about the compiler, call it `the compiler`, not `Rust` or
diff --git a/src/etc/Dockerfile b/src/etc/Dockerfile
new file mode 100644 (file)
index 0000000..94be84a
--- /dev/null
@@ -0,0 +1,33 @@
+FROM ubuntu:xenial
+
+# curl
+#   Download stage0, see src/bootstrap/bootstrap.py
+# g++
+#   Compile LLVM binding in src/rustllvm
+# gdb
+#   Used to run tests in src/test/debuginfo
+# git
+#   Get commit hash and commit date in version string
+# make
+#   Run build scripts in mk
+# libedit-dev zlib1g-dev
+#   LLVM dependencies as packaged in Ubuntu
+#   (They are optional, but Ubuntu package enables them)
+# llvm-3.7-dev (installed by llvm-3.7-tools)
+#   LLVM
+# llvm-3.7-tools
+#   FileCheck is used to run tests in src/test/codegen
+
+RUN apt-get update && apt-get -y install \
+    curl g++ gdb git make \
+    libedit-dev zlib1g-dev \
+    llvm-3.7-tools cmake
+
+# When we compile compiler-rt we pass it the llvm-config we just installed on
+# the system, but unfortunately it doesn't infer correctly where
+# LLVMConfig.cmake is so we need to coerce it a bit...
+RUN mkdir -p /usr/lib/llvm-3.7/build/share/llvm
+RUN ln -s /usr/share/llvm-3.7/cmake /usr/lib/llvm-3.7/build/share/llvm/cmake
+
+RUN mkdir /build
+WORKDIR /build
index 06a83c75936fe4a44a6584b9cbf9e09b9a554d1c..b2bb7859661ab63aae27658359731647887741f9 100644 (file)
@@ -139,7 +139,7 @@ class Type(object):
             return TYPE_KIND_STR_SLICE
 
         # REGULAR SLICE
-        if (unqualified_type_name.startswith("&[") and
+        if (unqualified_type_name.startswith(("&[", "&mut [")) and
             unqualified_type_name.endswith("]") and
             self.__conforms_to_field_layout(SLICE_FIELD_NAMES)):
             return TYPE_KIND_SLICE
index 38c9fbf98281a1c42c66fb57782b74fa51f4fddd..ad0c9d085aabb4e0bf52614976e582c6e3de9b32 100755 (executable)
 
 import gdb
 import re
+import sys
 import debugger_pretty_printers_common as rustpp
 
+if sys.version_info.major >= 3:
+    xrange = range
+
 #===============================================================================
 # GDB Pretty Printing Module for Rust
 #===============================================================================
@@ -211,15 +215,12 @@ class RustSlicePrinter:
                 ("(len: %i)" % length))
 
     def children(self):
-        cs = []
         (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
         assert data_ptr.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR
         raw_ptr = data_ptr.get_wrapped_value()
 
-        for index in range(0, length):
-            cs.append((str(index), (raw_ptr + index).dereference()))
-
-        return cs
+        for index in xrange(0, length):
+            yield (str(index), (raw_ptr + index).dereference())
 
 
 class RustStringSlicePrinter:
@@ -245,12 +246,10 @@ class RustStdVecPrinter:
                 ("(len: %i, cap: %i)" % (length, cap)))
 
     def children(self):
-        cs = []
         (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val)
         gdb_ptr = data_ptr.get_wrapped_value()
-        for index in range(0, length):
-            cs.append((str(index), (gdb_ptr + index).dereference()))
-        return cs
+        for index in xrange(0, length):
+            yield (str(index), (gdb_ptr + index).dereference())
 
 
 class RustStdStringPrinter:
index 3a609957faff1c1d8f4ad82e537768ec2e223b4f..28e3363189a082c6152c9bffb8443b44ac190102 100644 (file)
 # except according to those terms.
 
 import os
-import shutil
 import sys
-import tarfile
 
 path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../bootstrap"))
 sys.path.append(path)
 
 import bootstrap
 
-def main(argv):
+def main(triple):
     src_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
-    triple = argv[1]
     data = bootstrap.stage0_data(src_root)
 
     channel, date = data['rustc'].split('-', 1)
@@ -31,12 +28,12 @@ def main(argv):
     if not os.path.exists(dl_dir):
         os.makedirs(dl_dir)
 
-    filename_base = 'rustc-' + channel + '-' + triple
-    filename = filename_base + '.tar.gz'
-    url = 'https://static.rust-lang.org/dist/' + date + '/' + filename
+    filename = 'rustc-{}-{}.tar.gz'.format(channel, triple)
+    url = 'https://static.rust-lang.org/dist/{}/{}'.format(date, filename)
     dst = dl_dir + '/' + filename
-    if not os.path.exists(dst):
-        bootstrap.get(url, dst)
+    if os.path.exists(dst):
+        os.unlink(dst)
+    bootstrap.get(url, dst)
 
     stage0_dst = triple + '/stage0'
     if os.path.exists(stage0_dst):
@@ -48,4 +45,4 @@ def main(argv):
     bootstrap.unpack(dst, stage0_dst, match='rustc', verbose=True)
 
 if __name__ == '__main__':
-    main(sys.argv)
+    main(sys.argv[1])
index a930a0d083367d8a8a2982d334387748a6019fed..a5449b748dd5e98c2434bb2d86774f21bd9a62d4 100644 (file)
@@ -117,6 +117,7 @@ from xml.etree import cElementTree as ET
 from htmlentitydefs import entitydefs
 entitydefs['larrb'] = u'\u21e4'
 entitydefs['rarrb'] = u'\u21e5'
+entitydefs['nbsp'] = ' '
 
 # "void elements" (no closing tag) from the HTML Standard section 12.1.2
 VOID_ELEMENTS = set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
index 42902b06aee13637bc432039a93bcac16123de6b..f70ab65bce717044d448d893c445339268293795 100755 (executable)
 # Exit if anything fails
 set -e
 
+LLDB_VERSION=`lldb --version 2>/dev/null | head -1 | cut -d. -f1`
+
+if [ "$LLDB_VERSION" = "lldb-350" ]
+then
+    echo "***"
+       echo \
+"WARNING: This version of LLDB has known issues with Rust and cannot \
+display the contents of local variables!"
+    echo "***"
+fi
+
 # Create a tempfile containing the LLDB script we want to execute on startup
 TMPFILE=`mktemp /tmp/rust-lldb-commands.XXXXXX`
 
index 6859b3feeae0c69e26f546e1c2b1a7997fc2a0c9..e9b86afcfd9b44cb83a1a9905ba954f6ab22e612 100644 (file)
@@ -168,6 +168,33 @@ zone_force_unlock(malloc_zone_t *zone)
                jemalloc_postfork_parent();
 }
 
+static malloc_zone_t *get_default_zone()
+{
+       malloc_zone_t **zones = NULL;
+       unsigned int num_zones = 0;
+
+       /*
+        * On OSX 10.12, malloc_default_zone returns a special zone that is not
+        * present in the list of registered zones. That zone uses a "lite zone"
+        * if one is present (apparently enabled when malloc stack logging is
+        * enabled), or the first registered zone otherwise. In practice this
+        * means unless malloc stack logging is enabled, the first registered
+        * zone is the default.
+        * So get the list of zones to get the first one, instead of relying on
+        * malloc_default_zone.
+        */
+        if (KERN_SUCCESS != malloc_get_all_zones(0, NULL, (vm_address_t**) &zones,
+                                                &num_zones)) {
+               /* Reset the value in case the failure happened after it was set. */
+               num_zones = 0;
+       }
+
+       if (num_zones)
+               return zones[0];
+
+       return malloc_default_zone();
+}
+
 JEMALLOC_ATTR(constructor)
 void
 register_zone(void)
@@ -177,7 +204,7 @@ register_zone(void)
         * If something else replaced the system default zone allocator, don't
         * register jemalloc's.
         */
-       malloc_zone_t *default_zone = malloc_default_zone();
+       malloc_zone_t *default_zone = get_default_zone();
        malloc_zone_t *purgeable_zone = NULL;
        if (!default_zone->zone_name ||
            strcmp(default_zone->zone_name, "DefaultMallocZone") != 0) {
@@ -246,7 +273,6 @@ register_zone(void)
        malloc_zone_register(&zone);
 
        do {
-               default_zone = malloc_default_zone();
                /*
                 * Unregister and reregister the default zone.  On OSX >= 10.6,
                 * unregistering takes the last registered zone and places it
@@ -272,5 +298,7 @@ register_zone(void)
                        malloc_zone_unregister(purgeable_zone);
                        malloc_zone_register(purgeable_zone);
                }
-       } while (malloc_default_zone() != &zone);
+
+               default_zone = get_default_zone();
+       } while (default_zone != &zone);
 }
index d0a51e320cc2f8508a36b9c7006dafd3916207aa..729d9218a2960f85c8fd5a3c4d20832b9df9127e 100644 (file)
@@ -72,7 +72,7 @@
 use boxed::Box;
 
 use core::sync::atomic;
-use core::sync::atomic::Ordering::{Relaxed, Release, Acquire, SeqCst};
+use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
 use core::borrow;
 use core::fmt;
 use core::cmp::Ordering;
@@ -85,7 +85,7 @@ use core::ops::CoerceUnsized;
 use core::ptr::{self, Shared};
 use core::marker::Unsize;
 use core::hash::{Hash, Hasher};
-use core::{usize, isize};
+use core::{isize, usize};
 use core::convert::From;
 use heap::deallocate;
 
@@ -608,11 +608,13 @@ impl<T> Weak<T> {
     #[stable(feature = "downgraded_weak", since = "1.10.0")]
     pub fn new() -> Weak<T> {
         unsafe {
-            Weak { ptr: Shared::new(Box::into_raw(box ArcInner {
-                strong: atomic::AtomicUsize::new(0),
-                weak: atomic::AtomicUsize::new(1),
-                data: uninitialized(),
-            }))}
+            Weak {
+                ptr: Shared::new(Box::into_raw(box ArcInner {
+                    strong: atomic::AtomicUsize::new(0),
+                    weak: atomic::AtomicUsize::new(1),
+                    data: uninitialized(),
+                })),
+            }
         }
     }
 }
@@ -655,7 +657,9 @@ impl<T: ?Sized> Weak<T> {
 
             // See comments in `Arc::clone` for why we do this (for `mem::forget`).
             if n > MAX_REFCOUNT {
-                unsafe { abort(); }
+                unsafe {
+                    abort();
+                }
             }
 
             // Relaxed is valid for the same reason it is on Arc's Clone impl
@@ -946,7 +950,7 @@ mod tests {
     use std::mem::drop;
     use std::ops::Drop;
     use std::option::Option;
-    use std::option::Option::{Some, None};
+    use std::option::Option::{None, Some};
     use std::sync::atomic;
     use std::sync::atomic::Ordering::{Acquire, SeqCst};
     use std::thread;
index 10e4ea1c3f055ab7e0827c9ffb6b76bef9512261..51523ca8dc601126d8dafdb2a50d52cd103b962f 100644 (file)
@@ -64,7 +64,7 @@ use core::hash::{self, Hash};
 use core::marker::{self, Unsize};
 use core::mem;
 use core::ops::{CoerceUnsized, Deref, DerefMut};
-use core::ops::{Placer, Boxed, Place, InPlace, BoxPlace};
+use core::ops::{BoxPlace, Boxed, InPlace, Place, Placer};
 use core::ptr::{self, Unique};
 use core::raw::TraitObject;
 use core::convert::From;
@@ -535,7 +535,8 @@ pub trait FnBox<A> {
 
 #[unstable(feature = "fnbox",
            reason = "will be deprecated if and when Box<FnOnce> becomes usable", issue = "28796")]
-impl<A, F> FnBox<A> for F where F: FnOnce<A>
+impl<A, F> FnBox<A> for F
+    where F: FnOnce<A>
 {
     type Output = F::Output;
 
index 120301afa449f910f2201e5ec16ed2f07bb65a39..8d68ce3c1f6e2ebfa406c81a15cb50c478df4139 100644 (file)
@@ -12,7 +12,7 @@
 
 use core::any::Any;
 use core::ops::Deref;
-use core::result::Result::{Ok, Err};
+use core::result::Result::{Err, Ok};
 use core::clone::Clone;
 
 use std::boxed::Box;
index 08b403a60f3819d7ee07e9186141d237ac0fadab..bfed8a8e83a5a4dc25f6211c7bdb82cd4dd73691 100644 (file)
@@ -17,7 +17,7 @@
 
 use core::{isize, usize};
 #[cfg(not(test))]
-use core::intrinsics::{size_of, min_align_of};
+use core::intrinsics::{min_align_of, size_of};
 
 #[allow(improper_ctypes)]
 extern "C" {
index 8b3168b29aa4f5dafa1b1923d6b6cc5a2b24cbb5..58c841151c0f0e1b4bf0b46d0db8196c215ffdd1 100644 (file)
@@ -578,9 +578,9 @@ impl<T> Drop for RawVec<T> {
 // * We don't overflow `usize::MAX` and actually allocate too little
 //
 // On 64-bit we just need to check for overflow since trying to allocate
-// `> isize::MAX` bytes will surely fail. On 32-bit we need to add an extra
-// guard for this in case we're running on a platform which can use all 4GB in
-// user-space. e.g. PAE or x32
+// `> isize::MAX` bytes will surely fail. On 32-bit and 16-bit we need to add
+// an extra guard for this in case we're running on a platform which can use
+// all 4GB in user-space. e.g. PAE or x32
 
 #[inline]
 fn alloc_guard(alloc_size: usize) {
index b92f5af05e31592b13d2f4b284d5997d58c3d450..a873be455d5558c673c3f6cd4d0d50f89f611bbd 100644 (file)
@@ -159,11 +159,11 @@ use core::borrow;
 use core::cell::Cell;
 use core::cmp::Ordering;
 use core::fmt;
-use core::hash::{Hasher, Hash};
-use core::intrinsics::{assume, abort};
+use core::hash::{Hash, Hasher};
+use core::intrinsics::{abort, assume};
 use core::marker;
 use core::marker::Unsize;
-use core::mem::{self, align_of_val, size_of_val, forget, uninitialized};
+use core::mem::{self, align_of_val, forget, size_of_val, uninitialized};
 use core::ops::Deref;
 use core::ops::CoerceUnsized;
 use core::ptr::{self, Shared};
@@ -271,7 +271,7 @@ impl<T> Rc<T> {
 }
 
 impl<T: ?Sized> Rc<T> {
-    /// Downgrades the `Rc<T>` to a `Weak<T>` reference.
+    /// Creates a new `Weak<T>` reference from this value.
     ///
     /// # Examples
     ///
@@ -935,7 +935,7 @@ mod tests {
     use std::boxed::Box;
     use std::cell::RefCell;
     use std::option::Option;
-    use std::option::Option::{Some, None};
+    use std::option::Option::{None, Some};
     use std::result::Result::{Err, Ok};
     use std::mem::drop;
     use std::clone::Clone;
index 33a675331ab9dbb91e471e2a9b9d2ffde3e223fc..d1b3583d256b6e11bc6136c6ea364a5216b33a2e 100644 (file)
@@ -33,18 +33,27 @@ fn main() {
                  jemalloc.parent().unwrap().display());
         let stem = jemalloc.file_stem().unwrap().to_str().unwrap();
         let name = jemalloc.file_name().unwrap().to_str().unwrap();
-        let kind = if name.ends_with(".a") {"static"} else {"dylib"};
+        let kind = if name.ends_with(".a") {
+            "static"
+        } else {
+            "dylib"
+        };
         println!("cargo:rustc-link-lib={}={}", kind, &stem[3..]);
-        return
+        return;
     }
 
     let compiler = gcc::Config::new().get_compiler();
-    let ar = build_helper::cc2ar(compiler.path(), &target);
-    let cflags = compiler.args().iter().map(|s| s.to_str().unwrap())
-                         .collect::<Vec<_>>().join(" ");
+    // only msvc returns None for ar so unwrap is okay
+    let ar = build_helper::cc2ar(compiler.path(), &target).unwrap();
+    let cflags = compiler.args()
+                         .iter()
+                         .map(|s| s.to_str().unwrap())
+                         .collect::<Vec<_>>()
+                         .join(" ");
 
     let mut stack = src_dir.join("../jemalloc")
-                           .read_dir().unwrap()
+                           .read_dir()
+                           .unwrap()
                            .map(|e| e.unwrap())
                            .collect::<Vec<_>>();
     while let Some(entry) = stack.pop() {
@@ -57,7 +66,9 @@ fn main() {
     }
 
     let mut cmd = Command::new("sh");
-    cmd.arg(src_dir.join("../jemalloc/configure").to_str().unwrap()
+    cmd.arg(src_dir.join("../jemalloc/configure")
+                   .to_str()
+                   .unwrap()
                    .replace("C:\\", "/c/")
                    .replace("\\", "/"))
        .current_dir(&build_dir)
@@ -117,9 +128,10 @@ fn main() {
 
     run(&mut cmd);
     run(Command::new("make")
-                .current_dir(&build_dir)
-                .arg("build_lib_static")
-                .arg("-j").arg(env::var("NUM_JOBS").unwrap()));
+            .current_dir(&build_dir)
+            .arg("build_lib_static")
+            .arg("-j")
+            .arg(env::var("NUM_JOBS").unwrap()));
 
     if target.contains("windows") {
         println!("cargo:rustc-link-lib=static=jemalloc");
index 7651d91c06d6bb55d2368cd247a011e483d50c56..347e97e6ffc0a8ddd84e25b3a0637bf499d0ab8a 100644 (file)
@@ -39,12 +39,12 @@ use libc::{c_int, c_void, size_t};
                not(target_env = "musl")),
            link(name = "pthread"))]
 #[cfg(not(cargobuild))]
-extern {}
+extern "C" {}
 
 // Note that the symbols here are prefixed by default on OSX and Windows (we
 // don't explicitly request it), and on Android and DragonFly we explicitly
 // request it as unprefixing cause segfaults (mismatches in allocators).
-extern {
+extern "C" {
     #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                    target_os = "dragonfly", target_os = "windows"),
                link_name = "je_mallocx")]
@@ -136,8 +136,9 @@ pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize {
 // are available.
 #[no_mangle]
 #[cfg(target_os = "android")]
-pub extern fn pthread_atfork(_prefork: *mut u8,
-                             _postfork_parent: *mut u8,
-                             _postfork_child: *mut u8) -> i32 {
+pub extern "C" fn pthread_atfork(_prefork: *mut u8,
+                                 _postfork_parent: *mut u8,
+                                 _postfork_child: *mut u8)
+                                 -> i32 {
     0
 }
index 43c6e6e81209d93eb19e7cc45771a97438c19926..b9f5c6fcab9092a3d4565c5980a82f65888ff664 100644 (file)
 #![allow(missing_docs)]
 #![stable(feature = "rust1", since = "1.0.0")]
 
+use core::ops::{Drop, Deref, DerefMut};
 use core::iter::FromIterator;
 use core::mem::swap;
 use core::mem::size_of;
@@ -218,6 +219,37 @@ pub struct BinaryHeap<T> {
     data: Vec<T>,
 }
 
+/// A container object that represents the result of the [`peek_mut()`] method
+/// on `BinaryHeap`. See its documentation for details.
+///
+/// [`peek_mut()`]: struct.BinaryHeap.html#method.peek_mut
+#[unstable(feature = "binary_heap_peek_mut", issue = "34392")]
+pub struct PeekMut<'a, T: 'a + Ord> {
+    heap: &'a mut BinaryHeap<T>
+}
+
+#[unstable(feature = "binary_heap_peek_mut", issue = "34392")]
+impl<'a, T: Ord> Drop for PeekMut<'a, T> {
+    fn drop(&mut self) {
+        self.heap.sift_down(0);
+    }
+}
+
+#[unstable(feature = "binary_heap_peek_mut", issue = "34392")]
+impl<'a, T: Ord> Deref for PeekMut<'a, T> {
+    type Target = T;
+    fn deref(&self) -> &T {
+        &self.heap.data[0]
+    }
+}
+
+#[unstable(feature = "binary_heap_peek_mut", issue = "34392")]
+impl<'a, T: Ord> DerefMut for PeekMut<'a, T> {
+    fn deref_mut(&mut self) -> &mut T {
+        &mut self.heap.data[0]
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: Clone> Clone for BinaryHeap<T> {
     fn clone(&self) -> Self {
@@ -323,6 +355,42 @@ impl<T: Ord> BinaryHeap<T> {
         self.data.get(0)
     }
 
+    /// Returns a mutable reference to the greatest item in the binary heap, or
+    /// `None` if it is empty.
+    ///
+    /// Note: If the `PeekMut` value is leaked, the heap may be in an
+    /// inconsistent state.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(binary_heap_peek_mut)]
+    /// use std::collections::BinaryHeap;
+    /// let mut heap = BinaryHeap::new();
+    /// assert!(heap.peek_mut().is_none());
+    ///
+    /// heap.push(1);
+    /// heap.push(5);
+    /// heap.push(2);
+    /// {
+    ///     let mut val = heap.peek_mut().unwrap();
+    ///     *val = 0;
+    /// }
+    /// assert_eq!(heap.peek(), Some(&2));
+    /// ```
+    #[unstable(feature = "binary_heap_peek_mut", issue = "34392")]
+    pub fn peek_mut(&mut self) -> Option<PeekMut<T>> {
+        if self.is_empty() {
+            None
+        } else {
+            Some(PeekMut {
+                heap: self
+            })
+        }
+    }
+
     /// Returns the number of elements the binary heap can hold without reallocating.
     ///
     /// # Examples
@@ -757,8 +825,6 @@ impl<T: Ord> BinaryHeap<T> {
     /// Basic usage:
     ///
     /// ```
-    /// #![feature(binary_heap_append)]
-    ///
     /// use std::collections::BinaryHeap;
     ///
     /// let v = vec![-10, 1, 2, 3, 3];
@@ -772,9 +838,7 @@ impl<T: Ord> BinaryHeap<T> {
     /// assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
     /// assert!(b.is_empty());
     /// ```
-    #[unstable(feature = "binary_heap_append",
-           reason = "needs to be audited",
-           issue = "32526")]
+    #[stable(feature = "binary_heap_append", since = "1.11.0")]
     pub fn append(&mut self, other: &mut Self) {
         if self.len() < other.len() {
             swap(self, other);
index 6ca0db68a88ce42a1c87330c47fe1b8a34b38dc5..37dbeb4eae17d6d61f7dad0929e8ae3c9830c72d 100644 (file)
@@ -15,6 +15,7 @@
 use core::clone::Clone;
 use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
 use core::convert::AsRef;
+use core::default::Default;
 use core::hash::{Hash, Hasher};
 use core::marker::Sized;
 use core::ops::Deref;
@@ -248,6 +249,16 @@ impl<'a, B: ?Sized> fmt::Display for Cow<'a, B>
     }
 }
 
+#[stable(feature = "default", since = "1.11.0")]
+impl<'a, B: ?Sized> Default for Cow<'a, B>
+    where B: ToOwned,
+          <B as ToOwned>::Owned: Default
+{
+    fn default() -> Cow<'a, B> {
+        Owned(<B as ToOwned>::Owned::default())
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, B: ?Sized> Hash for Cow<'a, B> where B: Hash + ToOwned {
     #[inline]
index ec2f4a9f7f0b82208b230a687c95cd28e8c51a7e..aea7a1c13a28171778b427920c78c8cd89d3a1d0 100644 (file)
@@ -17,9 +17,9 @@ use core::ops::Index;
 use core::{fmt, intrinsics, mem, ptr};
 
 use borrow::Borrow;
-use Bound::{self, Included, Excluded, Unbounded};
+use Bound::{self, Excluded, Included, Unbounded};
 
-use super::node::{self, NodeRef, Handle, marker};
+use super::node::{self, Handle, NodeRef, marker};
 use super::search;
 
 use super::node::InsertResult::*;
@@ -68,7 +68,7 @@ use self::Entry::*;
 /// // would be `BTreeMap<&str, &str>` in this example).
 /// let mut movie_reviews = BTreeMap::new();
 ///
-/// // review some books.
+/// // review some movies.
 /// movie_reviews.insert("Office Space",       "Deals with real issues in the workplace.");
 /// movie_reviews.insert("Pulp Fiction",       "Masterpiece.");
 /// movie_reviews.insert("The Godfather",      "Very enjoyable.");
@@ -129,35 +129,38 @@ use self::Entry::*;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct BTreeMap<K, V> {
     root: node::Root<K, V>,
-    length: usize
+    length: usize,
 }
 
 impl<K, V> Drop for BTreeMap<K, V> {
     #[unsafe_destructor_blind_to_params]
     fn drop(&mut self) {
         unsafe {
-            for _ in ptr::read(self).into_iter() { }
+            for _ in ptr::read(self).into_iter() {
+            }
         }
     }
 }
 
 impl<K: Clone, V: Clone> Clone for BTreeMap<K, V> {
     fn clone(&self) -> BTreeMap<K, V> {
-        fn clone_subtree<K: Clone, V: Clone>(
-                node: node::NodeRef<marker::Immut, K, V, marker::LeafOrInternal>)
-                -> BTreeMap<K, V> {
+        fn clone_subtree<K: Clone, V: Clone>(node: node::NodeRef<marker::Immut,
+                                                                 K,
+                                                                 V,
+                                                                 marker::LeafOrInternal>)
+                                             -> BTreeMap<K, V> {
 
             match node.force() {
                 Leaf(leaf) => {
                     let mut out_tree = BTreeMap {
                         root: node::Root::new_leaf(),
-                        length: 0
+                        length: 0,
                     };
 
                     {
                         let mut out_node = match out_tree.root.as_mut().force() {
                             Leaf(leaf) => leaf,
-                            Internal(_) => unreachable!()
+                            Internal(_) => unreachable!(),
                         };
 
                         let mut in_edge = leaf.first_edge();
@@ -171,7 +174,7 @@ impl<K: Clone, V: Clone> Clone for BTreeMap<K, V> {
                     }
 
                     out_tree
-                },
+                }
                 Internal(internal) => {
                     let mut out_tree = clone_subtree(internal.first_edge().descend());
 
@@ -218,7 +221,7 @@ impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()>
     fn get(&self, key: &Q) -> Option<&K> {
         match search::search_tree(self.root.as_ref(), key) {
             Found(handle) => Some(handle.into_kv().0),
-            GoDown(_) => None
+            GoDown(_) => None,
         }
     }
 
@@ -226,12 +229,14 @@ impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()>
         match search::search_tree(self.root.as_mut(), key) {
             Found(handle) => {
                 Some(OccupiedEntry {
-                    handle: handle,
-                    length: &mut self.length,
-                    _marker: PhantomData,
-                }.remove_kv().0)
-            },
-            GoDown(_) => None
+                         handle: handle,
+                         length: &mut self.length,
+                         _marker: PhantomData,
+                     }
+                     .remove_kv()
+                     .0)
+            }
+            GoDown(_) => None,
         }
     }
 
@@ -244,7 +249,8 @@ impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()>
                     handle: handle,
                     length: &mut self.length,
                     _marker: PhantomData,
-                }.insert(());
+                }
+                .insert(());
                 None
             }
         }
@@ -255,14 +261,14 @@ impl<K, Q: ?Sized> super::Recover<Q> for BTreeMap<K, ()>
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Iter<'a, K: 'a, V: 'a> {
     range: Range<'a, K, V>,
-    length: usize
+    length: usize,
 }
 
 /// A mutable iterator over a BTreeMap's entries.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct IterMut<'a, K: 'a, V: 'a> {
     range: RangeMut<'a, K, V>,
-    length: usize
+    length: usize,
 }
 
 /// An owning iterator over a BTreeMap's entries.
@@ -270,7 +276,7 @@ pub struct IterMut<'a, K: 'a, V: 'a> {
 pub struct IntoIter<K, V> {
     front: Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge>,
     back: Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge>,
-    length: usize
+    length: usize,
 }
 
 /// An iterator over a BTreeMap's keys.
@@ -294,7 +300,7 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> {
 /// An iterator over a sub-range of BTreeMap's entries.
 pub struct Range<'a, K: 'a, V: 'a> {
     front: Handle<NodeRef<marker::Immut<'a>, K, V, marker::Leaf>, marker::Edge>,
-    back: Handle<NodeRef<marker::Immut<'a>, K, V, marker::Leaf>, marker::Edge>
+    back: Handle<NodeRef<marker::Immut<'a>, K, V, marker::Leaf>, marker::Edge>,
 }
 
 /// A mutable iterator over a sub-range of BTreeMap's entries.
@@ -311,15 +317,13 @@ pub struct RangeMut<'a, K: 'a, V: 'a> {
 pub enum Entry<'a, K: 'a, V: 'a> {
     /// A vacant Entry
     #[stable(feature = "rust1", since = "1.0.0")]
-    Vacant(
-        #[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>
-    ),
+    Vacant(#[stable(feature = "rust1", since = "1.0.0")]
+           VacantEntry<'a, K, V>),
 
     /// An occupied Entry
     #[stable(feature = "rust1", since = "1.0.0")]
-    Occupied(
-        #[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>
-    ),
+    Occupied(#[stable(feature = "rust1", since = "1.0.0")]
+             OccupiedEntry<'a, K, V>),
 }
 
 /// A vacant Entry.
@@ -336,11 +340,7 @@ pub struct VacantEntry<'a, K: 'a, V: 'a> {
 /// An occupied Entry.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
-    handle: Handle<NodeRef<
-        marker::Mut<'a>,
-        K, V,
-        marker::LeafOrInternal
-    >, marker::KV>,
+    handle: Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV>,
 
     length: &'a mut usize,
 
@@ -349,7 +349,7 @@ pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
 }
 
 // An iterator for merging two sorted sequences into one
-struct MergeIter<K, V, I: Iterator<Item=(K, V)>> {
+struct MergeIter<K, V, I: Iterator<Item = (K, V)>> {
     left: Peekable<I>,
     right: Peekable<I>,
 }
@@ -373,7 +373,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
     pub fn new() -> BTreeMap<K, V> {
         BTreeMap {
             root: node::Root::new_leaf(),
-            length: 0
+            length: 0,
         }
     }
 
@@ -415,10 +415,13 @@ impl<K: Ord, V> BTreeMap<K, V> {
     /// assert_eq!(map.get(&2), None);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V> where K: Borrow<Q>, Q: Ord {
+    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
+        where K: Borrow<Q>,
+              Q: Ord
+    {
         match search::search_tree(self.root.as_ref(), key) {
             Found(handle) => Some(handle.into_kv().1),
-            GoDown(_) => None
+            GoDown(_) => None,
         }
     }
 
@@ -440,7 +443,10 @@ impl<K: Ord, V> BTreeMap<K, V> {
     /// assert_eq!(map.contains_key(&2), false);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool where K: Borrow<Q>, Q: Ord {
+    pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
+        where K: Borrow<Q>,
+              Q: Ord
+    {
         self.get(key).is_some()
     }
 
@@ -465,10 +471,13 @@ impl<K: Ord, V> BTreeMap<K, V> {
     /// ```
     // See `get` for implementation notes, this is basically a copy-paste with mut's added
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut V> where K: Borrow<Q>, Q: Ord {
+    pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut V>
+        where K: Borrow<Q>,
+              Q: Ord
+    {
         match search::search_tree(self.root.as_mut(), key) {
             Found(handle) => Some(handle.into_kv_mut().1),
-            GoDown(_) => None
+            GoDown(_) => None,
         }
     }
 
@@ -528,16 +537,20 @@ impl<K: Ord, V> BTreeMap<K, V> {
     /// assert_eq!(map.remove(&1), None);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<V> where K: Borrow<Q>, Q: Ord {
+    pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<V>
+        where K: Borrow<Q>,
+              Q: Ord
+    {
         match search::search_tree(self.root.as_mut(), key) {
             Found(handle) => {
                 Some(OccupiedEntry {
-                    handle: handle,
-                    length: &mut self.length,
-                    _marker: PhantomData,
-                }.remove())
-            },
-            GoDown(_) => None
+                         handle: handle,
+                         length: &mut self.length,
+                         _marker: PhantomData,
+                     }
+                     .remove())
+            }
+            GoDown(_) => None,
         }
     }
 
@@ -546,7 +559,6 @@ impl<K: Ord, V> BTreeMap<K, V> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(btree_append)]
     /// use std::collections::BTreeMap;
     ///
     /// let mut a = BTreeMap::new();
@@ -570,8 +582,7 @@ impl<K: Ord, V> BTreeMap<K, V> {
     /// assert_eq!(a[&4], "e");
     /// assert_eq!(a[&5], "f");
     /// ```
-    #[unstable(feature = "btree_append", reason = "recently added as part of collections reform 2",
-               issue = "19986")]
+    #[stable(feature = "btree_append", since = "1.11.0")]
     pub fn append(&mut self, other: &mut Self) {
         // Do we have to append anything at all?
         if other.len() == 0 {
@@ -628,47 +639,63 @@ impl<K: Ord, V> BTreeMap<K, V> {
                                                        min: Bound<&Min>,
                                                        max: Bound<&Max>)
                                                        -> Range<K, V>
-        where K: Borrow<Min> + Borrow<Max>,
+        where K: Borrow<Min> + Borrow<Max>
     {
         let front = match min {
-            Included(key) => match search::search_tree(self.root.as_ref(), key) {
-                Found(kv_handle) => match kv_handle.left_edge().force() {
-                    Leaf(bottom) => bottom,
-                    Internal(internal) => last_leaf_edge(internal.descend())
-                },
-                GoDown(bottom) => bottom
-            },
-            Excluded(key) => match search::search_tree(self.root.as_ref(), key) {
-                Found(kv_handle) => match kv_handle.right_edge().force() {
-                    Leaf(bottom) => bottom,
-                    Internal(internal) => first_leaf_edge(internal.descend())
-                },
-                GoDown(bottom) => bottom
-            },
-            Unbounded => first_leaf_edge(self.root.as_ref())
+            Included(key) => {
+                match search::search_tree(self.root.as_ref(), key) {
+                    Found(kv_handle) => {
+                        match kv_handle.left_edge().force() {
+                            Leaf(bottom) => bottom,
+                            Internal(internal) => last_leaf_edge(internal.descend()),
+                        }
+                    }
+                    GoDown(bottom) => bottom,
+                }
+            }
+            Excluded(key) => {
+                match search::search_tree(self.root.as_ref(), key) {
+                    Found(kv_handle) => {
+                        match kv_handle.right_edge().force() {
+                            Leaf(bottom) => bottom,
+                            Internal(internal) => first_leaf_edge(internal.descend()),
+                        }
+                    }
+                    GoDown(bottom) => bottom,
+                }
+            }
+            Unbounded => first_leaf_edge(self.root.as_ref()),
         };
 
         let back = match max {
-            Included(key) => match search::search_tree(self.root.as_ref(), key) {
-                Found(kv_handle) => match kv_handle.right_edge().force() {
-                    Leaf(bottom) => bottom,
-                    Internal(internal) => first_leaf_edge(internal.descend())
-                },
-                GoDown(bottom) => bottom
-            },
-            Excluded(key) => match search::search_tree(self.root.as_ref(), key) {
-                Found(kv_handle) => match kv_handle.left_edge().force() {
-                    Leaf(bottom) => bottom,
-                    Internal(internal) => last_leaf_edge(internal.descend())
-                },
-                GoDown(bottom) => bottom
-            },
-            Unbounded => last_leaf_edge(self.root.as_ref())
+            Included(key) => {
+                match search::search_tree(self.root.as_ref(), key) {
+                    Found(kv_handle) => {
+                        match kv_handle.right_edge().force() {
+                            Leaf(bottom) => bottom,
+                            Internal(internal) => first_leaf_edge(internal.descend()),
+                        }
+                    }
+                    GoDown(bottom) => bottom,
+                }
+            }
+            Excluded(key) => {
+                match search::search_tree(self.root.as_ref(), key) {
+                    Found(kv_handle) => {
+                        match kv_handle.left_edge().force() {
+                            Leaf(bottom) => bottom,
+                            Internal(internal) => last_leaf_edge(internal.descend()),
+                        }
+                    }
+                    GoDown(bottom) => bottom,
+                }
+            }
+            Unbounded => last_leaf_edge(self.root.as_ref()),
         };
 
         Range {
             front: front,
-            back: back
+            back: back,
         }
     }
 
@@ -704,51 +731,67 @@ impl<K: Ord, V> BTreeMap<K, V> {
                                                            min: Bound<&Min>,
                                                            max: Bound<&Max>)
                                                            -> RangeMut<K, V>
-        where K: Borrow<Min> + Borrow<Max>,
+        where K: Borrow<Min> + Borrow<Max>
     {
         let root1 = self.root.as_mut();
         let root2 = unsafe { ptr::read(&root1) };
 
         let front = match min {
-            Included(key) => match search::search_tree(root1, key) {
-                Found(kv_handle) => match kv_handle.left_edge().force() {
-                    Leaf(bottom) => bottom,
-                    Internal(internal) => last_leaf_edge(internal.descend())
-                },
-                GoDown(bottom) => bottom
-            },
-            Excluded(key) => match search::search_tree(root1, key) {
-                Found(kv_handle) => match kv_handle.right_edge().force() {
-                    Leaf(bottom) => bottom,
-                    Internal(internal) => first_leaf_edge(internal.descend())
-                },
-                GoDown(bottom) => bottom
-            },
-            Unbounded => first_leaf_edge(root1)
+            Included(key) => {
+                match search::search_tree(root1, key) {
+                    Found(kv_handle) => {
+                        match kv_handle.left_edge().force() {
+                            Leaf(bottom) => bottom,
+                            Internal(internal) => last_leaf_edge(internal.descend()),
+                        }
+                    }
+                    GoDown(bottom) => bottom,
+                }
+            }
+            Excluded(key) => {
+                match search::search_tree(root1, key) {
+                    Found(kv_handle) => {
+                        match kv_handle.right_edge().force() {
+                            Leaf(bottom) => bottom,
+                            Internal(internal) => first_leaf_edge(internal.descend()),
+                        }
+                    }
+                    GoDown(bottom) => bottom,
+                }
+            }
+            Unbounded => first_leaf_edge(root1),
         };
 
         let back = match max {
-            Included(key) => match search::search_tree(root2, key) {
-                Found(kv_handle) => match kv_handle.right_edge().force() {
-                    Leaf(bottom) => bottom,
-                    Internal(internal) => first_leaf_edge(internal.descend())
-                },
-                GoDown(bottom) => bottom
-            },
-            Excluded(key) => match search::search_tree(root2, key) {
-                Found(kv_handle) => match kv_handle.left_edge().force() {
-                    Leaf(bottom) => bottom,
-                    Internal(internal) => last_leaf_edge(internal.descend())
-                },
-                GoDown(bottom) => bottom
-            },
-            Unbounded => last_leaf_edge(root2)
+            Included(key) => {
+                match search::search_tree(root2, key) {
+                    Found(kv_handle) => {
+                        match kv_handle.right_edge().force() {
+                            Leaf(bottom) => bottom,
+                            Internal(internal) => first_leaf_edge(internal.descend()),
+                        }
+                    }
+                    GoDown(bottom) => bottom,
+                }
+            }
+            Excluded(key) => {
+                match search::search_tree(root2, key) {
+                    Found(kv_handle) => {
+                        match kv_handle.left_edge().force() {
+                            Leaf(bottom) => bottom,
+                            Internal(internal) => last_leaf_edge(internal.descend()),
+                        }
+                    }
+                    GoDown(bottom) => bottom,
+                }
+            }
+            Unbounded => last_leaf_edge(root2),
         };
 
         RangeMut {
             front: front,
             back: back,
-            _marker: PhantomData
+            _marker: PhantomData,
         }
     }
 
@@ -773,21 +816,25 @@ impl<K: Ord, V> BTreeMap<K, V> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn entry(&mut self, key: K) -> Entry<K, V> {
         match search::search_tree(self.root.as_mut(), &key) {
-            Found(handle) => Occupied(OccupiedEntry {
-                handle: handle,
-                length: &mut self.length,
-                _marker: PhantomData,
-            }),
-            GoDown(handle) => Vacant(VacantEntry {
-                key: key,
-                handle: handle,
-                length: &mut self.length,
-                _marker: PhantomData,
-            })
+            Found(handle) => {
+                Occupied(OccupiedEntry {
+                    handle: handle,
+                    length: &mut self.length,
+                    _marker: PhantomData,
+                })
+            }
+            GoDown(handle) => {
+                Vacant(VacantEntry {
+                    key: key,
+                    handle: handle,
+                    length: &mut self.length,
+                    _marker: PhantomData,
+                })
+            }
         }
     }
 
-    fn from_sorted_iter<I: Iterator<Item=(K, V)>>(&mut self, iter: I) {
+    fn from_sorted_iter<I: Iterator<Item = (K, V)>>(&mut self, iter: I) {
         let mut cur_node = last_leaf_edge(self.root.as_mut()).into_node();
         // Iterate through all key-value pairs, pushing them into nodes at the right level.
         for (key, value) in iter {
@@ -810,12 +857,12 @@ impl<K: Ord, V> BTreeMap<K, V> {
                                 // Go up again.
                                 test_node = parent.forget_type();
                             }
-                        },
+                        }
                         Err(node) => {
                             // We are at the top, create a new root node and push there.
                             open_node = node.into_root_mut().push_level();
                             break;
-                        },
+                        }
                     }
                 }
 
@@ -842,13 +889,13 @@ impl<K: Ord, V> BTreeMap<K, V> {
             // Check if right-most child is underfull.
             let mut last_edge = internal.last_edge();
             let right_child_len = last_edge.reborrow().descend().len();
-            if right_child_len < node::CAPACITY / 2 {
+            if right_child_len < node::MIN_LEN {
                 // We need to steal.
                 let mut last_kv = match last_edge.left_kv() {
                     Ok(left) => left,
                     Err(_) => unreachable!(),
                 };
-                last_kv.bulk_steal_left(node::CAPACITY/2 - right_child_len);
+                last_kv.bulk_steal_left(node::MIN_LEN - right_child_len);
                 last_edge = last_kv.right_edge();
             }
 
@@ -856,6 +903,181 @@ impl<K: Ord, V> BTreeMap<K, V> {
             cur_node = last_edge.descend();
         }
     }
+
+    /// Splits the collection into two at the given key. Returns everything after the given key,
+    /// including the key.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut a = BTreeMap::new();
+    /// a.insert(1, "a");
+    /// a.insert(2, "b");
+    /// a.insert(3, "c");
+    /// a.insert(17, "d");
+    /// a.insert(41, "e");
+    ///
+    /// let b = a.split_off(&3);
+    ///
+    /// assert_eq!(a.len(), 2);
+    /// assert_eq!(b.len(), 3);
+    ///
+    /// assert_eq!(a[&1], "a");
+    /// assert_eq!(a[&2], "b");
+    ///
+    /// assert_eq!(b[&3], "c");
+    /// assert_eq!(b[&17], "d");
+    /// assert_eq!(b[&41], "e");
+    /// ```
+    #[stable(feature = "btree_split_off", since = "1.11.0")]
+    pub fn split_off<Q: ?Sized + Ord>(&mut self, key: &Q) -> Self
+        where K: Borrow<Q>
+    {
+        if self.is_empty() {
+            return Self::new();
+        }
+
+        let total_num = self.len();
+
+        let mut right = Self::new();
+        for _ in 0..(self.root.as_ref().height()) {
+            right.root.push_level();
+        }
+
+        {
+            let mut left_node = self.root.as_mut();
+            let mut right_node = right.root.as_mut();
+
+            loop {
+                let mut split_edge = match search::search_node(left_node, key) {
+                    // key is going to the right tree
+                    Found(handle) => handle.left_edge(),
+                    GoDown(handle) => handle,
+                };
+
+                split_edge.move_suffix(&mut right_node);
+
+                match (split_edge.force(), right_node.force()) {
+                    (Internal(edge), Internal(node)) => {
+                        left_node = edge.descend();
+                        right_node = node.first_edge().descend();
+                    }
+                    (Leaf(_), Leaf(_)) => {
+                        break;
+                    }
+                    _ => {
+                        unreachable!();
+                    }
+                }
+            }
+        }
+
+        self.fix_right_border();
+        right.fix_left_border();
+
+        if self.root.as_ref().height() < right.root.as_ref().height() {
+            self.recalc_length();
+            right.length = total_num - self.len();
+        } else {
+            right.recalc_length();
+            self.length = total_num - right.len();
+        }
+
+        right
+    }
+
+    /// Calculates the number of elements if it is incorrect.
+    fn recalc_length(&mut self) {
+        fn dfs<K, V>(node: NodeRef<marker::Immut, K, V, marker::LeafOrInternal>) -> usize {
+            let mut res = node.len();
+
+            if let Internal(node) = node.force() {
+                let mut edge = node.first_edge();
+                loop {
+                    res += dfs(edge.reborrow().descend());
+                    match edge.right_kv() {
+                        Ok(right_kv) => {
+                            edge = right_kv.right_edge();
+                        }
+                        Err(_) => {
+                            break;
+                        }
+                    }
+                }
+            }
+
+            res
+        }
+
+        self.length = dfs(self.root.as_ref());
+    }
+
+    /// Removes empty levels on the top.
+    fn fix_top(&mut self) {
+        loop {
+            {
+                let node = self.root.as_ref();
+                if node.height() == 0 || node.len() > 0 {
+                    break;
+                }
+            }
+            self.root.pop_level();
+        }
+    }
+
+    fn fix_right_border(&mut self) {
+        self.fix_top();
+
+        {
+            let mut cur_node = self.root.as_mut();
+
+            while let Internal(node) = cur_node.force() {
+                let mut last_kv = node.last_kv();
+
+                if last_kv.can_merge() {
+                    cur_node = last_kv.merge().descend();
+                } else {
+                    let right_len = last_kv.reborrow().right_edge().descend().len();
+                    // `MINLEN + 1` to avoid readjust if merge happens on the next level.
+                    if right_len < node::MIN_LEN + 1 {
+                        last_kv.bulk_steal_left(node::MIN_LEN + 1 - right_len);
+                    }
+                    cur_node = last_kv.right_edge().descend();
+                }
+            }
+        }
+
+        self.fix_top();
+    }
+
+    /// The symmetric clone of `fix_right_border`.
+    fn fix_left_border(&mut self) {
+        self.fix_top();
+
+        {
+            let mut cur_node = self.root.as_mut();
+
+            while let Internal(node) = cur_node.force() {
+                let mut first_kv = node.first_kv();
+
+                if first_kv.can_merge() {
+                    cur_node = first_kv.merge().descend();
+                } else {
+                    let left_len = first_kv.reborrow().left_edge().descend().len();
+                    if left_len < node::MIN_LEN + 1 {
+                        first_kv.bulk_steal_right(node::MIN_LEN + 1 - left_len);
+                    }
+                    cur_node = first_kv.left_edge().descend();
+                }
+            }
+        }
+
+        self.fix_top();
+    }
 }
 
 impl<'a, K: 'a, V: 'a> IntoIterator for &'a BTreeMap<K, V> {
@@ -896,14 +1118,16 @@ impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Iter<'a, K, V> {
 }
 
 impl<'a, K: 'a, V: 'a> ExactSizeIterator for Iter<'a, K, V> {
-    fn len(&self) -> usize { self.length }
+    fn len(&self) -> usize {
+        self.length
+    }
 }
 
 impl<'a, K, V> Clone for Iter<'a, K, V> {
     fn clone(&self) -> Iter<'a, K, V> {
         Iter {
             range: self.range.clone(),
-            length: self.length
+            length: self.length,
         }
     }
 }
@@ -946,7 +1170,9 @@ impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterMut<'a, K, V> {
 }
 
 impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterMut<'a, K, V> {
-    fn len(&self) -> usize { self.length }
+    fn len(&self) -> usize {
+        self.length
+    }
 }
 
 impl<K, V> IntoIterator for BTreeMap<K, V> {
@@ -962,14 +1188,15 @@ impl<K, V> IntoIterator for BTreeMap<K, V> {
         IntoIter {
             front: first_leaf_edge(root1),
             back: last_leaf_edge(root2),
-            length: len
+            length: len,
         }
     }
 }
 
 impl<K, V> Drop for IntoIter<K, V> {
     fn drop(&mut self) {
-        for _ in &mut *self { }
+        for _ in &mut *self {
+        }
         unsafe {
             let leaf_node = ptr::read(&self.front).into_node();
             if let Some(first_parent) = leaf_node.deallocate_and_ascend() {
@@ -1000,10 +1227,10 @@ impl<K, V> Iterator for IntoIter<K, V> {
                 let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
                 self.front = kv.right_edge();
                 return Some((k, v));
-            },
+            }
             Err(last_edge) => unsafe {
                 unwrap_unchecked(last_edge.into_node().deallocate_and_ascend())
-            }
+            },
         };
 
         loop {
@@ -1013,10 +1240,10 @@ impl<K, V> Iterator for IntoIter<K, V> {
                     let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
                     self.front = first_leaf_edge(kv.right_edge().descend());
                     return Some((k, v));
-                },
+                }
                 Err(last_edge) => unsafe {
                     cur_handle = unwrap_unchecked(last_edge.into_node().deallocate_and_ascend());
-                }
+                },
             }
         }
     }
@@ -1042,10 +1269,10 @@ impl<K, V> DoubleEndedIterator for IntoIter<K, V> {
                 let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
                 self.back = kv.left_edge();
                 return Some((k, v));
-            },
+            }
             Err(last_edge) => unsafe {
                 unwrap_unchecked(last_edge.into_node().deallocate_and_ascend())
-            }
+            },
         };
 
         loop {
@@ -1055,17 +1282,19 @@ impl<K, V> DoubleEndedIterator for IntoIter<K, V> {
                     let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
                     self.back = last_leaf_edge(kv.left_edge().descend());
                     return Some((k, v));
-                },
+                }
                 Err(last_edge) => unsafe {
                     cur_handle = unwrap_unchecked(last_edge.into_node().deallocate_and_ascend());
-                }
+                },
             }
         }
     }
 }
 
 impl<K, V> ExactSizeIterator for IntoIter<K, V> {
-    fn len(&self) -> usize { self.length }
+    fn len(&self) -> usize {
+        self.length
+    }
 }
 
 impl<'a, K, V> Iterator for Keys<'a, K, V> {
@@ -1094,9 +1323,7 @@ impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> {
 
 impl<'a, K, V> Clone for Keys<'a, K, V> {
     fn clone(&self) -> Keys<'a, K, V> {
-        Keys {
-            inner: self.inner.clone()
-        }
+        Keys { inner: self.inner.clone() }
     }
 }
 
@@ -1126,9 +1353,7 @@ impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> {
 
 impl<'a, K, V> Clone for Values<'a, K, V> {
     fn clone(&self) -> Values<'a, K, V> {
-        Values {
-            inner: self.inner.clone()
-        }
+        Values { inner: self.inner.clone() }
     }
 }
 
@@ -1180,7 +1405,7 @@ impl<'a, K, V> Range<'a, K, V> {
                 let ret = kv.into_kv();
                 self.front = kv.right_edge();
                 return ret;
-            },
+            }
             Err(last_edge) => {
                 let next_level = last_edge.into_node().ascend().ok();
                 unwrap_unchecked(next_level)
@@ -1193,7 +1418,7 @@ impl<'a, K, V> Range<'a, K, V> {
                     let ret = kv.into_kv();
                     self.front = first_leaf_edge(kv.right_edge().descend());
                     return ret;
-                },
+                }
                 Err(last_edge) => {
                     let next_level = last_edge.into_node().ascend().ok();
                     cur_handle = unwrap_unchecked(next_level);
@@ -1222,7 +1447,7 @@ impl<'a, K, V> Range<'a, K, V> {
                 let ret = kv.into_kv();
                 self.back = kv.left_edge();
                 return ret;
-            },
+            }
             Err(last_edge) => {
                 let next_level = last_edge.into_node().ascend().ok();
                 unwrap_unchecked(next_level)
@@ -1235,7 +1460,7 @@ impl<'a, K, V> Range<'a, K, V> {
                     let ret = kv.into_kv();
                     self.back = last_leaf_edge(kv.left_edge().descend());
                     return ret;
-                },
+                }
                 Err(last_edge) => {
                     let next_level = last_edge.into_node().ascend().ok();
                     cur_handle = unwrap_unchecked(next_level);
@@ -1249,7 +1474,7 @@ impl<'a, K, V> Clone for Range<'a, K, V> {
     fn clone(&self) -> Range<'a, K, V> {
         Range {
             front: self.front,
-            back: self.back
+            back: self.back,
         }
     }
 }
@@ -1261,7 +1486,7 @@ impl<'a, K, V> Iterator for RangeMut<'a, K, V> {
         if self.front == self.back {
             None
         } else {
-            unsafe { Some (self.next_unchecked()) }
+            unsafe { Some(self.next_unchecked()) }
         }
     }
 }
@@ -1275,7 +1500,7 @@ impl<'a, K, V> RangeMut<'a, K, V> {
                 let (k, v) = ptr::read(&kv).into_kv_mut();
                 self.front = kv.right_edge();
                 return (k, v);
-            },
+            }
             Err(last_edge) => {
                 let next_level = last_edge.into_node().ascend().ok();
                 unwrap_unchecked(next_level)
@@ -1288,7 +1513,7 @@ impl<'a, K, V> RangeMut<'a, K, V> {
                     let (k, v) = ptr::read(&kv).into_kv_mut();
                     self.front = first_leaf_edge(kv.right_edge().descend());
                     return (k, v);
-                },
+                }
                 Err(last_edge) => {
                     let next_level = last_edge.into_node().ascend().ok();
                     cur_handle = unwrap_unchecked(next_level);
@@ -1317,7 +1542,7 @@ impl<'a, K, V> RangeMut<'a, K, V> {
                 let (k, v) = ptr::read(&kv).into_kv_mut();
                 self.back = kv.left_edge();
                 return (k, v);
-            },
+            }
             Err(last_edge) => {
                 let next_level = last_edge.into_node().ascend().ok();
                 unwrap_unchecked(next_level)
@@ -1330,7 +1555,7 @@ impl<'a, K, V> RangeMut<'a, K, V> {
                     let (k, v) = ptr::read(&kv).into_kv_mut();
                     self.back = last_leaf_edge(kv.left_edge().descend());
                     return (k, v);
-                },
+                }
                 Err(last_edge) => {
                     let next_level = last_edge.into_node().ascend().ok();
                     cur_handle = unwrap_unchecked(next_level);
@@ -1341,7 +1566,7 @@ impl<'a, K, V> RangeMut<'a, K, V> {
 }
 
 impl<K: Ord, V> FromIterator<(K, V)> for BTreeMap<K, V> {
-    fn from_iter<T: IntoIterator<Item=(K, V)>>(iter: T) -> BTreeMap<K, V> {
+    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> BTreeMap<K, V> {
         let mut map = BTreeMap::new();
         map.extend(iter);
         map
@@ -1350,7 +1575,7 @@ impl<K: Ord, V> FromIterator<(K, V)> for BTreeMap<K, V> {
 
 impl<K: Ord, V> Extend<(K, V)> for BTreeMap<K, V> {
     #[inline]
-    fn extend<T: IntoIterator<Item=(K, V)>>(&mut self, iter: T) {
+    fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
         for (k, v) in iter {
             self.insert(k, v);
         }
@@ -1358,7 +1583,7 @@ impl<K: Ord, V> Extend<(K, V)> for BTreeMap<K, V> {
 }
 
 impl<'a, K: Ord + Copy, V: Copy> Extend<(&'a K, &'a V)> for BTreeMap<K, V> {
-    fn extend<I: IntoIterator<Item=(&'a K, &'a V)>>(&mut self, iter: I) {
+    fn extend<I: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: I) {
         self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));
     }
 }
@@ -1379,8 +1604,7 @@ impl<K: Ord, V> Default for BTreeMap<K, V> {
 
 impl<K: PartialEq, V: PartialEq> PartialEq for BTreeMap<K, V> {
     fn eq(&self, other: &BTreeMap<K, V>) -> bool {
-        self.len() == other.len() &&
-            self.iter().zip(other).all(|(a, b)| a == b)
+        self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b)
     }
 }
 
@@ -1407,7 +1631,8 @@ impl<K: Debug, V: Debug> Debug for BTreeMap<K, V> {
 }
 
 impl<'a, K: Ord, Q: ?Sized, V> Index<&'a Q> for BTreeMap<K, V>
-    where K: Borrow<Q>, Q: Ord
+    where K: Borrow<Q>,
+          Q: Ord
 {
     type Output = V;
 
@@ -1417,11 +1642,9 @@ impl<'a, K: Ord, Q: ?Sized, V> Index<&'a Q> for BTreeMap<K, V>
     }
 }
 
-fn first_leaf_edge<BorrowType, K, V>(
-        mut node: NodeRef<BorrowType,
-                          K, V,
-                          marker::LeafOrInternal>
-        ) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> {
+fn first_leaf_edge<BorrowType, K, V>
+    (mut node: NodeRef<BorrowType, K, V, marker::LeafOrInternal>)
+     -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> {
     loop {
         match node.force() {
             Leaf(leaf) => return leaf.first_edge(),
@@ -1432,11 +1655,9 @@ fn first_leaf_edge<BorrowType, K, V>(
     }
 }
 
-fn last_leaf_edge<BorrowType, K, V>(
-        mut node: NodeRef<BorrowType,
-                          K, V,
-                          marker::LeafOrInternal>
-        ) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> {
+fn last_leaf_edge<BorrowType, K, V>
+    (mut node: NodeRef<BorrowType, K, V, marker::LeafOrInternal>)
+     -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> {
     loop {
         match node.force() {
             Leaf(leaf) => return leaf.last_edge(),
@@ -1485,9 +1706,9 @@ impl<K, V> BTreeMap<K, V> {
         Iter {
             range: Range {
                 front: first_leaf_edge(self.root.as_ref()),
-                back: last_leaf_edge(self.root.as_ref())
+                back: last_leaf_edge(self.root.as_ref()),
             },
-            length: self.length
+            length: self.length,
         }
     }
 
@@ -1522,7 +1743,7 @@ impl<K, V> BTreeMap<K, V> {
                 back: last_leaf_edge(root2),
                 _marker: PhantomData,
             },
-            length: self.length
+            length: self.length,
         }
     }
 
@@ -1672,6 +1893,12 @@ impl<'a, K: Ord, V> VacantEntry<'a, K, V> {
         &self.key
     }
 
+    /// Take ownership of the key.
+    #[unstable(feature = "map_entry_recover_keys", issue = "34285")]
+    pub fn into_key(self) -> K {
+        self.key
+    }
+
     /// Sets the value of the entry with the VacantEntry's key,
     /// and returns a mutable reference to it.
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -1697,15 +1924,17 @@ impl<'a, K: Ord, V> VacantEntry<'a, K, V> {
 
         loop {
             match cur_parent {
-                Ok(parent) => match parent.insert(ins_k, ins_v, ins_edge) {
-                    Fit(_) => return unsafe { &mut *out_ptr },
-                    Split(left, k, v, right) => {
-                        ins_k = k;
-                        ins_v = v;
-                        ins_edge = right;
-                        cur_parent = left.ascend().map_err(|n| n.into_root_mut());
+                Ok(parent) => {
+                    match parent.insert(ins_k, ins_v, ins_edge) {
+                        Fit(_) => return unsafe { &mut *out_ptr },
+                        Split(left, k, v, right) => {
+                            ins_k = k;
+                            ins_v = v;
+                            ins_edge = right;
+                            cur_parent = left.ascend().map_err(|n| n.into_root_mut());
+                        }
                     }
-                },
+                }
                 Err(root) => {
                     root.push_level().push(ins_k, ins_v, ins_edge);
                     return unsafe { &mut *out_ptr };
@@ -1722,6 +1951,12 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
         self.handle.reborrow().into_kv().0
     }
 
+    /// Take ownership of the key and value from the map.
+    #[unstable(feature = "map_entry_recover_keys", issue = "34285")]
+    pub fn remove_pair(self) -> (K, V) {
+        self.remove_kv()
+    }
+
     /// Gets a reference to the value in the entry.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn get(&self) -> &V {
@@ -1760,7 +1995,7 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
             Leaf(leaf) => {
                 let (hole, old_key, old_val) = leaf.remove();
                 (hole.into_node(), old_key, old_val)
-            },
+            }
             Internal(mut internal) => {
                 let key_loc = internal.kv_mut().0 as *mut K;
                 let val_loc = internal.kv_mut().1 as *mut V;
@@ -1770,12 +2005,8 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
 
                 let (hole, key, val) = to_remove.remove();
 
-                let old_key = unsafe {
-                    mem::replace(&mut *key_loc, key)
-                };
-                let old_val = unsafe {
-                    mem::replace(&mut *val_loc, val)
-                };
+                let old_key = unsafe { mem::replace(&mut *key_loc, key) };
+                let old_val = unsafe { mem::replace(&mut *val_loc, val) };
 
                 (hole.into_node(), old_key, old_val)
             }
@@ -1787,14 +2018,16 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
             match handle_underfull_node(cur_node) {
                 AtRoot => break,
                 EmptyParent(_) => unreachable!(),
-                Merged(parent) => if parent.len() == 0 {
-                    // We must be at the root
-                    parent.into_root_mut().pop_level();
-                    break;
-                } else {
-                    cur_node = parent.forget_type();
-                },
-                Stole(_) => break
+                Merged(parent) => {
+                    if parent.len() == 0 {
+                        // We must be at the root
+                        parent.into_root_mut().pop_level();
+                        break;
+                    } else {
+                        cur_node = parent.forget_type();
+                    }
+                }
+                Stole(_) => break,
             }
         }
 
@@ -1806,13 +2039,11 @@ enum UnderflowResult<'a, K, V> {
     AtRoot,
     EmptyParent(NodeRef<marker::Mut<'a>, K, V, marker::Internal>),
     Merged(NodeRef<marker::Mut<'a>, K, V, marker::Internal>),
-    Stole(NodeRef<marker::Mut<'a>, K, V, marker::Internal>)
+    Stole(NodeRef<marker::Mut<'a>, K, V, marker::Internal>),
 }
 
-fn handle_underfull_node<'a, K, V>(node: NodeRef<marker::Mut<'a>,
-                                                 K, V,
-                                                 marker::LeafOrInternal>)
-                                                 -> UnderflowResult<'a, K, V> {
+fn handle_underfull_node<'a, K, V>(node: NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>)
+                                   -> UnderflowResult<'a, K, V> {
     let parent = if let Ok(parent) = node.ascend() {
         parent
     } else {
@@ -1821,10 +2052,12 @@ fn handle_underfull_node<'a, K, V>(node: NodeRef<marker::Mut<'a>,
 
     let (is_left, mut handle) = match parent.left_kv() {
         Ok(left) => (true, left),
-        Err(parent) => match parent.right_kv() {
-            Ok(right) => (false, right),
-            Err(parent) => {
-                return EmptyParent(parent.into_node());
+        Err(parent) => {
+            match parent.right_kv() {
+                Ok(right) => (false, right),
+                Err(parent) => {
+                    return EmptyParent(parent.into_node());
+                }
             }
         }
     };
@@ -1841,7 +2074,7 @@ fn handle_underfull_node<'a, K, V>(node: NodeRef<marker::Mut<'a>,
     }
 }
 
-impl<K: Ord, V, I: Iterator<Item=(K, V)>> Iterator for MergeIter<K, V, I> {
+impl<K: Ord, V, I: Iterator<Item = (K, V)>> Iterator for MergeIter<K, V, I> {
     type Item = (K, V);
 
     fn next(&mut self) -> Option<(K, V)> {
@@ -1855,16 +2088,12 @@ impl<K: Ord, V, I: Iterator<Item=(K, V)>> Iterator for MergeIter<K, V, I> {
         // Check which elements comes first and only advance the corresponding iterator.
         // If two keys are equal, take the value from `right`.
         match res {
-            Ordering::Less => {
-                self.left.next()
-            },
-            Ordering::Greater => {
-                self.right.next()
-            },
+            Ordering::Less => self.left.next(),
+            Ordering::Greater => self.right.next(),
             Ordering::Equal => {
                 self.left.next();
                 self.right.next()
-            },
+            }
         }
     }
 }
index ca1cf6bcc50280c81fe56a4445a6df2f13f72968..e9bc29118d508aa6b4bf36e926c16bfd4cae40f3 100644 (file)
@@ -51,6 +51,7 @@ use core::slice;
 use boxed::Box;
 
 const B: usize = 6;
+pub const MIN_LEN: usize = B - 1;
 pub const CAPACITY: usize = 2 * B - 1;
 
 /// The underlying representation of leaf nodes. Note that it is often unsafe to actually store
@@ -413,6 +414,19 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
         let len = self.len();
         Handle::new_edge(self, len)
     }
+
+    /// Note that `self` must be nonempty.
+    pub fn first_kv(self) -> Handle<Self, marker::KV> {
+        debug_assert!(self.len() > 0);
+        Handle::new_kv(self, 0)
+    }
+
+    /// Note that `self` must be nonempty.
+    pub fn last_kv(self) -> Handle<Self, marker::KV> {
+        let len = self.len();
+        debug_assert!(len > 0);
+        Handle::new_kv(self, len - 1)
+    }
 }
 
 impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
@@ -602,6 +616,17 @@ impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
         }
     }
 
+    fn correct_childrens_parent_links(&mut self, first: usize, after_last: usize) {
+        for i in first..after_last {
+            Handle::new_edge(unsafe { self.reborrow_mut() }, i).correct_parent_link();
+        }
+    }
+
+    fn correct_all_childrens_parent_links(&mut self) {
+        let len = self.len();
+        self.correct_childrens_parent_links(0, len + 1);
+    }
+
     /// Adds a key/value pair and an edge to go to the left of that pair to
     /// the beginning of the node.
     pub fn push_front(&mut self, key: K, val: V, edge: Root<K, V>) {
@@ -623,11 +648,8 @@ impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
 
             self.as_leaf_mut().len += 1;
 
-            for i in 0..self.len()+1 {
-                Handle::new_edge(self.reborrow_mut(), i).correct_parent_link();
-            }
+            self.correct_all_childrens_parent_links();
         }
-
     }
 }
 
@@ -696,6 +718,13 @@ impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> {
             (key, val, edge)
         }
     }
+
+    fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) {
+        (
+            self.keys_mut().as_mut_ptr(),
+            self.vals_mut().as_mut_ptr()
+        )
+    }
 }
 
 impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
@@ -1275,105 +1304,155 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::
     }
 
     /// This does stealing similar to `steal_left` but steals multiple elements at once.
-    pub fn bulk_steal_left(&mut self, n: usize) {
+    pub fn bulk_steal_left(&mut self, count: usize) {
         unsafe {
-            // Get raw pointers to left child's keys, values and edges.
-            let (left_len, left_k, left_v, left_e) = {
-                let mut left = self.reborrow_mut().left_edge().descend();
-
-                (left.len(),
-                 left.keys_mut().as_mut_ptr(),
-                 left.vals_mut().as_mut_ptr(),
-                 match left.force() {
-                     ForceResult::Leaf(_) => None,
-                     ForceResult::Internal(mut i) => Some(i.as_internal_mut().edges.as_mut_ptr()),
-                 })
-            };
-
-            // Get raw pointers to right child's keys, values and edges.
-            let (right_len, right_k, right_v, right_e) = {
-                let mut right = self.reborrow_mut().right_edge().descend();
-
-                (right.len(),
-                 right.keys_mut().as_mut_ptr(),
-                 right.vals_mut().as_mut_ptr(),
-                 match right.force() {
-                     ForceResult::Leaf(_) => None,
-                     ForceResult::Internal(mut i) => Some(i.as_internal_mut().edges.as_mut_ptr()),
-                 })
-            };
-
-            // Get raw pointers to parent's key and value.
-            let (parent_k, parent_v) = {
-                let kv = self.reborrow_mut().into_kv_mut();
-                (kv.0 as *mut K, kv.1 as *mut V)
-            };
+            let mut left_node = ptr::read(self).left_edge().descend();
+            let left_len = left_node.len();
+            let mut right_node = ptr::read(self).right_edge().descend();
+            let right_len = right_node.len();
 
             // Make sure that we may steal safely.
-            debug_assert!(right_len + n <= CAPACITY);
-            debug_assert!(left_len >= n);
-
-            // Make room for stolen elements in right child.
-            ptr::copy(right_k,
-                      right_k.offset(n as isize),
-                      right_len);
-            ptr::copy(right_v,
-                      right_v.offset(n as isize),
-                      right_len);
-            if let Some(edges) = right_e {
-                ptr::copy(edges,
-                          edges.offset(n as isize),
-                          right_len+1);
+            debug_assert!(right_len + count <= CAPACITY);
+            debug_assert!(left_len >= count);
+
+            let new_left_len = left_len - count;
+
+            // Move data.
+            {
+                let left_kv = left_node.reborrow_mut().into_kv_pointers_mut();
+                let right_kv = right_node.reborrow_mut().into_kv_pointers_mut();
+                let parent_kv = {
+                    let kv = self.reborrow_mut().into_kv_mut();
+                    (kv.0 as *mut K, kv.1 as *mut V)
+                };
+
+                // Make room for stolen elements in the right child.
+                ptr::copy(right_kv.0,
+                          right_kv.0.offset(count as isize),
+                          right_len);
+                ptr::copy(right_kv.1,
+                          right_kv.1.offset(count as isize),
+                          right_len);
+
+                // Move elements from the left child to the right one.
+                move_kv(left_kv, new_left_len + 1, right_kv, 0, count - 1);
+
+                // Move parent's key/value pair to the right child.
+                move_kv(parent_kv, 0, right_kv, count - 1, 1);
+
+                // Move the left-most stolen pair to the parent.
+                move_kv(left_kv, new_left_len, parent_kv, 0, 1);
             }
 
-            // Move elements from the left child to the right one.
-            let left_ind = (left_len - n) as isize;
-            ptr::copy_nonoverlapping(left_k.offset(left_ind + 1),
-                                     right_k,
-                                     n - 1);
-            ptr::copy_nonoverlapping(left_v.offset(left_ind + 1),
-                                     right_v,
-                                     n - 1);
-            match (left_e, right_e) {
-                (Some(left), Some(right)) => {
-                    ptr::copy_nonoverlapping(left.offset(left_ind + 1),
-                                             right,
-                                             n);
+            left_node.reborrow_mut().as_leaf_mut().len -= count as u16;
+            right_node.reborrow_mut().as_leaf_mut().len += count as u16;
+
+            match (left_node.force(), right_node.force()) {
+                (ForceResult::Internal(left), ForceResult::Internal(mut right)) => {
+                    // Make room for stolen edges.
+                    let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr();
+                    ptr::copy(right_edges,
+                              right_edges.offset(count as isize),
+                              right_len + 1);
+                    right.correct_childrens_parent_links(count, count + right_len + 1);
+
+                    move_edges(left, new_left_len + 1, right, 0, count);
                 },
-                (Some(_), None) => unreachable!(),
-                (None, Some(_)) => unreachable!(),
-                (None, None) => {},
+                (ForceResult::Leaf(_), ForceResult::Leaf(_)) => { }
+                _ => { unreachable!(); }
             }
+        }
+    }
 
-            // Copy parent key/value pair to right child.
-            ptr::copy_nonoverlapping(parent_k,
-                                     right_k.offset(n as isize - 1),
-                                     1);
-            ptr::copy_nonoverlapping(parent_v,
-                                     right_v.offset(n as isize - 1),
-                                     1);
-            // Copy left-most stolen pair to parent.
-            ptr::copy_nonoverlapping(left_k.offset(left_ind),
-                                     parent_k,
-                                     1);
-            ptr::copy_nonoverlapping(left_v.offset(left_ind),
-                                     parent_v,
-                                     1);
-
-            // Fix lengths of left and right child and parent pointers in children of the right
-            // child.
-            self.reborrow_mut().left_edge().descend().as_leaf_mut().len -= n as u16;
-            let mut right = self.reborrow_mut().right_edge().descend();
-            right.as_leaf_mut().len += n as u16;
-            if let ForceResult::Internal(mut node) = right.force() {
-                for i in 0..(right_len+n+1) {
-                    Handle::new_edge(node.reborrow_mut(), i as usize).correct_parent_link();
-                }
+    /// The symmetric clone of `bulk_steal_left`.
+    pub fn bulk_steal_right(&mut self, count: usize) {
+        unsafe {
+            let mut left_node = ptr::read(self).left_edge().descend();
+            let left_len = left_node.len();
+            let mut right_node = ptr::read(self).right_edge().descend();
+            let right_len = right_node.len();
+
+            // Make sure that we may steal safely.
+            debug_assert!(left_len + count <= CAPACITY);
+            debug_assert!(right_len >= count);
+
+            let new_right_len = right_len - count;
+
+            // Move data.
+            {
+                let left_kv = left_node.reborrow_mut().into_kv_pointers_mut();
+                let right_kv = right_node.reborrow_mut().into_kv_pointers_mut();
+                let parent_kv = {
+                    let kv = self.reborrow_mut().into_kv_mut();
+                    (kv.0 as *mut K, kv.1 as *mut V)
+                };
+
+                // Move parent's key/value pair to the left child.
+                move_kv(parent_kv, 0, left_kv, left_len, 1);
+
+                // Move elements from the right child to the left one.
+                move_kv(right_kv, 0, left_kv, left_len + 1, count - 1);
+
+                // Move the right-most stolen pair to the parent.
+                move_kv(right_kv, count - 1, parent_kv, 0, 1);
+
+                // Fix right indexing
+                ptr::copy(right_kv.0.offset(count as isize),
+                          right_kv.0,
+                          new_right_len);
+                ptr::copy(right_kv.1.offset(count as isize),
+                          right_kv.1,
+                          new_right_len);
+            }
+
+            left_node.reborrow_mut().as_leaf_mut().len += count as u16;
+            right_node.reborrow_mut().as_leaf_mut().len -= count as u16;
+
+            match (left_node.force(), right_node.force()) {
+                (ForceResult::Internal(left), ForceResult::Internal(mut right)) => {
+                    move_edges(right.reborrow_mut(), 0, left, left_len + 1, count);
+
+                    // Fix right indexing.
+                    let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr();
+                    ptr::copy(right_edges.offset(count as isize),
+                              right_edges,
+                              new_right_len + 1);
+                    right.correct_childrens_parent_links(0, new_right_len + 1);
+                },
+                (ForceResult::Leaf(_), ForceResult::Leaf(_)) => { }
+                _ => { unreachable!(); }
             }
         }
     }
 }
 
+unsafe fn move_kv<K, V>(
+    source: (*mut K, *mut V), source_offset: usize,
+    dest: (*mut K, *mut V), dest_offset: usize,
+    count: usize)
+{
+    ptr::copy_nonoverlapping(source.0.offset(source_offset as isize),
+                             dest.0.offset(dest_offset as isize),
+                             count);
+    ptr::copy_nonoverlapping(source.1.offset(source_offset as isize),
+                             dest.1.offset(dest_offset as isize),
+                             count);
+}
+
+// Source and destination must have the same height.
+unsafe fn move_edges<K, V>(
+    mut source: NodeRef<marker::Mut, K, V, marker::Internal>, source_offset: usize,
+    mut dest: NodeRef<marker::Mut, K, V, marker::Internal>, dest_offset: usize,
+    count: usize)
+{
+    let source_ptr = source.as_internal_mut().edges.as_mut_ptr();
+    let dest_ptr = dest.as_internal_mut().edges.as_mut_ptr();
+    ptr::copy_nonoverlapping(source_ptr.offset(source_offset as isize),
+                             dest_ptr.offset(dest_offset as isize),
+                             count);
+    dest.correct_childrens_parent_links(dest_offset, dest_offset + count);
+}
+
 impl<BorrowType, K, V, HandleType>
         Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, HandleType> {
 
@@ -1397,6 +1476,41 @@ impl<BorrowType, K, V, HandleType>
     }
 }
 
+impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::Edge> {
+    /// Move the suffix after `self` from one node to another one. `right` must be empty.
+    /// The first edge of `right` remains unchanged.
+    pub fn move_suffix(&mut self,
+            right: &mut NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>) {
+        unsafe {
+            let left_new_len = self.idx;
+            let mut left_node = self.reborrow_mut().into_node();
+
+            let right_new_len = left_node.len() - left_new_len;
+            let mut right_node = right.reborrow_mut();
+
+            debug_assert!(right_node.len() == 0);
+            debug_assert!(left_node.height == right_node.height);
+
+            let left_kv = left_node.reborrow_mut().into_kv_pointers_mut();
+            let right_kv = right_node.reborrow_mut().into_kv_pointers_mut();
+
+
+            move_kv(left_kv, left_new_len, right_kv, 0, right_new_len);
+
+            left_node.reborrow_mut().as_leaf_mut().len = left_new_len as u16;
+            right_node.reborrow_mut().as_leaf_mut().len = right_new_len as u16;
+
+            match (left_node.force(), right_node.force()) {
+                (ForceResult::Internal(left), ForceResult::Internal(right)) => {
+                    move_edges(left, left_new_len + 1, right, 1, right_new_len);
+                },
+                (ForceResult::Leaf(_), ForceResult::Leaf(_)) => { }
+                _ => { unreachable!(); }
+            }
+        }
+    }
+}
+
 pub enum ForceResult<Leaf, Internal> {
     Leaf(Leaf),
     Internal(Internal)
index 3ee42499a38f868c5d4b4ebf00896fbd68a83f37..0f885bc2950a6b3426778e7508445b286c76e47e 100644 (file)
@@ -551,7 +551,6 @@ impl<T: Ord> BTreeSet<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(btree_append)]
     /// use std::collections::BTreeSet;
     ///
     /// let mut a = BTreeSet::new();
@@ -575,11 +574,44 @@ impl<T: Ord> BTreeSet<T> {
     /// assert!(a.contains(&4));
     /// assert!(a.contains(&5));
     /// ```
-    #[unstable(feature = "btree_append", reason = "recently added as part of collections reform 2",
-               issue = "19986")]
+    #[stable(feature = "btree_append", since = "1.11.0")]
     pub fn append(&mut self, other: &mut Self) {
         self.map.append(&mut other.map);
     }
+
+    /// Splits the collection into two at the given key. Returns everything after the given key,
+    /// including the key.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut a = BTreeMap::new();
+    /// a.insert(1, "a");
+    /// a.insert(2, "b");
+    /// a.insert(3, "c");
+    /// a.insert(17, "d");
+    /// a.insert(41, "e");
+    ///
+    /// let b = a.split_off(&3);
+    ///
+    /// assert_eq!(a.len(), 2);
+    /// assert_eq!(b.len(), 3);
+    ///
+    /// assert_eq!(a[&1], "a");
+    /// assert_eq!(a[&2], "b");
+    ///
+    /// assert_eq!(b[&3], "c");
+    /// assert_eq!(b[&17], "d");
+    /// assert_eq!(b[&41], "e");
+    /// ```
+    #[stable(feature = "btree_split_off", since = "1.11.0")]
+    pub fn split_off<Q: ?Sized + Ord>(&mut self, key: &Q) -> Self where T: Borrow<Q> {
+        BTreeSet { map: self.map.split_off(key) }
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
index 0ebb89b8a22a3d77af63268d361fa714450e9510..15de0dd802d99f0a067c6df18d086e1cdb3465b8 100644 (file)
@@ -8,19 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! Utilities for formatting and printing strings
+//! Utilities for formatting and printing `String`s
 //!
 //! This module contains the runtime support for the `format!` syntax extension.
 //! This macro is implemented in the compiler to emit calls to this module in
-//! order to format arguments at runtime into strings and streams.
+//! order to format arguments at runtime into strings.
 //!
 //! # Usage
 //!
 //! The `format!` macro is intended to be familiar to those coming from C's
-//! printf/fprintf functions or Python's `str.format` function. In its current
-//! revision, the `format!` macro returns a `String` type which is the result of
-//! the formatting. In the future it will also be able to pass in a stream to
-//! format arguments directly while performing minimal allocations.
+//! printf/fprintf functions or Python's `str.format` function.
 //!
 //! Some examples of the `format!` extension are:
 //!
@@ -31,6 +28,7 @@
 //! format!("{:?}", (3, 4));          // => "(3, 4)"
 //! format!("{value}", value=4);      // => "4"
 //! format!("{} {}", 1, 2);           // => "1 2"
+//! format!("{:04}", 42);             // => "0042" with leading zeros
 //! ```
 //!
 //! From these, you can see that the first argument is a format string. It is
index 6ab66fc217b46247f57dc548f35b394e63b4f312..f027d074cb6f0bc8175db50364576b4311b35122 100644 (file)
@@ -38,7 +38,6 @@
 #![feature(fmt_internals)]
 #![feature(heap_api)]
 #![feature(inclusive_range)]
-#![feature(iter_arith)]
 #![feature(lang_items)]
 #![feature(nonzero)]
 #![feature(pattern)]
@@ -49,7 +48,6 @@
 #![feature(specialization)]
 #![feature(staged_api)]
 #![feature(step_by)]
-#![feature(str_char)]
 #![feature(unboxed_closures)]
 #![feature(unicode)]
 #![feature(unique)]
@@ -105,12 +103,14 @@ pub mod vec_deque;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub mod btree_map {
+    //! A map based on a B-Tree.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use btree::map::*;
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub mod btree_set {
+    //! A set based on a B-Tree.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use btree::set::*;
 }
index 2059943bfdf6140ca9ecb615338a21a2b5773ded..55308a46f0ac55c7c8645540df0cd9344d74ef67 100644 (file)
@@ -37,7 +37,7 @@ use boxed::Box;
 pub use core::str::{FromStr, Utf8Error};
 #[allow(deprecated)]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use core::str::{Lines, LinesAny, CharRange};
+pub use core::str::{Lines, LinesAny};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::str::{Split, RSplit};
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -112,11 +112,6 @@ impl<S: Borrow<str>> SliceConcatExt<str> for [S] {
     }
 }
 
-/// Deprecated, renamed to EncodeUtf16
-#[unstable(feature = "str_utf16", issue = "27714")]
-#[rustc_deprecated(since = "1.8.0", reason = "renamed to EncodeUtf16")]
-pub type Utf16Units<'a> = EncodeUtf16<'a>;
-
 /// External iterator for a string's UTF-16 code units.
 ///
 /// For use with the `std::iter` module.
@@ -352,230 +347,6 @@ impl str {
         core_str::StrExt::slice_mut_unchecked(self, begin, end)
     }
 
-    /// Given a byte position, returns the next `char` and its index.
-    ///
-    /// # Panics
-    ///
-    /// If `i` is greater than or equal to the length of the string.
-    /// If `i` is not the index of the beginning of a valid UTF-8 sequence.
-    ///
-    /// # Examples
-    ///
-    /// This example manually iterates through the code points of a string;
-    /// this should normally be
-    /// done by `.chars()` or `.char_indices()`.
-    ///
-    /// ```
-    /// #![feature(str_char)]
-    /// #![allow(deprecated)]
-    ///
-    /// use std::str::CharRange;
-    ///
-    /// let s = "中华Việt Nam";
-    /// let mut i = 0;
-    /// while i < s.len() {
-    ///     let CharRange {ch, next} = s.char_range_at(i);
-    ///     println!("{}: {}", i, ch);
-    ///     i = next;
-    /// }
-    /// ```
-    ///
-    /// This outputs:
-    ///
-    /// ```text
-    /// 0: 中
-    /// 3: 华
-    /// 6: V
-    /// 7: i
-    /// 8: e
-    /// 9:
-    /// 11:
-    /// 13: t
-    /// 14:
-    /// 15: N
-    /// 16: a
-    /// 17: m
-    /// ```
-    #[unstable(feature = "str_char",
-               reason = "often replaced by char_indices, this method may \
-                         be removed in favor of just char_at() or eventually \
-                         removed altogether",
-               issue = "27754")]
-    #[inline]
-    #[rustc_deprecated(reason = "use slicing plus chars() plus len_utf8",
-                       since = "1.9.0")]
-    #[allow(deprecated)]
-    pub fn char_range_at(&self, start: usize) -> CharRange {
-        core_str::StrExt::char_range_at(self, start)
-    }
-
-    /// Given a byte position, returns the previous `char` and its position.
-    ///
-    /// Note that Unicode has many features, such as combining marks, ligatures,
-    /// and direction marks, that need to be taken into account to correctly reverse a string.
-    ///
-    /// Returns 0 for next index if called on start index 0.
-    ///
-    /// # Panics
-    ///
-    /// If `i` is greater than the length of the string.
-    /// If `i` is not an index following a valid UTF-8 sequence.
-    ///
-    /// # Examples
-    ///
-    /// This example manually iterates through the code points of a string;
-    /// this should normally be
-    /// done by `.chars().rev()` or `.char_indices()`.
-    ///
-    /// ```
-    /// #![feature(str_char)]
-    /// #![allow(deprecated)]
-    ///
-    /// use std::str::CharRange;
-    ///
-    /// let s = "中华Việt Nam";
-    /// let mut i = s.len();
-    /// while i > 0 {
-    ///     let CharRange {ch, next} = s.char_range_at_reverse(i);
-    ///     println!("{}: {}", i, ch);
-    ///     i = next;
-    /// }
-    /// ```
-    ///
-    /// This outputs:
-    ///
-    /// ```text
-    /// 18: m
-    /// 17: a
-    /// 16: N
-    /// 15:
-    /// 14: t
-    /// 13:
-    /// 11:
-    /// 9: e
-    /// 8: i
-    /// 7: V
-    /// 6: 华
-    /// 3: 中
-    /// ```
-    #[unstable(feature = "str_char",
-               reason = "often replaced by char_indices, this method may \
-                         be removed in favor of just char_at_reverse() or \
-                         eventually removed altogether",
-               issue = "27754")]
-    #[inline]
-    #[rustc_deprecated(reason = "use slicing plus chars().rev() plus len_utf8",
-                       since = "1.9.0")]
-    #[allow(deprecated)]
-    pub fn char_range_at_reverse(&self, start: usize) -> CharRange {
-        core_str::StrExt::char_range_at_reverse(self, start)
-    }
-
-    /// Given a byte position, returns the `char` at that position.
-    ///
-    /// # Panics
-    ///
-    /// If `i` is greater than or equal to the length of the string.
-    /// If `i` is not the index of the beginning of a valid UTF-8 sequence.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(str_char)]
-    /// #![allow(deprecated)]
-    ///
-    /// let s = "abπc";
-    /// assert_eq!(s.char_at(1), 'b');
-    /// assert_eq!(s.char_at(2), 'π');
-    /// assert_eq!(s.char_at(4), 'c');
-    /// ```
-    #[unstable(feature = "str_char",
-               reason = "frequently replaced by the chars() iterator, this \
-                         method may be removed or possibly renamed in the \
-                         future; it is normally replaced by chars/char_indices \
-                         iterators or by getting the first char from a \
-                         subslice",
-               issue = "27754")]
-    #[inline]
-    #[allow(deprecated)]
-    #[rustc_deprecated(reason = "use slicing plus chars()",
-                       since = "1.9.0")]
-    pub fn char_at(&self, i: usize) -> char {
-        core_str::StrExt::char_at(self, i)
-    }
-
-    /// Given a byte position, returns the `char` at that position, counting
-    /// from the end.
-    ///
-    /// # Panics
-    ///
-    /// If `i` is greater than the length of the string.
-    /// If `i` is not an index following a valid UTF-8 sequence.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(str_char)]
-    /// #![allow(deprecated)]
-    ///
-    /// let s = "abπc";
-    /// assert_eq!(s.char_at_reverse(1), 'a');
-    /// assert_eq!(s.char_at_reverse(2), 'b');
-    /// assert_eq!(s.char_at_reverse(3), 'π');
-    /// ```
-    #[unstable(feature = "str_char",
-               reason = "see char_at for more details, but reverse semantics \
-                         are also somewhat unclear, especially with which \
-                         cases generate panics",
-               issue = "27754")]
-    #[inline]
-    #[rustc_deprecated(reason = "use slicing plus chars().rev()",
-                       since = "1.9.0")]
-    #[allow(deprecated)]
-    pub fn char_at_reverse(&self, i: usize) -> char {
-        core_str::StrExt::char_at_reverse(self, i)
-    }
-
-    /// Retrieves the first `char` from a `&str` and returns it.
-    ///
-    /// Note that a single Unicode character (grapheme cluster)
-    /// can be composed of multiple `char`s.
-    ///
-    /// This does not allocate a new string; instead, it returns a slice that
-    /// points one code point beyond the code point that was shifted.
-    ///
-    /// `None` is returned if the slice is empty.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(str_char)]
-    /// #![allow(deprecated)]
-    ///
-    /// let s = "Łódź"; // \u{141}o\u{301}dz\u{301}
-    /// let (c, s1) = s.slice_shift_char().unwrap();
-    ///
-    /// assert_eq!(c, 'Ł');
-    /// assert_eq!(s1, "ódź");
-    ///
-    /// let (c, s2) = s1.slice_shift_char().unwrap();
-    ///
-    /// assert_eq!(c, 'o');
-    /// assert_eq!(s2, "\u{301}dz\u{301}");
-    /// ```
-    #[unstable(feature = "str_char",
-               reason = "awaiting conventions about shifting and slices and \
-                         may not be warranted with the existence of the chars \
-                         and/or char_indices iterators",
-               issue = "27754")]
-    #[inline]
-    #[rustc_deprecated(reason = "use chars() plus Chars::as_str",
-                       since = "1.9.0")]
-    #[allow(deprecated)]
-    pub fn slice_shift_char(&self) -> Option<(char, &str)> {
-        core_str::StrExt::slice_shift_char(self)
-    }
-
     /// Divide one string slice into two at an index.
     ///
     /// The argument, `mid`, should be a byte offset from the start of the
@@ -867,16 +638,6 @@ impl str {
         core_str::StrExt::lines_any(self)
     }
 
-    /// Returns an iterator of `u16` over the string encoded as UTF-16.
-    #[unstable(feature = "str_utf16",
-               reason = "this functionality may only be provided by libunicode",
-               issue = "27714")]
-    #[rustc_deprecated(since = "1.8.0", reason = "renamed to encode_utf16")]
-    #[allow(deprecated)]
-    pub fn utf16_units(&self) -> Utf16Units {
-        Utf16Units { encoder: Utf16Encoder::new(self[..].chars()) }
-    }
-
     /// Returns an iterator of `u16` over the string encoded as UTF-16.
     #[stable(feature = "encode_utf16", since = "1.8.0")]
     pub fn encode_utf16(&self) -> EncodeUtf16 {
@@ -1097,8 +858,34 @@ impl str {
     /// assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]);
     /// ```
     ///
-    /// This can lead to possibly surprising behavior when whitespace is used
-    /// as the separator. This code is correct:
+    /// Contiguous separators are separated by the empty string.
+    ///
+    /// ```
+    /// let x = "(///)".to_string();
+    /// let d: Vec<_> = x.split('/').collect();;
+    ///
+    /// assert_eq!(d, &["(", "", "", ")"]);
+    /// ```
+    ///
+    /// Separators at the start or end of a string are neighbored
+    /// by empty strings.
+    ///
+    /// ```
+    /// let d: Vec<_> = "010".split("0").collect();
+    /// assert_eq!(d, &["", "1", ""]);
+    /// ```
+    ///
+    /// When the empty string is used as a separator, it separates
+    /// every character in the string, along with the beginning
+    /// and end of the string.
+    ///
+    /// ```
+    /// let f: Vec<_> = "rust".split("").collect();
+    /// assert_eq!(f, &["", "r", "u", "s", "t", ""]);
+    /// ```
+    ///
+    /// Contiguous separators can lead to possibly surprising behavior
+    /// when whitespace is used as the separator. This code is correct:
     ///
     /// ```
     /// let x = "    a  b c".to_string();
@@ -1617,8 +1404,8 @@ impl str {
     /// Returns a string slice with all prefixes and suffixes that match a
     /// pattern repeatedly removed.
     ///
-    /// The pattern can be a `&str`, [`char`], or a closure that determines
-    /// if a character matches.
+    /// The pattern can be a [`char`] or a closure that determines if a
+    /// character matches.
     ///
     /// [`char`]: primitive.char.html
     ///
index 58d4a4ed4eb084c04e0f47279d2b96c891280a47..9797113b8ad66450ac1934404e9acad9c112456a 100644 (file)
@@ -966,7 +966,7 @@ impl<T: Clone> Vec<T> {
         }
     }
 
-    /// Appends all elements in a slice to the `Vec`.
+    /// Clones and appends all elements in a slice to the `Vec`.
     ///
     /// Iterates over the slice `other`, clones each element, and then appends
     /// it to this `Vec`. The `other` vector is traversed in-order.
@@ -1658,7 +1658,7 @@ impl<T> Iterator for IntoIter<T> {
 
     #[inline]
     fn count(self) -> usize {
-        self.size_hint().0
+        self.len()
     }
 }
 
index 58194fe75f79aa3895e9d7e2f71a3be34060ff61..be933abe41fe201f8d1c8be9efd9e88d1b708de0 100644 (file)
@@ -81,6 +81,18 @@ fn test_peek_and_pop() {
     }
 }
 
+#[test]
+fn test_peek_mut() {
+    let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
+    let mut heap = BinaryHeap::from(data);
+    assert_eq!(heap.peek(), Some(&10));
+    {
+        let mut top = heap.peek_mut().unwrap();
+        *top -= 2;
+    }
+    assert_eq!(heap.peek(), Some(&9));
+}
+
 #[test]
 fn test_push() {
     let mut heap = BinaryHeap::from(vec![2, 4, 9]);
@@ -192,6 +204,12 @@ fn test_empty_peek() {
     assert!(empty.peek().is_none());
 }
 
+#[test]
+fn test_empty_peek_mut() {
+    let mut empty = BinaryHeap::<i32>::new();
+    assert!(empty.peek_mut().is_none());
+}
+
 #[test]
 fn test_empty_replace() {
     let mut heap = BinaryHeap::new();
index 1858791776f0e305b6caffc7b6231dbfd1d37107..49fce68d15e542138a720a59b05d19c880951dd7 100644 (file)
@@ -9,10 +9,13 @@
 // except according to those terms.
 
 use std::collections::BTreeMap;
-use std::collections::Bound::{Excluded, Included, Unbounded, self};
+use std::collections::Bound::{self, Excluded, Included, Unbounded};
 use std::collections::btree_map::Entry::{Occupied, Vacant};
 use std::rc::Rc;
 
+use std::iter::FromIterator;
+use super::DeterministicRng;
+
 #[test]
 fn test_basic_large() {
     let mut map = BTreeMap::new();
@@ -20,41 +23,41 @@ fn test_basic_large() {
     assert_eq!(map.len(), 0);
 
     for i in 0..size {
-        assert_eq!(map.insert(i, 10*i), None);
+        assert_eq!(map.insert(i, 10 * i), None);
         assert_eq!(map.len(), i + 1);
     }
 
     for i in 0..size {
-        assert_eq!(map.get(&i).unwrap(), &(i*10));
+        assert_eq!(map.get(&i).unwrap(), &(i * 10));
     }
 
-    for i in size..size*2 {
+    for i in size..size * 2 {
         assert_eq!(map.get(&i), None);
     }
 
     for i in 0..size {
-        assert_eq!(map.insert(i, 100*i), Some(10*i));
+        assert_eq!(map.insert(i, 100 * i), Some(10 * i));
         assert_eq!(map.len(), size);
     }
 
     for i in 0..size {
-        assert_eq!(map.get(&i).unwrap(), &(i*100));
+        assert_eq!(map.get(&i).unwrap(), &(i * 100));
     }
 
-    for i in 0..size/2 {
-        assert_eq!(map.remove(&(i*2)), Some(i*200));
+    for i in 0..size / 2 {
+        assert_eq!(map.remove(&(i * 2)), Some(i * 200));
         assert_eq!(map.len(), size - i - 1);
     }
 
-    for i in 0..size/2 {
-        assert_eq!(map.get(&(2*i)), None);
-        assert_eq!(map.get(&(2*i+1)).unwrap(), &(i*200 + 100));
+    for i in 0..size / 2 {
+        assert_eq!(map.get(&(2 * i)), None);
+        assert_eq!(map.get(&(2 * i + 1)).unwrap(), &(i * 200 + 100));
     }
 
-    for i in 0..size/2 {
-        assert_eq!(map.remove(&(2*i)), None);
-        assert_eq!(map.remove(&(2*i+1)), Some(i*200 + 100));
-        assert_eq!(map.len(), size/2 - i - 1);
+    for i in 0..size / 2 {
+        assert_eq!(map.remove(&(2 * i)), None);
+        assert_eq!(map.remove(&(2 * i + 1)), Some(i * 200 + 100));
+        assert_eq!(map.len(), size / 2 - i - 1);
     }
 }
 
@@ -81,7 +84,9 @@ fn test_iter() {
     // Forwards
     let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
 
-    fn test<T>(size: usize, mut iter: T) where T: Iterator<Item=(usize, usize)> {
+    fn test<T>(size: usize, mut iter: T)
+        where T: Iterator<Item = (usize, usize)>
+    {
         for i in 0..size {
             assert_eq!(iter.size_hint(), (size - i, Some(size - i)));
             assert_eq!(iter.next().unwrap(), (i, i));
@@ -101,7 +106,9 @@ fn test_iter_rev() {
     // Forwards
     let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
 
-    fn test<T>(size: usize, mut iter: T) where T: Iterator<Item=(usize, usize)> {
+    fn test<T>(size: usize, mut iter: T)
+        where T: Iterator<Item = (usize, usize)>
+    {
         for i in 0..size {
             assert_eq!(iter.size_hint(), (size - i, Some(size - i)));
             assert_eq!(iter.next().unwrap(), (size - i - 1, size - i - 1));
@@ -125,8 +132,7 @@ fn test_values_mut() {
     }
 
     let values: Vec<String> = a.values().cloned().collect();
-    assert_eq!(values, [String::from("hello!"),
-                        String::from("goodbye!")]);
+    assert_eq!(values, [String::from("hello!"), String::from("goodbye!")]);
 }
 
 #[test]
@@ -137,7 +143,8 @@ fn test_iter_mixed() {
     let mut map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
 
     fn test<T>(size: usize, mut iter: T)
-            where T: Iterator<Item=(usize, usize)> + DoubleEndedIterator {
+        where T: Iterator<Item = (usize, usize)> + DoubleEndedIterator
+    {
         for i in 0..size / 4 {
             assert_eq!(iter.size_hint(), (size - i * 2, Some(size - i * 2)));
             assert_eq!(iter.next().unwrap(), (i, i));
@@ -202,7 +209,7 @@ fn test_range() {
     for i in 0..size {
         for j in i..size {
             let mut kvs = map.range(Included(&i), Included(&j)).map(|(&k, &v)| (k, v));
-            let mut pairs = (i..j+1).map(|i| (i, i));
+            let mut pairs = (i..j + 1).map(|i| (i, i));
 
             for (kv, pair) in kvs.by_ref().zip(pairs.by_ref()) {
                 assert_eq!(kv, pair);
@@ -242,7 +249,7 @@ fn test_borrow() {
 }
 
 #[test]
-fn test_entry(){
+fn test_entry() {
     let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
 
     let mut map: BTreeMap<_, _> = xs.iter().cloned().collect();
@@ -341,17 +348,23 @@ fn test_bad_zst() {
     struct Bad;
 
     impl PartialEq for Bad {
-        fn eq(&self, _: &Self) -> bool { false }
+        fn eq(&self, _: &Self) -> bool {
+            false
+        }
     }
 
     impl Eq for Bad {}
 
     impl PartialOrd for Bad {
-        fn partial_cmp(&self, _: &Self) -> Option<Ordering> { Some(Ordering::Less) }
+        fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
+            Some(Ordering::Less)
+        }
     }
 
     impl Ord for Bad {
-        fn cmp(&self, _: &Self) -> Ordering { Ordering::Less }
+        fn cmp(&self, _: &Self) -> Ordering {
+            Ordering::Less
+        }
     }
 
     let mut m = BTreeMap::new();
@@ -368,27 +381,27 @@ fn test_clone() {
     assert_eq!(map.len(), 0);
 
     for i in 0..size {
-        assert_eq!(map.insert(i, 10*i), None);
+        assert_eq!(map.insert(i, 10 * i), None);
         assert_eq!(map.len(), i + 1);
         assert_eq!(map, map.clone());
     }
 
     for i in 0..size {
-        assert_eq!(map.insert(i, 100*i), Some(10*i));
+        assert_eq!(map.insert(i, 100 * i), Some(10 * i));
         assert_eq!(map.len(), size);
         assert_eq!(map, map.clone());
     }
 
-    for i in 0..size/2 {
-        assert_eq!(map.remove(&(i*2)), Some(i*200));
+    for i in 0..size / 2 {
+        assert_eq!(map.remove(&(i * 2)), Some(i * 200));
         assert_eq!(map.len(), size - i - 1);
         assert_eq!(map, map.clone());
     }
 
-    for i in 0..size/2 {
-        assert_eq!(map.remove(&(2*i)), None);
-        assert_eq!(map.remove(&(2*i+1)), Some(i*200 + 100));
-        assert_eq!(map.len(), size/2 - i - 1);
+    for i in 0..size / 2 {
+        assert_eq!(map.remove(&(2 * i)), None);
+        assert_eq!(map.remove(&(2 * i + 1)), Some(i * 200 + 100));
+        assert_eq!(map.len(), size / 2 - i - 1);
         assert_eq!(map, map.clone());
     }
 }
@@ -398,16 +411,36 @@ fn test_clone() {
 fn test_variance() {
     use std::collections::btree_map::{Iter, IntoIter, Range, Keys, Values};
 
-    fn map_key<'new>(v: BTreeMap<&'static str, ()>) -> BTreeMap<&'new str, ()> { v }
-    fn map_val<'new>(v: BTreeMap<(), &'static str>) -> BTreeMap<(), &'new str> { v }
-    fn iter_key<'a, 'new>(v: Iter<'a, &'static str, ()>) -> Iter<'a, &'new str, ()> { v }
-    fn iter_val<'a, 'new>(v: Iter<'a, (), &'static str>) -> Iter<'a, (), &'new str> { v }
-    fn into_iter_key<'new>(v: IntoIter<&'static str, ()>) -> IntoIter<&'new str, ()> { v }
-    fn into_iter_val<'new>(v: IntoIter<(), &'static str>) -> IntoIter<(), &'new str> { v }
-    fn range_key<'a, 'new>(v: Range<'a, &'static str, ()>) -> Range<'a, &'new str, ()> { v }
-    fn range_val<'a, 'new>(v: Range<'a, (), &'static str>) -> Range<'a, (), &'new str> { v }
-    fn keys<'a, 'new>(v: Keys<'a, &'static str, ()>) -> Keys<'a, &'new str, ()> { v }
-    fn vals<'a, 'new>(v: Values<'a, (), &'static str>) -> Values<'a, (), &'new str> { v }
+    fn map_key<'new>(v: BTreeMap<&'static str, ()>) -> BTreeMap<&'new str, ()> {
+        v
+    }
+    fn map_val<'new>(v: BTreeMap<(), &'static str>) -> BTreeMap<(), &'new str> {
+        v
+    }
+    fn iter_key<'a, 'new>(v: Iter<'a, &'static str, ()>) -> Iter<'a, &'new str, ()> {
+        v
+    }
+    fn iter_val<'a, 'new>(v: Iter<'a, (), &'static str>) -> Iter<'a, (), &'new str> {
+        v
+    }
+    fn into_iter_key<'new>(v: IntoIter<&'static str, ()>) -> IntoIter<&'new str, ()> {
+        v
+    }
+    fn into_iter_val<'new>(v: IntoIter<(), &'static str>) -> IntoIter<(), &'new str> {
+        v
+    }
+    fn range_key<'a, 'new>(v: Range<'a, &'static str, ()>) -> Range<'a, &'new str, ()> {
+        v
+    }
+    fn range_val<'a, 'new>(v: Range<'a, (), &'static str>) -> Range<'a, (), &'new str> {
+        v
+    }
+    fn keys<'a, 'new>(v: Keys<'a, &'static str, ()>) -> Keys<'a, &'new str, ()> {
+        v
+    }
+    fn vals<'a, 'new>(v: Values<'a, (), &'static str>) -> Values<'a, (), &'new str> {
+        v
+    }
 }
 
 #[test]
@@ -440,7 +473,7 @@ fn test_vacant_entry_key() {
         Vacant(e) => {
             assert_eq!(key, *e.key());
             e.insert(value.clone());
-        },
+        }
     }
     assert_eq!(a.len(), 1);
     assert_eq!(a[key], value);
@@ -498,6 +531,51 @@ create_append_test!(test_append_181, 181);
 create_append_test!(test_append_239, 239);
 create_append_test!(test_append_1700, 1700);
 
+fn rand_data(len: usize) -> Vec<(u32, u32)> {
+    let mut rng = DeterministicRng::new();
+    Vec::from_iter(
+        (0..len).map(|_| (rng.next(), rng.next()))
+    )
+}
+
+#[test]
+fn test_split_off_empty_right() {
+    let mut data = rand_data(173);
+
+    let mut map = BTreeMap::from_iter(data.clone());
+    let right = map.split_off(&(data.iter().max().unwrap().0 + 1));
+
+    data.sort();
+    assert!(map.into_iter().eq(data));
+    assert!(right.into_iter().eq(None));
+}
+
+#[test]
+fn test_split_off_empty_left() {
+    let mut data = rand_data(314);
+
+    let mut map = BTreeMap::from_iter(data.clone());
+    let right = map.split_off(&data.iter().min().unwrap().0);
+
+    data.sort();
+    assert!(map.into_iter().eq(None));
+    assert!(right.into_iter().eq(data));
+}
+
+#[test]
+fn test_split_off_large_random_sorted() {
+    let mut data = rand_data(1529);
+    // special case with maximum height.
+    data.sort();
+
+    let mut map = BTreeMap::from_iter(data.clone());
+    let key = data[data.len() / 2].0;
+    let right = map.split_off(&key);
+
+    assert!(map.into_iter().eq(data.clone().into_iter().filter(|x| x.0 < key)));
+    assert!(right.into_iter().eq(data.into_iter().filter(|x| x.0 >= key)));
+}
+
 mod bench {
     use std::collections::BTreeMap;
     use std::__rand::{Rng, thread_rng};
index 0db48f3ce9edb4bac58c1b4f2596a48f5eca20da..ea43f423b7c1f0bf7e60570c110cd317444202fe 100644 (file)
 
 mod map;
 mod set;
+
+/// XorShiftRng
+struct DeterministicRng {
+    x: u32,
+    y: u32,
+    z: u32,
+    w: u32,
+}
+
+impl DeterministicRng {
+    fn new() -> Self {
+        DeterministicRng {
+            x: 0x193a6754,
+            y: 0xa8a7d469,
+            z: 0x97830e05,
+            w: 0x113ba7bb
+        }
+    }
+
+    fn next(&mut self) -> u32 {
+        let x = self.x;
+        let t = x ^ (x << 11);
+        self.x = self.y;
+        self.y = self.z;
+        self.z = self.w;
+        let w_ = self.w;
+        self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8));
+        self.w
+    }
+}
index 53ccfd5b4e26dc0c4dbd3325457b46828cf03616..f7b647d7772db4a57d91ec37d1cfc300703f23e6 100644 (file)
@@ -10,6 +10,9 @@
 
 use std::collections::BTreeSet;
 
+use std::iter::FromIterator;
+use super::DeterministicRng;
+
 #[test]
 fn test_clone_eq() {
   let mut m = BTreeSet::new();
@@ -289,3 +292,48 @@ fn test_append() {
     assert_eq!(a.contains(&4), true);
     assert_eq!(a.contains(&5), true);
 }
+
+fn rand_data(len: usize) -> Vec<u32> {
+    let mut rng = DeterministicRng::new();
+    Vec::from_iter(
+        (0..len).map(|_| rng.next())
+    )
+}
+
+#[test]
+fn test_split_off_empty_right() {
+    let mut data = rand_data(173);
+
+    let mut set = BTreeSet::from_iter(data.clone());
+    let right = set.split_off(&(data.iter().max().unwrap() + 1));
+
+    data.sort();
+    assert!(set.into_iter().eq(data));
+    assert!(right.into_iter().eq(None));
+}
+
+#[test]
+fn test_split_off_empty_left() {
+    let mut data = rand_data(314);
+
+    let mut set = BTreeSet::from_iter(data.clone());
+    let right = set.split_off(data.iter().min().unwrap());
+
+    data.sort();
+    assert!(set.into_iter().eq(None));
+    assert!(right.into_iter().eq(data));
+}
+
+#[test]
+fn test_split_off_large_random_sorted() {
+    let mut data = rand_data(1529);
+    // special case with maximum height.
+    data.sort();
+
+    let mut set = BTreeSet::from_iter(data.clone());
+    let key = data[data.len() / 2];
+    let right = set.split_off(&key);
+
+    assert!(set.into_iter().eq(data.clone().into_iter().filter(|x| *x < key)));
+    assert!(right.into_iter().eq(data.into_iter().filter(|x| *x >= key)));
+}
index b073c2f3ae4dd307d99ef5e79a1b3e74c8044b32..972361326d7bb33af99e99d0a34b49ff8a4ccc70 100644 (file)
@@ -17,7 +17,9 @@ use self::Foo::*;
 #[derive(Copy, Clone, PartialEq, Debug)]
 #[repr(usize)]
 enum Foo {
-    A, B, C
+    A,
+    B,
+    C,
 }
 
 impl CLike for Foo {
@@ -157,15 +159,15 @@ fn test_iterator() {
 
     e1.insert(C);
     let elems: Vec<_> = e1.iter().collect();
-    assert_eq!(elems, [A,C]);
+    assert_eq!(elems, [A, C]);
 
     e1.insert(C);
     let elems: Vec<_> = e1.iter().collect();
-    assert_eq!(elems, [A,C]);
+    assert_eq!(elems, [A, C]);
 
     e1.insert(B);
     let elems: Vec<_> = e1.iter().collect();
-    assert_eq!(elems, [A,B,C]);
+    assert_eq!(elems, [A, B, C]);
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -183,7 +185,7 @@ fn test_operators() {
 
     let e_union = e1 | e2;
     let elems: Vec<_> = e_union.iter().collect();
-    assert_eq!(elems, [A,B,C]);
+    assert_eq!(elems, [A, B, C]);
 
     let e_intersection = e1 & e2;
     let elems: Vec<_> = e_intersection.iter().collect();
@@ -201,17 +203,17 @@ fn test_operators() {
     // Bitwise XOR of two sets, aka symmetric difference
     let e_symmetric_diff = e1 ^ e2;
     let elems: Vec<_> = e_symmetric_diff.iter().collect();
-    assert_eq!(elems, [A,B]);
+    assert_eq!(elems, [A, B]);
 
     // Another way to express symmetric difference
     let e_symmetric_diff = (e1 - e2) | (e2 - e1);
     let elems: Vec<_> = e_symmetric_diff.iter().collect();
-    assert_eq!(elems, [A,B]);
+    assert_eq!(elems, [A, B]);
 
     // Yet another way to express symmetric difference
     let e_symmetric_diff = (e1 | e2) - (e1 & e2);
     let elems: Vec<_> = e_symmetric_diff.iter().collect();
-    assert_eq!(elems, [A,B]);
+    assert_eq!(elems, [A, B]);
 }
 
 #[test]
index bae21f1bd9b8fe41454d2bee5ce5758ede1a0ecc..8ae63808f27404b9abdc4406b6eab7ef40b251f6 100644 (file)
 #![deny(warnings)]
 
 #![feature(binary_heap_extras)]
-#![feature(binary_heap_append)]
+#![feature(binary_heap_peek_mut)]
 #![feature(box_syntax)]
-#![feature(btree_append)]
 #![feature(btree_range)]
 #![feature(collections)]
 #![feature(collections_bound)]
 #![feature(const_fn)]
 #![feature(fn_traits)]
 #![feature(enumset)]
-#![feature(iter_arith)]
 #![feature(linked_list_contains)]
 #![feature(pattern)]
 #![feature(rand)]
 #![feature(step_by)]
-#![feature(str_char)]
 #![feature(str_escape)]
 #![feature(test)]
 #![feature(unboxed_closures)]
index 7265d53be4837d2979c6579a9141875502a01e7f..956d75a95a58e9b77230ce2804a5ebbaeab9e72c 100644 (file)
@@ -54,7 +54,7 @@ fn test_basic() {
 
 #[cfg(test)]
 fn generate_test() -> LinkedList<i32> {
-    list_from(&[0,1,2,3,4,5,6])
+    list_from(&[0, 1, 2, 3, 4, 5, 6])
 }
 
 #[cfg(test)]
@@ -78,7 +78,7 @@ fn test_split_off() {
 
     // not singleton, forwards
     {
-        let u = vec![1,2,3,4,5];
+        let u = vec![1, 2, 3, 4, 5];
         let mut m = list_from(&u);
         let mut n = m.split_off(2);
         assert_eq!(m.len(), 2);
@@ -92,7 +92,7 @@ fn test_split_off() {
     }
     // not singleton, backwards
     {
-        let u = vec![1,2,3,4,5];
+        let u = vec![1, 2, 3, 4, 5];
         let mut m = list_from(&u);
         let mut n = m.split_off(4);
         assert_eq!(m.len(), 4);
@@ -246,33 +246,33 @@ fn test_eq() {
     m.push_back(1);
     assert!(n == m);
 
-    let n = list_from(&[2,3,4]);
-    let m = list_from(&[1,2,3]);
+    let n = list_from(&[2, 3, 4]);
+    let m = list_from(&[1, 2, 3]);
     assert!(n != m);
 }
 
 #[test]
 fn test_hash() {
-  let mut x = LinkedList::new();
-  let mut y = LinkedList::new();
+    let mut x = LinkedList::new();
+    let mut y = LinkedList::new();
 
-  assert!(::hash(&x) == ::hash(&y));
+    assert!(::hash(&x) == ::hash(&y));
 
-  x.push_back(1);
-  x.push_back(2);
-  x.push_back(3);
+    x.push_back(1);
+    x.push_back(2);
+    x.push_back(3);
 
-  y.push_front(3);
-  y.push_front(2);
-  y.push_front(1);
+    y.push_front(3);
+    y.push_front(2);
+    y.push_front(1);
 
-  assert!(::hash(&x) == ::hash(&y));
+    assert!(::hash(&x) == ::hash(&y));
 }
 
 #[test]
 fn test_ord() {
     let n = list_from(&[]);
-    let m = list_from(&[1,2,3]);
+    let m = list_from(&[1, 2, 3]);
     assert!(n < m);
     assert!(m > n);
     assert!(n <= n);
@@ -281,7 +281,7 @@ fn test_ord() {
 
 #[test]
 fn test_ord_nan() {
-    let nan = 0.0f64/0.0;
+    let nan = 0.0f64 / 0.0;
     let n = list_from(&[nan]);
     let m = list_from(&[nan]);
     assert!(!(n < m));
@@ -296,15 +296,15 @@ fn test_ord_nan() {
     assert!(!(n <= one));
     assert!(!(n >= one));
 
-    let u = list_from(&[1.0f64,2.0,nan]);
-    let v = list_from(&[1.0f64,2.0,3.0]);
+    let u = list_from(&[1.0f64, 2.0, nan]);
+    let v = list_from(&[1.0f64, 2.0, 3.0]);
     assert!(!(u < v));
     assert!(!(u > v));
     assert!(!(u <= v));
     assert!(!(u >= v));
 
-    let s = list_from(&[1.0f64,2.0,4.0,2.0]);
-    let t = list_from(&[1.0f64,2.0,3.0,2.0]);
+    let s = list_from(&[1.0f64, 2.0, 4.0, 2.0]);
+    let t = list_from(&[1.0f64, 2.0, 3.0, 2.0]);
     assert!(!(s < t));
     assert!(s > one);
     assert!(!(s <= one));
@@ -317,7 +317,8 @@ fn test_show() {
     assert_eq!(format!("{:?}", list), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
 
     let list: LinkedList<_> = vec!["just", "one", "test", "more"].iter().cloned().collect();
-    assert_eq!(format!("{:?}", list), "[\"just\", \"one\", \"test\", \"more\"]");
+    assert_eq!(format!("{:?}", list),
+               "[\"just\", \"one\", \"test\", \"more\"]");
 }
 
 #[test]
index 236c151891d11880d0d2da424ac29bc8247c7531..71416f2069fa0976d5042d11d9e67f0f110261be 100644 (file)
@@ -13,9 +13,13 @@ use std::mem;
 use std::__rand::{Rng, thread_rng};
 use std::rc::Rc;
 
-fn square(n: usize) -> usize { n * n }
+fn square(n: usize) -> usize {
+    n * n
+}
 
-fn is_odd(n: &usize) -> bool { *n % 2 == 1 }
+fn is_odd(n: &usize) -> bool {
+    *n % 2 == 1
+}
 
 #[test]
 fn test_from_fn() {
@@ -76,9 +80,9 @@ fn test_is_empty() {
 #[test]
 fn test_len_divzero() {
     type Z = [i8; 0];
-    let v0 : &[Z] = &[];
-    let v1 : &[Z] = &[[]];
-    let v2 : &[Z] = &[[], []];
+    let v0: &[Z] = &[];
+    let v1: &[Z] = &[[]];
+    let v2: &[Z] = &[[], []];
     assert_eq!(mem::size_of::<Z>(), 0);
     assert_eq!(v0.len(), 0);
     assert_eq!(v1.len(), 1);
@@ -295,7 +299,7 @@ fn test_push() {
 
 #[test]
 fn test_truncate() {
-    let mut v: Vec<Box<_>> = vec![box 6,box 5,box 4];
+    let mut v: Vec<Box<_>> = vec![box 6, box 5, box 4];
     v.truncate(1);
     let v = v;
     assert_eq!(v.len(), 1);
@@ -305,7 +309,7 @@ fn test_truncate() {
 
 #[test]
 fn test_clear() {
-    let mut v: Vec<Box<_>> = vec![box 6,box 5,box 4];
+    let mut v: Vec<Box<_>> = vec![box 6, box 5, box 4];
     v.clear();
     assert_eq!(v.len(), 0);
     // If the unsafe block didn't drop things properly, we blow up here.
@@ -320,12 +324,12 @@ fn test_dedup() {
     }
     case(vec![], vec![]);
     case(vec![1], vec![1]);
-    case(vec![1,1], vec![1]);
-    case(vec![1,2,3], vec![1,2,3]);
-    case(vec![1,1,2,3], vec![1,2,3]);
-    case(vec![1,2,2,3], vec![1,2,3]);
-    case(vec![1,2,3,3], vec![1,2,3]);
-    case(vec![1,1,2,2,2,3,3], vec![1,2,3]);
+    case(vec![1, 1], vec![1]);
+    case(vec![1, 2, 3], vec![1, 2, 3]);
+    case(vec![1, 1, 2, 3], vec![1, 2, 3]);
+    case(vec![1, 2, 2, 3], vec![1, 2, 3]);
+    case(vec![1, 2, 3, 3], vec![1, 2, 3]);
+    case(vec![1, 1, 2, 2, 2, 3, 3], vec![1, 2, 3]);
 }
 
 #[test]
@@ -336,10 +340,8 @@ fn test_dedup_unique() {
     v1.dedup();
     let mut v2: Vec<Box<_>> = vec![box 1, box 2, box 3, box 3];
     v2.dedup();
-    /*
-     * If the boxed pointers were leaked or otherwise misused, valgrind
-     * and/or rt should raise errors.
-     */
+    // If the boxed pointers were leaked or otherwise misused, valgrind
+    // and/or rt should raise errors.
 }
 
 #[test]
@@ -350,10 +352,8 @@ fn test_dedup_shared() {
     v1.dedup();
     let mut v2: Vec<Box<_>> = vec![box 1, box 2, box 3, box 3];
     v2.dedup();
-    /*
-     * If the pointers were leaked or otherwise misused, valgrind and/or
-     * rt should raise errors.
-     */
+    // If the pointers were leaked or otherwise misused, valgrind and/or
+    // rt should raise errors.
 }
 
 #[test]
@@ -365,31 +365,31 @@ fn test_retain() {
 
 #[test]
 fn test_binary_search() {
-    assert_eq!([1,2,3,4,5].binary_search(&5).ok(), Some(4));
-    assert_eq!([1,2,3,4,5].binary_search(&4).ok(), Some(3));
-    assert_eq!([1,2,3,4,5].binary_search(&3).ok(), Some(2));
-    assert_eq!([1,2,3,4,5].binary_search(&2).ok(), Some(1));
-    assert_eq!([1,2,3,4,5].binary_search(&1).ok(), Some(0));
-
-    assert_eq!([2,4,6,8,10].binary_search(&1).ok(), None);
-    assert_eq!([2,4,6,8,10].binary_search(&5).ok(), None);
-    assert_eq!([2,4,6,8,10].binary_search(&4).ok(), Some(1));
-    assert_eq!([2,4,6,8,10].binary_search(&10).ok(), Some(4));
-
-    assert_eq!([2,4,6,8].binary_search(&1).ok(), None);
-    assert_eq!([2,4,6,8].binary_search(&5).ok(), None);
-    assert_eq!([2,4,6,8].binary_search(&4).ok(), Some(1));
-    assert_eq!([2,4,6,8].binary_search(&8).ok(), Some(3));
-
-    assert_eq!([2,4,6].binary_search(&1).ok(), None);
-    assert_eq!([2,4,6].binary_search(&5).ok(), None);
-    assert_eq!([2,4,6].binary_search(&4).ok(), Some(1));
-    assert_eq!([2,4,6].binary_search(&6).ok(), Some(2));
-
-    assert_eq!([2,4].binary_search(&1).ok(), None);
-    assert_eq!([2,4].binary_search(&5).ok(), None);
-    assert_eq!([2,4].binary_search(&2).ok(), Some(0));
-    assert_eq!([2,4].binary_search(&4).ok(), Some(1));
+    assert_eq!([1, 2, 3, 4, 5].binary_search(&5).ok(), Some(4));
+    assert_eq!([1, 2, 3, 4, 5].binary_search(&4).ok(), Some(3));
+    assert_eq!([1, 2, 3, 4, 5].binary_search(&3).ok(), Some(2));
+    assert_eq!([1, 2, 3, 4, 5].binary_search(&2).ok(), Some(1));
+    assert_eq!([1, 2, 3, 4, 5].binary_search(&1).ok(), Some(0));
+
+    assert_eq!([2, 4, 6, 8, 10].binary_search(&1).ok(), None);
+    assert_eq!([2, 4, 6, 8, 10].binary_search(&5).ok(), None);
+    assert_eq!([2, 4, 6, 8, 10].binary_search(&4).ok(), Some(1));
+    assert_eq!([2, 4, 6, 8, 10].binary_search(&10).ok(), Some(4));
+
+    assert_eq!([2, 4, 6, 8].binary_search(&1).ok(), None);
+    assert_eq!([2, 4, 6, 8].binary_search(&5).ok(), None);
+    assert_eq!([2, 4, 6, 8].binary_search(&4).ok(), Some(1));
+    assert_eq!([2, 4, 6, 8].binary_search(&8).ok(), Some(3));
+
+    assert_eq!([2, 4, 6].binary_search(&1).ok(), None);
+    assert_eq!([2, 4, 6].binary_search(&5).ok(), None);
+    assert_eq!([2, 4, 6].binary_search(&4).ok(), Some(1));
+    assert_eq!([2, 4, 6].binary_search(&6).ok(), Some(2));
+
+    assert_eq!([2, 4].binary_search(&1).ok(), None);
+    assert_eq!([2, 4].binary_search(&5).ok(), None);
+    assert_eq!([2, 4].binary_search(&2).ok(), Some(0));
+    assert_eq!([2, 4].binary_search(&4).ok(), Some(1));
 
     assert_eq!([2].binary_search(&1).ok(), None);
     assert_eq!([2].binary_search(&5).ok(), None);
@@ -398,14 +398,14 @@ fn test_binary_search() {
     assert_eq!([].binary_search(&1).ok(), None);
     assert_eq!([].binary_search(&5).ok(), None);
 
-    assert!([1,1,1,1,1].binary_search(&1).ok() != None);
-    assert!([1,1,1,1,2].binary_search(&1).ok() != None);
-    assert!([1,1,1,2,2].binary_search(&1).ok() != None);
-    assert!([1,1,2,2,2].binary_search(&1).ok() != None);
-    assert_eq!([1,2,2,2,2].binary_search(&1).ok(), Some(0));
+    assert!([1, 1, 1, 1, 1].binary_search(&1).ok() != None);
+    assert!([1, 1, 1, 1, 2].binary_search(&1).ok() != None);
+    assert!([1, 1, 1, 2, 2].binary_search(&1).ok() != None);
+    assert!([1, 1, 2, 2, 2].binary_search(&1).ok() != None);
+    assert_eq!([1, 2, 2, 2, 2].binary_search(&1).ok(), Some(0));
 
-    assert_eq!([1,2,3,4,5].binary_search(&6).ok(), None);
-    assert_eq!([1,2,3,4,5].binary_search(&0).ok(), None);
+    assert_eq!([1, 2, 3, 4, 5].binary_search(&6).ok(), None);
+    assert_eq!([1, 2, 3, 4, 5].binary_search(&0).ok(), None);
 }
 
 #[test]
@@ -460,15 +460,17 @@ fn test_sort_stability() {
             // the second item represents which occurrence of that
             // number this element is, i.e. the second elements
             // will occur in sorted order.
-            let mut v: Vec<_> = (0..len).map(|_| {
-                    let n = thread_rng().gen::<usize>() % 10;
-                    counts[n] += 1;
-                    (n, counts[n])
-                }).collect();
+            let mut v: Vec<_> = (0..len)
+                                    .map(|_| {
+                                        let n = thread_rng().gen::<usize>() % 10;
+                                        counts[n] += 1;
+                                        (n, counts[n])
+                                    })
+                                    .collect();
 
             // only sort on the first element, so an unstable sort
             // may mix up the counts.
-            v.sort_by(|&(a,_), &(b,_)| a.cmp(&b));
+            v.sort_by(|&(a, _), &(b, _)| a.cmp(&b));
 
             // this comparison includes the count (the second item
             // of the tuple), so elements with equal first items
@@ -679,7 +681,7 @@ fn test_rev_iterator() {
 #[test]
 fn test_mut_rev_iterator() {
     let mut xs = [1, 2, 3, 4, 5];
-    for (i,x) in xs.iter_mut().rev().enumerate() {
+    for (i, x) in xs.iter_mut().rev().enumerate() {
         *x += i;
     }
     assert!(xs == [5, 5, 5, 5, 5])
@@ -687,35 +689,32 @@ fn test_mut_rev_iterator() {
 
 #[test]
 fn test_move_iterator() {
-    let xs = vec![1,2,3,4,5];
-    assert_eq!(xs.into_iter().fold(0, |a: usize, b: usize| 10*a + b), 12345);
+    let xs = vec![1, 2, 3, 4, 5];
+    assert_eq!(xs.into_iter().fold(0, |a: usize, b: usize| 10 * a + b),
+               12345);
 }
 
 #[test]
 fn test_move_rev_iterator() {
-    let xs = vec![1,2,3,4,5];
-    assert_eq!(xs.into_iter().rev().fold(0, |a: usize, b: usize| 10*a + b), 54321);
+    let xs = vec![1, 2, 3, 4, 5];
+    assert_eq!(xs.into_iter().rev().fold(0, |a: usize, b: usize| 10 * a + b),
+               54321);
 }
 
 #[test]
 fn test_splitator() {
-    let xs = &[1,2,3,4,5];
+    let xs = &[1, 2, 3, 4, 5];
 
     let splits: &[&[_]] = &[&[1], &[3], &[5]];
-    assert_eq!(xs.split(|x| *x % 2 == 0).collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[], &[2,3,4,5]];
-    assert_eq!(xs.split(|x| *x == 1).collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[1,2,3,4], &[]];
-    assert_eq!(xs.split(|x| *x == 5).collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[1,2,3,4,5]];
-    assert_eq!(xs.split(|x| *x == 10).collect::<Vec<_>>(),
-               splits);
+    assert_eq!(xs.split(|x| *x % 2 == 0).collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[], &[2, 3, 4, 5]];
+    assert_eq!(xs.split(|x| *x == 1).collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[1, 2, 3, 4], &[]];
+    assert_eq!(xs.split(|x| *x == 5).collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]];
+    assert_eq!(xs.split(|x| *x == 10).collect::<Vec<_>>(), splits);
     let splits: &[&[_]] = &[&[], &[], &[], &[], &[], &[]];
-    assert_eq!(xs.split(|_| true).collect::<Vec<&[i32]>>(),
-               splits);
+    assert_eq!(xs.split(|_| true).collect::<Vec<&[i32]>>(), splits);
 
     let xs: &[i32] = &[];
     let splits: &[&[i32]] = &[&[]];
@@ -724,17 +723,14 @@ fn test_splitator() {
 
 #[test]
 fn test_splitnator() {
-    let xs = &[1,2,3,4,5];
+    let xs = &[1, 2, 3, 4, 5];
 
-    let splits: &[&[_]] = &[&[1,2,3,4,5]];
-    assert_eq!(xs.splitn(1, |x| *x % 2 == 0).collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[1], &[3,4,5]];
-    assert_eq!(xs.splitn(2, |x| *x % 2 == 0).collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[], &[], &[], &[4,5]];
-    assert_eq!(xs.splitn(4, |_| true).collect::<Vec<_>>(),
-               splits);
+    let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]];
+    assert_eq!(xs.splitn(1, |x| *x % 2 == 0).collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[1], &[3, 4, 5]];
+    assert_eq!(xs.splitn(2, |x| *x % 2 == 0).collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[], &[], &[], &[4, 5]];
+    assert_eq!(xs.splitn(4, |_| true).collect::<Vec<_>>(), splits);
 
     let xs: &[i32] = &[];
     let splits: &[&[i32]] = &[&[]];
@@ -743,40 +739,34 @@ fn test_splitnator() {
 
 #[test]
 fn test_splitnator_mut() {
-    let xs = &mut [1,2,3,4,5];
+    let xs = &mut [1, 2, 3, 4, 5];
 
-    let splits: &[&mut[_]] = &[&mut [1,2,3,4,5]];
+    let splits: &[&mut [_]] = &[&mut [1, 2, 3, 4, 5]];
     assert_eq!(xs.splitn_mut(1, |x| *x % 2 == 0).collect::<Vec<_>>(),
                splits);
-    let splits: &[&mut[_]] = &[&mut [1], &mut [3,4,5]];
+    let splits: &[&mut [_]] = &[&mut [1], &mut [3, 4, 5]];
     assert_eq!(xs.splitn_mut(2, |x| *x % 2 == 0).collect::<Vec<_>>(),
                splits);
-    let splits: &[&mut[_]] = &[&mut [], &mut [], &mut [], &mut [4,5]];
-    assert_eq!(xs.splitn_mut(4, |_| true).collect::<Vec<_>>(),
-               splits);
+    let splits: &[&mut [_]] = &[&mut [], &mut [], &mut [], &mut [4, 5]];
+    assert_eq!(xs.splitn_mut(4, |_| true).collect::<Vec<_>>(), splits);
 
     let xs: &mut [i32] = &mut [];
-    let splits: &[&mut[i32]] = &[&mut []];
-    assert_eq!(xs.splitn_mut(2, |x| *x == 5).collect::<Vec<_>>(),
-               splits);
+    let splits: &[&mut [i32]] = &[&mut []];
+    assert_eq!(xs.splitn_mut(2, |x| *x == 5).collect::<Vec<_>>(), splits);
 }
 
 #[test]
 fn test_rsplitator() {
-    let xs = &[1,2,3,4,5];
+    let xs = &[1, 2, 3, 4, 5];
 
     let splits: &[&[_]] = &[&[5], &[3], &[1]];
-    assert_eq!(xs.split(|x| *x % 2 == 0).rev().collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[2,3,4,5], &[]];
-    assert_eq!(xs.split(|x| *x == 1).rev().collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[], &[1,2,3,4]];
-    assert_eq!(xs.split(|x| *x == 5).rev().collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[1,2,3,4,5]];
-    assert_eq!(xs.split(|x| *x == 10).rev().collect::<Vec<_>>(),
-               splits);
+    assert_eq!(xs.split(|x| *x % 2 == 0).rev().collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[2, 3, 4, 5], &[]];
+    assert_eq!(xs.split(|x| *x == 1).rev().collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[], &[1, 2, 3, 4]];
+    assert_eq!(xs.split(|x| *x == 5).rev().collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]];
+    assert_eq!(xs.split(|x| *x == 10).rev().collect::<Vec<_>>(), splits);
 
     let xs: &[i32] = &[];
     let splits: &[&[i32]] = &[&[]];
@@ -785,19 +775,16 @@ fn test_rsplitator() {
 
 #[test]
 fn test_rsplitnator() {
-    let xs = &[1,2,3,4,5];
+    let xs = &[1, 2, 3, 4, 5];
 
-    let splits: &[&[_]] = &[&[1,2,3,4,5]];
-    assert_eq!(xs.rsplitn(1, |x| *x % 2 == 0).collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[5], &[1,2,3]];
-    assert_eq!(xs.rsplitn(2, |x| *x % 2 == 0).collect::<Vec<_>>(),
-               splits);
-    let splits: &[&[_]] = &[&[], &[], &[], &[1,2]];
-    assert_eq!(xs.rsplitn(4, |_| true).collect::<Vec<_>>(),
-               splits);
+    let splits: &[&[_]] = &[&[1, 2, 3, 4, 5]];
+    assert_eq!(xs.rsplitn(1, |x| *x % 2 == 0).collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[5], &[1, 2, 3]];
+    assert_eq!(xs.rsplitn(2, |x| *x % 2 == 0).collect::<Vec<_>>(), splits);
+    let splits: &[&[_]] = &[&[], &[], &[], &[1, 2]];
+    assert_eq!(xs.rsplitn(4, |_| true).collect::<Vec<_>>(), splits);
 
-    let xs: &[i32]  = &[];
+    let xs: &[i32] = &[];
     let splits: &[&[i32]] = &[&[]];
     assert_eq!(xs.rsplitn(2, |x| *x == 5).collect::<Vec<&[i32]>>(), splits);
     assert!(xs.rsplitn(0, |x| *x % 2 == 0).next().is_none());
@@ -805,55 +792,55 @@ fn test_rsplitnator() {
 
 #[test]
 fn test_windowsator() {
-    let v = &[1,2,3,4];
+    let v = &[1, 2, 3, 4];
 
-    let wins: &[&[_]] = &[&[1,2], &[2,3], &[3,4]];
+    let wins: &[&[_]] = &[&[1, 2], &[2, 3], &[3, 4]];
     assert_eq!(v.windows(2).collect::<Vec<_>>(), wins);
 
-    let wins: &[&[_]] = &[&[1,2,3], &[2,3,4]];
+    let wins: &[&[_]] = &[&[1, 2, 3], &[2, 3, 4]];
     assert_eq!(v.windows(3).collect::<Vec<_>>(), wins);
     assert!(v.windows(6).next().is_none());
 
-    let wins: &[&[_]] = &[&[3,4], &[2,3], &[1,2]];
+    let wins: &[&[_]] = &[&[3, 4], &[2, 3], &[1, 2]];
     assert_eq!(v.windows(2).rev().collect::<Vec<&[_]>>(), wins);
 }
 
 #[test]
 #[should_panic]
 fn test_windowsator_0() {
-    let v = &[1,2,3,4];
+    let v = &[1, 2, 3, 4];
     let _it = v.windows(0);
 }
 
 #[test]
 fn test_chunksator() {
-    let v = &[1,2,3,4,5];
+    let v = &[1, 2, 3, 4, 5];
 
     assert_eq!(v.chunks(2).len(), 3);
 
-    let chunks: &[&[_]] = &[&[1,2], &[3,4], &[5]];
+    let chunks: &[&[_]] = &[&[1, 2], &[3, 4], &[5]];
     assert_eq!(v.chunks(2).collect::<Vec<_>>(), chunks);
-    let chunks: &[&[_]] = &[&[1,2,3], &[4,5]];
+    let chunks: &[&[_]] = &[&[1, 2, 3], &[4, 5]];
     assert_eq!(v.chunks(3).collect::<Vec<_>>(), chunks);
-    let chunks: &[&[_]] = &[&[1,2,3,4,5]];
+    let chunks: &[&[_]] = &[&[1, 2, 3, 4, 5]];
     assert_eq!(v.chunks(6).collect::<Vec<_>>(), chunks);
 
-    let chunks: &[&[_]] = &[&[5], &[3,4], &[1,2]];
+    let chunks: &[&[_]] = &[&[5], &[3, 4], &[1, 2]];
     assert_eq!(v.chunks(2).rev().collect::<Vec<_>>(), chunks);
 }
 
 #[test]
 #[should_panic]
 fn test_chunksator_0() {
-    let v = &[1,2,3,4];
+    let v = &[1, 2, 3, 4];
     let _it = v.chunks(0);
 }
 
 #[test]
 fn test_reverse_part() {
-    let mut values = [1,2,3,4,5];
+    let mut values = [1, 2, 3, 4, 5];
     values[1..4].reverse();
-    assert!(values == [1,4,3,2,5]);
+    assert!(values == [1, 4, 3, 2, 5]);
 }
 
 #[test]
@@ -869,16 +856,15 @@ fn test_show() {
     test_show_vec!(empty, "[]");
     test_show_vec!(vec![1], "[1]");
     test_show_vec!(vec![1, 2, 3], "[1, 2, 3]");
-    test_show_vec!(vec![vec![], vec![1], vec![1, 1]],
-                   "[[], [1], [1, 1]]");
+    test_show_vec!(vec![vec![], vec![1], vec![1, 1]], "[[], [1], [1, 1]]");
 
-    let empty_mut: &mut [i32] = &mut[];
+    let empty_mut: &mut [i32] = &mut [];
     test_show_vec!(empty_mut, "[]");
-    let v = &mut[1];
+    let v = &mut [1];
     test_show_vec!(v, "[1]");
-    let v = &mut[1, 2, 3];
+    let v = &mut [1, 2, 3];
     test_show_vec!(v, "[1, 2, 3]");
-    let v: &mut[&mut[_]] = &mut[&mut[], &mut[1], &mut[1, 1]];
+    let v: &mut [&mut [_]] = &mut [&mut [], &mut [1], &mut [1, 1]];
     test_show_vec!(v, "[[], [1], [1, 1]]");
 }
 
@@ -914,7 +900,7 @@ fn test_overflow_does_not_cause_segfault_managed() {
 
 #[test]
 fn test_mut_split_at() {
-    let mut values = [1,2,3,4,5];
+    let mut values = [1, 2, 3, 4, 5];
     {
         let (left, right) = values.split_at_mut(2);
         {
@@ -1021,32 +1007,32 @@ fn test_ends_with() {
 
 #[test]
 fn test_mut_splitator() {
-    let mut xs = [0,1,0,2,3,0,0,4,5,0];
+    let mut xs = [0, 1, 0, 2, 3, 0, 0, 4, 5, 0];
     assert_eq!(xs.split_mut(|x| *x == 0).count(), 6);
     for slice in xs.split_mut(|x| *x == 0) {
         slice.reverse();
     }
-    assert!(xs == [0,1,0,3,2,0,0,5,4,0]);
+    assert!(xs == [0, 1, 0, 3, 2, 0, 0, 5, 4, 0]);
 
-    let mut xs = [0,1,0,2,3,0,0,4,5,0,6,7];
+    let mut xs = [0, 1, 0, 2, 3, 0, 0, 4, 5, 0, 6, 7];
     for slice in xs.split_mut(|x| *x == 0).take(5) {
         slice.reverse();
     }
-    assert!(xs == [0,1,0,3,2,0,0,5,4,0,6,7]);
+    assert!(xs == [0, 1, 0, 3, 2, 0, 0, 5, 4, 0, 6, 7]);
 }
 
 #[test]
 fn test_mut_splitator_rev() {
-    let mut xs = [1,2,0,3,4,0,0,5,6,0];
+    let mut xs = [1, 2, 0, 3, 4, 0, 0, 5, 6, 0];
     for slice in xs.split_mut(|x| *x == 0).rev().take(4) {
         slice.reverse();
     }
-    assert!(xs == [1,2,0,4,3,0,0,6,5,0]);
+    assert!(xs == [1, 2, 0, 4, 3, 0, 0, 6, 5, 0]);
 }
 
 #[test]
 fn test_get_mut() {
-    let mut v = [0,1,2];
+    let mut v = [0, 1, 2];
     assert_eq!(v.get_mut(3), None);
     v.get_mut(1).map(|e| *e = 7);
     assert_eq!(v[1], 7);
@@ -1119,7 +1105,7 @@ fn test_box_slice_clone_panics() {
 
     struct Canary {
         count: Arc<AtomicUsize>,
-        panics: bool
+        panics: bool,
     }
 
     impl Drop for Canary {
@@ -1130,27 +1116,37 @@ fn test_box_slice_clone_panics() {
 
     impl Clone for Canary {
         fn clone(&self) -> Self {
-            if self.panics { panic!() }
+            if self.panics {
+                panic!()
+            }
 
             Canary {
                 count: self.count.clone(),
-                panics: self.panics
+                panics: self.panics,
             }
         }
     }
 
     let drop_count = Arc::new(AtomicUsize::new(0));
-    let canary = Canary { count: drop_count.clone(), panics: false };
-    let panic = Canary { count: drop_count.clone(), panics: true };
+    let canary = Canary {
+        count: drop_count.clone(),
+        panics: false,
+    };
+    let panic = Canary {
+        count: drop_count.clone(),
+        panics: true,
+    };
 
     spawn(move || {
         // When xs is dropped, +5.
-        let xs = vec![canary.clone(), canary.clone(), canary.clone(),
-                      panic, canary].into_boxed_slice();
+        let xs = vec![canary.clone(), canary.clone(), canary.clone(), panic, canary]
+                     .into_boxed_slice();
 
         // When panic is cloned, +3.
         xs.clone();
-    }).join().unwrap_err();
+    })
+        .join()
+        .unwrap_err();
 
     // Total = 8
     assert_eq!(drop_count.load(Ordering::SeqCst), 8);
@@ -1198,7 +1194,9 @@ mod bench {
                 sum += *x;
             }
             // sum == 11806, to stop dead code elimination.
-            if sum == 0 {panic!()}
+            if sum == 0 {
+                panic!()
+            }
         })
     }
 
@@ -1217,8 +1215,7 @@ mod bench {
 
     #[bench]
     fn concat(b: &mut Bencher) {
-        let xss: Vec<Vec<i32>> =
-            (0..100).map(|i| (0..i).collect()).collect();
+        let xss: Vec<Vec<i32>> = (0..100).map(|i| (0..i).collect()).collect();
         b.iter(|| {
             xss.concat();
         });
@@ -1226,11 +1223,8 @@ mod bench {
 
     #[bench]
     fn join(b: &mut Bencher) {
-        let xss: Vec<Vec<i32>> =
-            (0..100).map(|i| (0..i).collect()).collect();
-        b.iter(|| {
-            xss.join(&0)
-        });
+        let xss: Vec<Vec<i32>> = (0..100).map(|i| (0..i).collect()).collect();
+        b.iter(|| xss.join(&0));
     }
 
     #[bench]
@@ -1245,17 +1239,13 @@ mod bench {
     #[bench]
     fn starts_with_same_vector(b: &mut Bencher) {
         let vec: Vec<_> = (0..100).collect();
-        b.iter(|| {
-            vec.starts_with(&vec)
-        })
+        b.iter(|| vec.starts_with(&vec))
     }
 
     #[bench]
     fn starts_with_single_element(b: &mut Bencher) {
         let vec: Vec<_> = vec![0];
-        b.iter(|| {
-            vec.starts_with(&vec)
-        })
+        b.iter(|| vec.starts_with(&vec))
     }
 
     #[bench]
@@ -1263,25 +1253,19 @@ mod bench {
         let vec: Vec<_> = (0..100).collect();
         let mut match_vec: Vec<_> = (0..99).collect();
         match_vec.push(0);
-        b.iter(|| {
-            vec.starts_with(&match_vec)
-        })
+        b.iter(|| vec.starts_with(&match_vec))
     }
 
     #[bench]
     fn ends_with_same_vector(b: &mut Bencher) {
         let vec: Vec<_> = (0..100).collect();
-        b.iter(|| {
-            vec.ends_with(&vec)
-        })
+        b.iter(|| vec.ends_with(&vec))
     }
 
     #[bench]
     fn ends_with_single_element(b: &mut Bencher) {
         let vec: Vec<_> = vec![0];
-        b.iter(|| {
-            vec.ends_with(&vec)
-        })
+        b.iter(|| vec.ends_with(&vec))
     }
 
     #[bench]
@@ -1289,24 +1273,18 @@ mod bench {
         let vec: Vec<_> = (0..100).collect();
         let mut match_vec: Vec<_> = (0..100).collect();
         match_vec[0] = 200;
-        b.iter(|| {
-            vec.starts_with(&match_vec)
-        })
+        b.iter(|| vec.starts_with(&match_vec))
     }
 
     #[bench]
     fn contains_last_element(b: &mut Bencher) {
         let vec: Vec<_> = (0..100).collect();
-        b.iter(|| {
-            vec.contains(&99)
-        })
+        b.iter(|| vec.contains(&99))
     }
 
     #[bench]
     fn zero_1kb_from_elem(b: &mut Bencher) {
-        b.iter(|| {
-            vec![0u8; 1024]
-        });
+        b.iter(|| vec![0u8; 1024]);
     }
 
     #[bench]
@@ -1356,8 +1334,7 @@ mod bench {
             let mut v = vec![(0, 0); 30];
             for _ in 0..100 {
                 let l = v.len();
-                v.insert(rng.gen::<usize>() % (l + 1),
-                         (1, 1));
+                v.insert(rng.gen::<usize>() % (l + 1), (1, 1));
             }
         })
     }
@@ -1418,7 +1395,8 @@ mod bench {
     fn sort_big_random_small(b: &mut Bencher) {
         let mut rng = thread_rng();
         b.iter(|| {
-            let mut v = rng.gen_iter::<BigSortable>().take(5)
+            let mut v = rng.gen_iter::<BigSortable>()
+                           .take(5)
                            .collect::<Vec<BigSortable>>();
             v.sort();
         });
@@ -1429,7 +1407,8 @@ mod bench {
     fn sort_big_random_medium(b: &mut Bencher) {
         let mut rng = thread_rng();
         b.iter(|| {
-            let mut v = rng.gen_iter::<BigSortable>().take(100)
+            let mut v = rng.gen_iter::<BigSortable>()
+                           .take(100)
                            .collect::<Vec<BigSortable>>();
             v.sort();
         });
@@ -1440,7 +1419,8 @@ mod bench {
     fn sort_big_random_large(b: &mut Bencher) {
         let mut rng = thread_rng();
         b.iter(|| {
-            let mut v = rng.gen_iter::<BigSortable>().take(10000)
+            let mut v = rng.gen_iter::<BigSortable>()
+                           .take(10000)
                            .collect::<Vec<BigSortable>>();
             v.sort();
         });
index a1820a1cb96e3901b973454ce802f4a23494c909..07428f3f8b2d89a3cdbb61384b8de63dcb2bd072 100644 (file)
@@ -123,8 +123,6 @@ macro_rules! test_concat {
 fn test_concat_for_different_types() {
     test_concat!("ab", vec![s("a"), s("b")]);
     test_concat!("ab", vec!["a", "b"]);
-    test_concat!("ab", vec!["a", "b"]);
-    test_concat!("ab", vec![s("a"), s("b")]);
 }
 
 #[test]
@@ -194,24 +192,24 @@ fn test_unsafe_slice() {
 
 #[test]
 fn test_starts_with() {
-    assert!(("".starts_with("")));
-    assert!(("abc".starts_with("")));
-    assert!(("abc".starts_with("a")));
-    assert!((!"a".starts_with("abc")));
-    assert!((!"".starts_with("abc")));
-    assert!((!"ödd".starts_with("-")));
-    assert!(("ödd".starts_with("öd")));
+    assert!("".starts_with(""));
+    assert!("abc".starts_with(""));
+    assert!("abc".starts_with("a"));
+    assert!(!"a".starts_with("abc"));
+    assert!(!"".starts_with("abc"));
+    assert!(!"ödd".starts_with("-"));
+    assert!("ödd".starts_with("öd"));
 }
 
 #[test]
 fn test_ends_with() {
-    assert!(("".ends_with("")));
-    assert!(("abc".ends_with("")));
-    assert!(("abc".ends_with("c")));
-    assert!((!"a".ends_with("abc")));
-    assert!((!"".ends_with("abc")));
-    assert!((!"ddö".ends_with("-")));
-    assert!(("ddö".ends_with("dö")));
+    assert!("".ends_with(""));
+    assert!("abc".ends_with(""));
+    assert!("abc".ends_with("c"));
+    assert!(!"a".ends_with("abc"));
+    assert!(!"".ends_with("abc"));
+    assert!(!"ddö".ends_with("-"));
+    assert!("ddö".ends_with("dö"));
 }
 
 #[test]
@@ -479,20 +477,6 @@ fn test_is_whitespace() {
     assert!(!"   _   ".chars().all(|c| c.is_whitespace()));
 }
 
-#[test]
-#[allow(deprecated)]
-fn test_slice_shift_char() {
-    let data = "ประเทศไทย中";
-    assert_eq!(data.slice_shift_char(), Some(('ป', "ระเทศไทย中")));
-}
-
-#[test]
-#[allow(deprecated)]
-fn test_slice_shift_char_2() {
-    let empty = "";
-    assert_eq!(empty.slice_shift_char(), None);
-}
-
 #[test]
 fn test_is_utf8() {
     // deny overlong encodings
@@ -674,30 +658,6 @@ fn test_contains_char() {
     assert!(!"".contains('a'));
 }
 
-#[test]
-#[allow(deprecated)]
-fn test_char_at() {
-    let s = "ศไทย中华Việt Nam";
-    let v = vec!['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m'];
-    let mut pos = 0;
-    for ch in &v {
-        assert!(s.char_at(pos) == *ch);
-        pos += ch.to_string().len();
-    }
-}
-
-#[test]
-#[allow(deprecated)]
-fn test_char_at_reverse() {
-    let s = "ศไทย中华Việt Nam";
-    let v = vec!['ศ','ไ','ท','ย','中','华','V','i','ệ','t',' ','N','a','m'];
-    let mut pos = s.len();
-    for ch in v.iter().rev() {
-        assert!(s.char_at_reverse(pos) == *ch);
-        pos -= ch.to_string().len();
-    }
-}
-
 #[test]
 fn test_split_at() {
     let s = "ศไทย中华Việt Nam";
@@ -764,26 +724,6 @@ fn test_total_ord() {
     assert_eq!("22".cmp("1234"), Greater);
 }
 
-#[test]
-#[allow(deprecated)]
-fn test_char_range_at() {
-    let data = "b¢€𤭢𤭢€¢b";
-    assert_eq!('b', data.char_range_at(0).ch);
-    assert_eq!('¢', data.char_range_at(1).ch);
-    assert_eq!('€', data.char_range_at(3).ch);
-    assert_eq!('𤭢', data.char_range_at(6).ch);
-    assert_eq!('𤭢', data.char_range_at(10).ch);
-    assert_eq!('€', data.char_range_at(14).ch);
-    assert_eq!('¢', data.char_range_at(17).ch);
-    assert_eq!('b', data.char_range_at(19).ch);
-}
-
-#[test]
-#[allow(deprecated)]
-fn test_char_range_at_reverse_underflow() {
-    assert_eq!("abc".char_range_at_reverse(0).next, 0);
-}
-
 #[test]
 fn test_iterator() {
     let s = "ศไทย中华Việt Nam";
index c2eafa1b90f12331477052a6fbf7be0cdbf8217b..7f0fd282ae5e943f32b7f281a7c2f9137f2f50d7 100644 (file)
@@ -31,8 +31,8 @@ impl<'a> IntoCow<'a, str> for &'a str {
 
 #[test]
 fn test_from_str() {
-  let owned: Option<::std::string::String> = "string".parse().ok();
-  assert_eq!(owned.as_ref().map(|s| &**s), Some("string"));
+    let owned: Option<::std::string::String> = "string".parse().ok();
+    assert_eq!(owned.as_ref().map(|s| &**s), Some("string"));
 }
 
 #[test]
@@ -44,8 +44,7 @@ fn test_unsized_to_string() {
 #[test]
 fn test_from_utf8() {
     let xs = b"hello".to_vec();
-    assert_eq!(String::from_utf8(xs).unwrap(),
-               String::from("hello"));
+    assert_eq!(String::from_utf8(xs).unwrap(), String::from("hello"));
 
     let xs = "ศไทย中华Việt Nam".as_bytes().to_vec();
     assert_eq!(String::from_utf8(xs).unwrap(),
@@ -87,56 +86,40 @@ fn test_from_utf8_lossy() {
                String::from("\u{FFFD}foo\u{FFFD}bar\u{FFFD}\u{FFFD}baz").into_cow());
 
     let xs = b"\xF0\x80\x80\x80foo\xF0\x90\x80\x80bar";
-    assert_eq!(String::from_utf8_lossy(xs), String::from("\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}\
-                                           foo\u{10000}bar").into_cow());
+    assert_eq!(String::from_utf8_lossy(xs),
+               String::from("\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}foo\u{10000}bar").into_cow());
 
     // surrogates
     let xs = b"\xED\xA0\x80foo\xED\xBF\xBFbar";
-    assert_eq!(String::from_utf8_lossy(xs), String::from("\u{FFFD}\u{FFFD}\u{FFFD}foo\
-                                           \u{FFFD}\u{FFFD}\u{FFFD}bar").into_cow());
+    assert_eq!(String::from_utf8_lossy(xs),
+               String::from("\u{FFFD}\u{FFFD}\u{FFFD}foo\u{FFFD}\u{FFFD}\u{FFFD}bar").into_cow());
 }
 
 #[test]
 fn test_from_utf16() {
-    let pairs =
-        [(String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"),
-          vec![0xd800, 0xdf45, 0xd800, 0xdf3f,
-            0xd800, 0xdf3b, 0xd800, 0xdf46,
-            0xd800, 0xdf39, 0xd800, 0xdf3b,
-            0xd800, 0xdf30, 0x000a]),
-
-         (String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"),
-          vec![0xd801, 0xdc12, 0xd801,
-            0xdc49, 0xd801, 0xdc2e, 0xd801,
-            0xdc40, 0xd801, 0xdc32, 0xd801,
-            0xdc4b, 0x0020, 0xd801, 0xdc0f,
-            0xd801, 0xdc32, 0xd801, 0xdc4d,
-            0x000a]),
-
-         (String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"),
-          vec![0xd800, 0xdf00, 0xd800, 0xdf16,
-            0xd800, 0xdf0b, 0xd800, 0xdf04,
-            0xd800, 0xdf11, 0xd800, 0xdf09,
-            0x00b7, 0xd800, 0xdf0c, 0xd800,
-            0xdf04, 0xd800, 0xdf15, 0xd800,
-            0xdf04, 0xd800, 0xdf0b, 0xd800,
-            0xdf09, 0xd800, 0xdf11, 0x000a ]),
-
-         (String::from("𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n"),
-          vec![0xd801, 0xdc8b, 0xd801, 0xdc98,
-            0xd801, 0xdc88, 0xd801, 0xdc91,
-            0xd801, 0xdc9b, 0xd801, 0xdc92,
-            0x0020, 0xd801, 0xdc95, 0xd801,
-            0xdc93, 0x0020, 0xd801, 0xdc88,
-            0xd801, 0xdc9a, 0xd801, 0xdc8d,
-            0x0020, 0xd801, 0xdc8f, 0xd801,
-            0xdc9c, 0xd801, 0xdc92, 0xd801,
-            0xdc96, 0xd801, 0xdc86, 0x0020,
-            0xd801, 0xdc95, 0xd801, 0xdc86,
-            0x000a ]),
-         // Issue #12318, even-numbered non-BMP planes
-         (String::from("\u{20000}"),
-          vec![0xD840, 0xDC00])];
+    let pairs = [(String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"),
+                  vec![0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800,
+                       0xdf39, 0xd800, 0xdf3b, 0xd800, 0xdf30, 0x000a]),
+
+                 (String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"),
+                  vec![0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801,
+                       0xdc32, 0xd801, 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801,
+                       0xdc4d, 0x000a]),
+
+                 (String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"),
+                  vec![0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800,
+                       0xdf11, 0xd800, 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800,
+                       0xdf15, 0xd800, 0xdf04, 0xd800, 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11,
+                       0x000a]),
+
+                 (String::from("𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n"),
+                  vec![0xd801, 0xdc8b, 0xd801, 0xdc98, 0xd801, 0xdc88, 0xd801, 0xdc91, 0xd801,
+                       0xdc9b, 0xd801, 0xdc92, 0x0020, 0xd801, 0xdc95, 0xd801, 0xdc93, 0x0020,
+                       0xd801, 0xdc88, 0xd801, 0xdc9a, 0xd801, 0xdc8d, 0x0020, 0xd801, 0xdc8f,
+                       0xd801, 0xdc9c, 0xd801, 0xdc92, 0xd801, 0xdc96, 0xd801, 0xdc86, 0x0020,
+                       0xd801, 0xdc95, 0xd801, 0xdc86, 0x000a]),
+                 // Issue #12318, even-numbered non-BMP planes
+                 (String::from("\u{20000}"), vec![0xD840, 0xDC00])];
 
     for p in &pairs {
         let (s, u) = (*p).clone();
@@ -173,13 +156,15 @@ fn test_utf16_invalid() {
 fn test_from_utf16_lossy() {
     // completely positive cases tested above.
     // lead + eof
-    assert_eq!(String::from_utf16_lossy(&[0xD800]), String::from("\u{FFFD}"));
+    assert_eq!(String::from_utf16_lossy(&[0xD800]),
+               String::from("\u{FFFD}"));
     // lead + lead
     assert_eq!(String::from_utf16_lossy(&[0xD800, 0xD800]),
                String::from("\u{FFFD}\u{FFFD}"));
 
     // isolated trail
-    assert_eq!(String::from_utf16_lossy(&[0x0061, 0xDC00]), String::from("a\u{FFFD}"));
+    assert_eq!(String::from_utf16_lossy(&[0x0061, 0xDC00]),
+               String::from("a\u{FFFD}"));
 
     // general
     assert_eq!(String::from_utf16_lossy(&[0xD800, 0xd801, 0xdc8b, 0xD800]),
@@ -288,7 +273,8 @@ fn remove() {
     assert_eq!(s, "ไทย中华Vit Nam; foobar");
 }
 
-#[test] #[should_panic]
+#[test]
+#[should_panic]
 fn remove_bad() {
     "ศ".to_string().remove(1);
 }
@@ -302,8 +288,16 @@ fn insert() {
     assert_eq!(s, "ệfooยbar");
 }
 
-#[test] #[should_panic] fn insert_bad1() { "".to_string().insert(1, 't'); }
-#[test] #[should_panic] fn insert_bad2() { "ệ".to_string().insert(1, 't'); }
+#[test]
+#[should_panic]
+fn insert_bad1() {
+    "".to_string().insert(1, 't');
+}
+#[test]
+#[should_panic]
+fn insert_bad2() {
+    "ệ".to_string().insert(1, 't');
+}
 
 #[test]
 fn test_slicing() {
@@ -331,8 +325,7 @@ fn test_vectors() {
     assert_eq!(format!("{:?}", x), "[]");
     assert_eq!(format!("{:?}", vec![1]), "[1]");
     assert_eq!(format!("{:?}", vec![1, 2, 3]), "[1, 2, 3]");
-    assert!(format!("{:?}", vec![vec![], vec![1], vec![1, 1]]) ==
-           "[[], [1], [1, 1]]");
+    assert!(format!("{:?}", vec![vec![], vec![1], vec![1, 1]]) == "[[], [1], [1, 1]]");
 }
 
 #[test]
@@ -390,9 +383,7 @@ fn test_into_boxed_str() {
 
 #[bench]
 fn bench_with_capacity(b: &mut Bencher) {
-    b.iter(|| {
-        String::with_capacity(100)
-    });
+    b.iter(|| String::with_capacity(100));
 }
 
 #[bench]
@@ -495,25 +486,19 @@ fn bench_exact_size_shrink_to_fit(b: &mut Bencher) {
 fn bench_from_str(b: &mut Bencher) {
     let s = "Hello there, the quick brown fox jumped over the lazy dog! \
              Lorem ipsum dolor sit amet, consectetur. ";
-    b.iter(|| {
-        String::from(s)
-    })
+    b.iter(|| String::from(s))
 }
 
 #[bench]
 fn bench_from(b: &mut Bencher) {
     let s = "Hello there, the quick brown fox jumped over the lazy dog! \
              Lorem ipsum dolor sit amet, consectetur. ";
-    b.iter(|| {
-        String::from(s)
-    })
+    b.iter(|| String::from(s))
 }
 
 #[bench]
 fn bench_to_string(b: &mut Bencher) {
     let s = "Hello there, the quick brown fox jumped over the lazy dog! \
              Lorem ipsum dolor sit amet, consectetur. ";
-    b.iter(|| {
-        s.to_string()
-    })
+    b.iter(|| s.to_string())
 }
index 0fb00543ddd9731e43d5ff7590b1902c217cf20a..cb99659cc0ead7238fd30d75d49cdef095aea976 100644 (file)
@@ -15,7 +15,7 @@ use std::mem::size_of;
 use test::Bencher;
 
 struct DropCounter<'a> {
-    count: &'a mut u32
+    count: &'a mut u32,
 }
 
 impl<'a> Drop for DropCounter<'a> {
@@ -33,17 +33,17 @@ fn test_small_vec_struct() {
 fn test_double_drop() {
     struct TwoVec<T> {
         x: Vec<T>,
-        y: Vec<T>
+        y: Vec<T>,
     }
 
     let (mut count_x, mut count_y) = (0, 0);
     {
         let mut tv = TwoVec {
             x: Vec::new(),
-            y: Vec::new()
+            y: Vec::new(),
         };
-        tv.x.push(DropCounter {count: &mut count_x});
-        tv.y.push(DropCounter {count: &mut count_y});
+        tv.x.push(DropCounter { count: &mut count_x });
+        tv.y.push(DropCounter { count: &mut count_y });
 
         // If Vec had a drop flag, here is where it would be zeroed.
         // Instead, it should rely on its internal state to prevent
@@ -85,12 +85,16 @@ fn test_extend() {
     let mut w = Vec::new();
 
     v.extend(0..3);
-    for i in 0..3 { w.push(i) }
+    for i in 0..3 {
+        w.push(i)
+    }
 
     assert_eq!(v, w);
 
     v.extend(3..10);
-    for i in 3..10 { w.push(i) }
+    for i in 3..10 {
+        w.push(i)
+    }
 
     assert_eq!(v, w);
 
@@ -117,7 +121,7 @@ fn test_extend_ref() {
 fn test_slice_from_mut() {
     let mut values = vec![1, 2, 3, 4, 5];
     {
-        let slice = &mut values[2 ..];
+        let slice = &mut values[2..];
         assert!(slice == [3, 4, 5]);
         for p in slice {
             *p += 2;
@@ -131,7 +135,7 @@ fn test_slice_from_mut() {
 fn test_slice_to_mut() {
     let mut values = vec![1, 2, 3, 4, 5];
     {
-        let slice = &mut values[.. 2];
+        let slice = &mut values[..2];
         assert!(slice == [1, 2]);
         for p in slice {
             *p += 1;
@@ -169,7 +173,7 @@ fn test_split_at_mut() {
 #[test]
 fn test_clone() {
     let v: Vec<i32> = vec![];
-    let w = vec!(1, 2, 3);
+    let w = vec![1, 2, 3];
 
     assert_eq!(v, v.clone());
 
@@ -181,9 +185,9 @@ fn test_clone() {
 
 #[test]
 fn test_clone_from() {
-    let mut v = vec!();
-    let three: Vec<Box<_>> = vec!(box 1, box 2, box 3);
-    let two: Vec<Box<_>> = vec!(box 4, box 5);
+    let mut v = vec![];
+    let three: Vec<Box<_>> = vec![box 1, box 2, box 3];
+    let two: Vec<Box<_>> = vec![box 4, box 5];
     // zero, long
     v.clone_from(&three);
     assert_eq!(v, three);
@@ -235,16 +239,22 @@ fn zero_sized_values() {
     assert_eq!(v.iter_mut().count(), 4);
 
     for &mut () in &mut v {}
-    unsafe { v.set_len(0); }
+    unsafe {
+        v.set_len(0);
+    }
     assert_eq!(v.iter_mut().count(), 0);
 }
 
 #[test]
 fn test_partition() {
-    assert_eq!(vec![].into_iter().partition(|x: &i32| *x < 3), (vec![], vec![]));
-    assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 4), (vec![1, 2, 3], vec![]));
-    assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 2), (vec![1], vec![2, 3]));
-    assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 0), (vec![], vec![1, 2, 3]));
+    assert_eq!(vec![].into_iter().partition(|x: &i32| *x < 3),
+               (vec![], vec![]));
+    assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 4),
+               (vec![1, 2, 3], vec![]));
+    assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 2),
+               (vec![1], vec![2, 3]));
+    assert_eq!(vec![1, 2, 3].into_iter().partition(|x| *x < 0),
+               (vec![], vec![1, 2, 3]));
 }
 
 #[test]
@@ -264,7 +274,9 @@ fn test_vec_truncate_drop() {
     struct Elem(i32);
     impl Drop for Elem {
         fn drop(&mut self) {
-            unsafe { drops += 1; }
+            unsafe {
+                drops += 1;
+            }
         }
     }
 
@@ -344,7 +356,7 @@ fn test_slice_out_of_bounds_5() {
 #[test]
 #[should_panic]
 fn test_swap_remove_empty() {
-    let mut vec= Vec::<i32>::new();
+    let mut vec = Vec::<i32>::new();
     vec.swap_remove(0);
 }
 
@@ -386,7 +398,7 @@ fn test_drain_items() {
         vec2.push(i);
     }
     assert_eq!(vec, []);
-    assert_eq!(vec2, [ 1, 2, 3 ]);
+    assert_eq!(vec2, [1, 2, 3]);
 }
 
 #[test]
@@ -472,7 +484,7 @@ fn test_into_iter_count() {
 
 #[test]
 fn test_into_iter_clone() {
-    fn iter_equal<I: Iterator<Item=i32>>(it: I, slice: &[i32]) {
+    fn iter_equal<I: Iterator<Item = i32>>(it: I, slice: &[i32]) {
         let v: Vec<i32> = it.collect();
         assert_eq!(&v[..], slice);
     }
index 05af9bd704dc819ee6152b7066c04654b15a44e7..a02666a50c2258e20bb1b8bc3f589fe91a4a3122 100644 (file)
@@ -52,7 +52,7 @@ fn test_simple() {
 }
 
 #[cfg(test)]
-fn test_parameterized<T:Clone + PartialEq + Debug>(a: T, b: T, c: T, d: T) {
+fn test_parameterized<T: Clone + PartialEq + Debug>(a: T, b: T, c: T, d: T) {
     let mut deq = VecDeque::new();
     assert_eq!(deq.len(), 0);
     deq.push_front(a.clone());
@@ -186,7 +186,7 @@ enum Taggypar<T> {
 struct RecCy {
     x: i32,
     y: i32,
-    t: Taggy
+    t: Taggy,
 }
 
 #[test]
@@ -209,10 +209,26 @@ fn test_param_taggypar() {
 
 #[test]
 fn test_param_reccy() {
-    let reccy1 = RecCy { x: 1, y: 2, t: One(1) };
-    let reccy2 = RecCy { x: 345, y: 2, t: Two(1, 2) };
-    let reccy3 = RecCy { x: 1, y: 777, t: Three(1, 2, 3) };
-    let reccy4 = RecCy { x: 19, y: 252, t: Two(17, 42) };
+    let reccy1 = RecCy {
+        x: 1,
+        y: 2,
+        t: One(1),
+    };
+    let reccy2 = RecCy {
+        x: 345,
+        y: 2,
+        t: Two(1, 2),
+    };
+    let reccy3 = RecCy {
+        x: 1,
+        y: 777,
+        t: Three(1, 2, 3),
+    };
+    let reccy4 = RecCy {
+        x: 19,
+        y: 252,
+        t: Two(17, 42),
+    };
     test_parameterized::<RecCy>(reccy1, reccy2, reccy3, reccy4);
 }
 
@@ -257,13 +273,13 @@ fn test_with_capacity_non_power_two() {
     // underlying Vec which didn't hold and lead
     // to corruption.
     // (Vec grows to next power of two)
-    //good- [9, 12, 15, X, X, X, X, |6]
-    //bug-  [15, 12, X, X, X, |6, X, X]
+    // good- [9, 12, 15, X, X, X, X, |6]
+    // bug-  [15, 12, X, X, X, |6, X, X]
     assert_eq!(d3.pop_front(), Some(6));
 
     // Which leads us to the following state which
     // would be a failure case.
-    //bug-  [15, 12, X, X, X, X, |X, X]
+    // bug-  [15, 12, X, X, X, X, |X, X]
     assert_eq!(d3.front(), Some(&9));
 }
 
@@ -301,7 +317,7 @@ fn test_iter() {
         d.push_back(i);
     }
     {
-        let b: &[_] = &[&0,&1,&2,&3,&4];
+        let b: &[_] = &[&0, &1, &2, &3, &4];
         assert_eq!(d.iter().collect::<Vec<_>>(), b);
     }
 
@@ -309,7 +325,7 @@ fn test_iter() {
         d.push_front(i);
     }
     {
-        let b: &[_] = &[&8,&7,&6,&0,&1,&2,&3,&4];
+        let b: &[_] = &[&8, &7, &6, &0, &1, &2, &3, &4];
         assert_eq!(d.iter().collect::<Vec<_>>(), b);
     }
 
@@ -318,7 +334,10 @@ fn test_iter() {
     loop {
         match it.next() {
             None => break,
-            _ => { len -= 1; assert_eq!(it.size_hint(), (len, Some(len))) }
+            _ => {
+                len -= 1;
+                assert_eq!(it.size_hint(), (len, Some(len)))
+            }
         }
     }
 }
@@ -332,14 +351,14 @@ fn test_rev_iter() {
         d.push_back(i);
     }
     {
-        let b: &[_] = &[&4,&3,&2,&1,&0];
+        let b: &[_] = &[&4, &3, &2, &1, &0];
         assert_eq!(d.iter().rev().collect::<Vec<_>>(), b);
     }
 
     for i in 6..9 {
         d.push_front(i);
     }
-    let b: &[_] = &[&4,&3,&2,&1,&0,&6,&7,&8];
+    let b: &[_] = &[&4, &3, &2, &1, &0, &6, &7, &8];
     assert_eq!(d.iter().rev().collect::<Vec<_>>(), b);
 }
 
@@ -424,7 +443,7 @@ fn test_into_iter() {
             d.push_back(i);
         }
 
-        let b = vec![0,1,2,3,4];
+        let b = vec![0, 1, 2, 3, 4];
         assert_eq!(d.into_iter().collect::<Vec<_>>(), b);
     }
 
@@ -438,7 +457,7 @@ fn test_into_iter() {
             d.push_front(i);
         }
 
-        let b = vec![8,7,6,0,1,2,3,4];
+        let b = vec![8, 7, 6, 0, 1, 2, 3, 4];
         assert_eq!(d.into_iter().collect::<Vec<_>>(), b);
     }
 
@@ -502,7 +521,7 @@ fn test_drain() {
             d.push_front(i);
         }
 
-        assert_eq!(d.drain(..).collect::<Vec<_>>(), [8,7,6,0,1,2,3,4]);
+        assert_eq!(d.drain(..).collect::<Vec<_>>(), [8, 7, 6, 0, 1, 2, 3, 4]);
         assert!(d.is_empty());
     }
 
@@ -532,7 +551,7 @@ fn test_drain() {
 
 #[test]
 fn test_from_iter() {
-    let v = vec!(1,2,3,4,5,6,7);
+    let v = vec![1, 2, 3, 4, 5, 6, 7];
     let deq: VecDeque<_> = v.iter().cloned().collect();
     let u: Vec<_> = deq.iter().cloned().collect();
     assert_eq!(u, v);
@@ -540,7 +559,7 @@ fn test_from_iter() {
     let seq = (0..).step_by(2).take(256);
     let deq: VecDeque<_> = seq.collect();
     for (i, &x) in deq.iter().enumerate() {
-        assert_eq!(2*i, x);
+        assert_eq!(2 * i, x);
     }
     assert_eq!(deq.len(), 256);
 }
@@ -585,20 +604,20 @@ fn test_eq() {
 
 #[test]
 fn test_hash() {
-  let mut x = VecDeque::new();
-  let mut y = VecDeque::new();
+    let mut x = VecDeque::new();
+    let mut y = VecDeque::new();
 
-  x.push_back(1);
-  x.push_back(2);
-  x.push_back(3);
+    x.push_back(1);
+    x.push_back(2);
+    x.push_back(3);
 
-  y.push_back(0);
-  y.push_back(1);
-  y.pop_front();
-  y.push_back(2);
-  y.push_back(3);
+    y.push_back(0);
+    y.push_back(1);
+    y.pop_front();
+    y.push_back(2);
+    y.push_back(3);
 
-  assert!(::hash(&x) == ::hash(&y));
+    assert!(::hash(&x) == ::hash(&y));
 }
 
 #[test]
@@ -665,10 +684,12 @@ fn test_show() {
     let ringbuf: VecDeque<_> = (0..10).collect();
     assert_eq!(format!("{:?}", ringbuf), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
 
-    let ringbuf: VecDeque<_> = vec!["just", "one", "test", "more"].iter()
-                                                                    .cloned()
-                                                                    .collect();
-    assert_eq!(format!("{:?}", ringbuf), "[\"just\", \"one\", \"test\", \"more\"]");
+    let ringbuf: VecDeque<_> = vec!["just", "one", "test", "more"]
+                                   .iter()
+                                   .cloned()
+                                   .collect();
+    assert_eq!(format!("{:?}", ringbuf),
+               "[\"just\", \"one\", \"test\", \"more\"]");
 }
 
 #[test]
@@ -677,7 +698,9 @@ fn test_drop() {
     struct Elem;
     impl Drop for Elem {
         fn drop(&mut self) {
-            unsafe { drops += 1; }
+            unsafe {
+                drops += 1;
+            }
         }
     }
 
@@ -688,7 +711,7 @@ fn test_drop() {
     ring.push_front(Elem);
     drop(ring);
 
-    assert_eq!(unsafe {drops}, 4);
+    assert_eq!(unsafe { drops }, 4);
 }
 
 #[test]
@@ -697,7 +720,9 @@ fn test_drop_with_pop() {
     struct Elem;
     impl Drop for Elem {
         fn drop(&mut self) {
-            unsafe { drops += 1; }
+            unsafe {
+                drops += 1;
+            }
         }
     }
 
@@ -709,10 +734,10 @@ fn test_drop_with_pop() {
 
     drop(ring.pop_back());
     drop(ring.pop_front());
-    assert_eq!(unsafe {drops}, 2);
+    assert_eq!(unsafe { drops }, 2);
 
     drop(ring);
-    assert_eq!(unsafe {drops}, 4);
+    assert_eq!(unsafe { drops }, 4);
 }
 
 #[test]
@@ -721,7 +746,9 @@ fn test_drop_clear() {
     struct Elem;
     impl Drop for Elem {
         fn drop(&mut self) {
-            unsafe { drops += 1; }
+            unsafe {
+                drops += 1;
+            }
         }
     }
 
@@ -731,10 +758,10 @@ fn test_drop_clear() {
     ring.push_back(Elem);
     ring.push_front(Elem);
     ring.clear();
-    assert_eq!(unsafe {drops}, 4);
+    assert_eq!(unsafe { drops }, 4);
 
     drop(ring);
-    assert_eq!(unsafe {drops}, 4);
+    assert_eq!(unsafe { drops }, 4);
 }
 
 #[test]
@@ -822,7 +849,7 @@ fn test_get_mut() {
 
     match ring.get_mut(1) {
         Some(x) => *x = -1,
-        None => ()
+        None => (),
     };
 
     assert_eq!(ring.get_mut(0), Some(&mut 0));
@@ -852,13 +879,13 @@ fn test_front() {
 fn test_as_slices() {
     let mut ring: VecDeque<i32> = VecDeque::with_capacity(127);
     let cap = ring.capacity() as i32;
-    let first = cap/2;
-    let last  = cap - first;
+    let first = cap / 2;
+    let last = cap - first;
     for i in 0..first {
         ring.push_back(i);
 
         let (left, right) = ring.as_slices();
-        let expected: Vec<_> = (0..i+1).collect();
+        let expected: Vec<_> = (0..i + 1).collect();
         assert_eq!(left, &expected[..]);
         assert_eq!(right, []);
     }
@@ -866,7 +893,7 @@ fn test_as_slices() {
     for j in -last..0 {
         ring.push_front(j);
         let (left, right) = ring.as_slices();
-        let expected_left: Vec<_> = (-last..j+1).rev().collect();
+        let expected_left: Vec<_> = (-last..j + 1).rev().collect();
         let expected_right: Vec<_> = (0..first).collect();
         assert_eq!(left, &expected_left[..]);
         assert_eq!(right, &expected_right[..]);
@@ -880,13 +907,13 @@ fn test_as_slices() {
 fn test_as_mut_slices() {
     let mut ring: VecDeque<i32> = VecDeque::with_capacity(127);
     let cap = ring.capacity() as i32;
-    let first = cap/2;
-    let last  = cap - first;
+    let first = cap / 2;
+    let last = cap - first;
     for i in 0..first {
         ring.push_back(i);
 
         let (left, right) = ring.as_mut_slices();
-        let expected: Vec<_> = (0..i+1).collect();
+        let expected: Vec<_> = (0..i + 1).collect();
         assert_eq!(left, &expected[..]);
         assert_eq!(right, []);
     }
@@ -894,7 +921,7 @@ fn test_as_mut_slices() {
     for j in -last..0 {
         ring.push_front(j);
         let (left, right) = ring.as_mut_slices();
-        let expected_left: Vec<_> = (-last..j+1).rev().collect();
+        let expected_left: Vec<_> = (-last..j + 1).rev().collect();
         let expected_right: Vec<_> = (0..first).collect();
         assert_eq!(left, &expected_left[..]);
         assert_eq!(right, &expected_right[..]);
index 02fe574b81edd07ddfa66ed5c4474c02570e920b..3b406ac0447f22f7c8a164c337886dddac6468a4 100644 (file)
@@ -2,7 +2,6 @@
 authors = ["The Rust Project Developers"]
 name = "core"
 version = "0.0.0"
-build = "build.rs"
 
 [lib]
 name = "core"
index dfd2ba9154d53eceb71ec17fbb6ddf46c84a55f6..49304b1f3bfa17d23e632189bfbf942419658292 100644 (file)
@@ -85,7 +85,7 @@ use marker::{Reflect, Sized};
 
 /// A type to emulate dynamic typing.
 ///
-/// Every type with no non-`'static` references implements `Any`.
+/// Most types implement `Any`. However, any type which contains a non-`'static` reference does not.
 /// See the [module-level documentation][mod] for more details.
 ///
 /// [mod]: index.html
diff --git a/src/libcore/build.rs b/src/libcore/build.rs
deleted file mode 100644 (file)
index 255a367..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![deny(warnings)]
-
-fn main() {
-    // Remove this whenever snapshots and rustbuild nightlies are synced.
-    println!("cargo:rustc-cfg=cargobuild");
-    println!("cargo:rerun-if-changed=build.rs")
-}
index 4929088201deaa8f7400f634e4243b629d67733f..9435be3b0124b60bfb1fd36be9842558548ba449 100644 (file)
@@ -238,7 +238,7 @@ impl<T:Copy> Cell<T> {
     /// This call borrows `Cell` mutably (at compile-time) which guarantees
     /// that we possess the only reference.
     #[inline]
-    #[unstable(feature = "cell_get_mut", issue = "33444")]
+    #[stable(feature = "cell_get_mut", since = "1.11.0")]
     pub fn get_mut(&mut self) -> &mut T {
         unsafe {
             &mut *self.value.get()
@@ -509,7 +509,7 @@ impl<T: ?Sized> RefCell<T> {
     /// This call borrows `RefCell` mutably (at compile-time) so there is no
     /// need for dynamic checks.
     #[inline]
-    #[unstable(feature = "cell_get_mut", issue="33444")]
+    #[stable(feature = "cell_get_mut", since = "1.11.0")]
     pub fn get_mut(&mut self) -> &mut T {
         unsafe {
             &mut *self.value.get()
@@ -618,7 +618,9 @@ impl<'b> Clone for BorrowRef<'b> {
         // Since this Ref exists, we know the borrow flag
         // is not set to WRITING.
         let borrow = self.borrow.get();
-        debug_assert!(borrow != WRITING && borrow != UNUSED);
+        debug_assert!(borrow != UNUSED);
+        // Prevent the borrow counter from overflowing.
+        assert!(borrow != WRITING);
         self.borrow.set(borrow + 1);
         BorrowRef { borrow: self.borrow }
     }
@@ -692,40 +694,6 @@ impl<'b, T: ?Sized> Ref<'b, T> {
             borrow: orig.borrow,
         }
     }
-
-    /// Make a new `Ref` for an optional component of the borrowed data, e.g. an
-    /// enum variant.
-    ///
-    /// The `RefCell` is already immutably borrowed, so this cannot fail.
-    ///
-    /// This is an associated function that needs to be used as
-    /// `Ref::filter_map(...)`.  A method would interfere with methods of the
-    /// same name on the contents of a `RefCell` used through `Deref`.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// # #![feature(cell_extras)]
-    /// use std::cell::{RefCell, Ref};
-    ///
-    /// let c = RefCell::new(Ok(5));
-    /// let b1: Ref<Result<u32, ()>> = c.borrow();
-    /// let b2: Ref<u32> = Ref::filter_map(b1, |o| o.as_ref().ok()).unwrap();
-    /// assert_eq!(*b2, 5)
-    /// ```
-    #[unstable(feature = "cell_extras", reason = "recently added",
-               issue = "27746")]
-    #[rustc_deprecated(since = "1.8.0", reason = "can be built on `Ref::map`: \
-        https://crates.io/crates/ref_filter_map")]
-    #[inline]
-    pub fn filter_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Option<Ref<'b, U>>
-        where F: FnOnce(&T) -> Option<&U>
-    {
-        f(orig.value).map(move |new| Ref {
-            value: new,
-            borrow: orig.borrow,
-        })
-    }
 }
 
 #[unstable(feature = "coerce_unsized", issue = "27732")]
@@ -765,47 +733,6 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
             borrow: orig.borrow,
         }
     }
-
-    /// Make a new `RefMut` for an optional component of the borrowed data, e.g.
-    /// an enum variant.
-    ///
-    /// The `RefCell` is already mutably borrowed, so this cannot fail.
-    ///
-    /// This is an associated function that needs to be used as
-    /// `RefMut::filter_map(...)`.  A method would interfere with methods of the
-    /// same name on the contents of a `RefCell` used through `Deref`.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// # #![feature(cell_extras)]
-    /// use std::cell::{RefCell, RefMut};
-    ///
-    /// let c = RefCell::new(Ok(5));
-    /// {
-    ///     let b1: RefMut<Result<u32, ()>> = c.borrow_mut();
-    ///     let mut b2: RefMut<u32> = RefMut::filter_map(b1, |o| {
-    ///         o.as_mut().ok()
-    ///     }).unwrap();
-    ///     assert_eq!(*b2, 5);
-    ///     *b2 = 42;
-    /// }
-    /// assert_eq!(*c.borrow(), Ok(42));
-    /// ```
-    #[unstable(feature = "cell_extras", reason = "recently added",
-               issue = "27746")]
-    #[rustc_deprecated(since = "1.8.0", reason = "can be built on `RefMut::map`: \
-        https://crates.io/crates/ref_filter_map")]
-    #[inline]
-    pub fn filter_map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> Option<RefMut<'b, U>>
-        where F: FnOnce(&mut T) -> Option<&mut U>
-    {
-        let RefMut { value, borrow } = orig;
-        f(value).map(move |new| RefMut {
-            value: new,
-            borrow: borrow,
-        })
-    }
 }
 
 struct BorrowRefMut<'b> {
index 6a2331dddcf0e80e30260bf6bdac9c24ad8ffc0d..d80b456181ae475d8e53651b30b2aefab89a38a5 100644 (file)
@@ -411,14 +411,17 @@ pub struct EscapeUnicode {
     hex_digit_idx: usize,
 }
 
+// The enum values are ordered so that their representation is the
+// same as the remaining length (besides the hexadecimal digits). This
+// likely makes `len()` a single load from memory) and inline-worth.
 #[derive(Clone, Debug)]
 enum EscapeUnicodeState {
-    Backslash,
-    Type,
-    LeftBrace,
-    Value,
-    RightBrace,
     Done,
+    RightBrace,
+    Value,
+    LeftBrace,
+    Type,
+    Backslash,
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -457,19 +460,17 @@ impl Iterator for EscapeUnicode {
         }
     }
 
+    #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let n = match self.state {
-            EscapeUnicodeState::Backslash => 5,
-            EscapeUnicodeState::Type => 4,
-            EscapeUnicodeState::LeftBrace => 3,
-            EscapeUnicodeState::Value => 2,
-            EscapeUnicodeState::RightBrace => 1,
-            EscapeUnicodeState::Done => 0,
-        };
-        let n = n + self.hex_digit_idx;
+        let n = self.len();
         (n, Some(n))
     }
 
+    #[inline]
+    fn count(self) -> usize {
+        self.len()
+    }
+
     fn last(self) -> Option<char> {
         match self.state {
             EscapeUnicodeState::Done => None,
@@ -483,6 +484,22 @@ impl Iterator for EscapeUnicode {
     }
 }
 
+#[stable(feature = "exact_size_escape", since = "1.11.0")]
+impl ExactSizeIterator for EscapeUnicode {
+    #[inline]
+    fn len(&self) -> usize {
+        // The match is a single memory access with no branching
+        self.hex_digit_idx + match self.state {
+            EscapeUnicodeState::Done => 0,
+            EscapeUnicodeState::RightBrace => 1,
+            EscapeUnicodeState::Value => 2,
+            EscapeUnicodeState::LeftBrace => 3,
+            EscapeUnicodeState::Type => 4,
+            EscapeUnicodeState::Backslash => 5,
+        }
+    }
+}
+
 /// An iterator that yields the literal escape code of a `char`.
 ///
 /// This `struct` is created by the [`escape_default()`] method on [`char`]. See
@@ -498,9 +515,9 @@ pub struct EscapeDefault {
 
 #[derive(Clone, Debug)]
 enum EscapeDefaultState {
-    Backslash(char),
-    Char(char),
     Done,
+    Char(char),
+    Backslash(char),
     Unicode(EscapeUnicode),
 }
 
@@ -523,22 +540,15 @@ impl Iterator for EscapeDefault {
         }
     }
 
+    #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        match self.state {
-            EscapeDefaultState::Char(_) => (1, Some(1)),
-            EscapeDefaultState::Backslash(_) => (2, Some(2)),
-            EscapeDefaultState::Unicode(ref iter) => iter.size_hint(),
-            EscapeDefaultState::Done => (0, Some(0)),
-        }
+        let n = self.len();
+        (n, Some(n))
     }
 
+    #[inline]
     fn count(self) -> usize {
-        match self.state {
-            EscapeDefaultState::Char(_) => 1,
-            EscapeDefaultState::Unicode(iter) => iter.count(),
-            EscapeDefaultState::Done => 0,
-            EscapeDefaultState::Backslash(_) => 2,
-        }
+        self.len()
     }
 
     fn nth(&mut self, n: usize) -> Option<char> {
@@ -578,6 +588,18 @@ impl Iterator for EscapeDefault {
     }
 }
 
+#[stable(feature = "exact_size_escape", since = "1.11.0")]
+impl ExactSizeIterator for EscapeDefault {
+    fn len(&self) -> usize {
+        match self.state {
+            EscapeDefaultState::Done => 0,
+            EscapeDefaultState::Char(_) => 1,
+            EscapeDefaultState::Backslash(_) => 2,
+            EscapeDefaultState::Unicode(ref iter) => iter.len(),
+        }
+    }
+}
+
 /// An iterator over `u8` entries represending the UTF-8 encoding of a `char`
 /// value.
 ///
index e8ea993c6940a33c9caa0ae37c01bf82fa9c0f8d..e8cd36f3cd70bf13d751790bcbefdb2e9a16a937 100644 (file)
 
 use marker::Sized;
 
-/// A common trait for cloning an object.
+/// A common trait for the ability to explicitly duplicate an object.
 ///
-/// This trait can be used with `#[derive]`.
+/// Differs from `Copy` in that `Copy` is implicit and extremely inexpensive, while
+/// `Clone` is always explicit and may or may not be expensive. In order to enforce
+/// these characteristics, Rust does not allow you to reimplement `Copy`, but you
+/// may reimplement `Clone` and run arbitrary code.
+///
+/// Since `Clone` is more general than `Copy`, you can automatically make anything
+/// `Copy` be `Clone` as well.
+///
+/// ## Derivable
+///
+/// This trait can be used with `#[derive]` if all fields are `Clone`. The `derive`d
+/// implementation of `clone()` calls `clone()` on each field.
+///
+/// ## How can I implement `Clone`?
 ///
 /// Types that are `Copy` should have a trivial implementation of `Clone`. More formally:
 /// if `T: Copy`, `x: T`, and `y: &T`, then `let x = y.clone();` is equivalent to `let x = *y;`.
 /// Manual implementations should be careful to uphold this invariant; however, unsafe code
 /// must not rely on it to ensure memory safety.
+///
+/// An example is an array holding more than 32 elements of a type that is `Clone`; the standard
+/// library only implements `Clone` up until arrays of size 32. In this case, the implementation of
+/// `Clone` cannot be `derive`d, but can be implemented as:
+///
+/// ```
+/// #[derive(Copy)]
+/// struct Stats {
+///    frequencies: [i32; 100],
+/// }
+///
+/// impl Clone for Stats {
+///     fn clone(&self) -> Stats { *self }
+/// }
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Clone : Sized {
     /// Returns a copy of the value.
index d3481ba3f0523079270fb21ebea95f2416383992..8764766b2ef8675a04ea69be0b5bc3209fa162c1 100644 (file)
@@ -53,12 +53,43 @@ use option::Option::{self, Some};
 /// symmetrically and transitively: if `T: PartialEq<U>` and `U: PartialEq<V>`
 /// then `U: PartialEq<T>` and `T: PartialEq<V>`.
 ///
+/// ## Derivable
+///
+/// This trait can be used with `#[derive]`. When `derive`d on structs, two
+/// instances are equal if all fields are equal, and not equal if any fields
+/// are not equal. When `derive`d on enums, each variant is equal to itself
+/// and not equal to the other variants.
+///
+/// ## How can I implement `PartialEq`?
+///
 /// PartialEq only requires the `eq` method to be implemented; `ne` is defined
 /// in terms of it by default. Any manual implementation of `ne` *must* respect
 /// the rule that `eq` is a strict inverse of `ne`; that is, `!(a == b)` if and
 /// only if `a != b`.
 ///
-/// This trait can be used with `#[derive]`.
+/// An example implementation for a domain in which two books are considered
+/// the same book if their ISBN matches, even if the formats differ:
+///
+/// ```
+/// enum BookFormat { Paperback, Hardback, Ebook }
+/// struct Book {
+///     isbn: i32,
+///     format: BookFormat,
+/// }
+///
+/// impl PartialEq for Book {
+///     fn eq(&self, other: &Book) -> bool {
+///         self.isbn == other.isbn
+///     }
+/// }
+///
+/// let b1 = Book { isbn: 3, format: BookFormat::Paperback };
+/// let b2 = Book { isbn: 3, format: BookFormat::Ebook };
+/// let b3 = Book { isbn: 10, format: BookFormat::Paperback };
+///
+/// assert!(b1 == b2);
+/// assert!(b1 != b3);
+/// ```
 ///
 /// # Examples
 ///
@@ -96,7 +127,32 @@ pub trait PartialEq<Rhs: ?Sized = Self> {
 /// This property cannot be checked by the compiler, and therefore `Eq` implies
 /// `PartialEq`, and has no extra methods.
 ///
-/// This trait can be used with `#[derive]`.
+/// ## Derivable
+///
+/// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has
+/// no extra methods, it is only informing the compiler that this is an
+/// equivalence relation rather than a partial equivalence relation. Note that
+/// the `derive` strategy requires all fields are `PartialEq`, which isn't
+/// always desired.
+///
+/// ## How can I implement `Eq`?
+///
+/// If you cannot use the `derive` strategy, specify that your type implements
+/// `Eq`, which has no methods:
+///
+/// ```
+/// enum BookFormat { Paperback, Hardback, Ebook }
+/// struct Book {
+///     isbn: i32,
+///     format: BookFormat,
+/// }
+/// impl PartialEq for Book {
+///     fn eq(&self, other: &Book) -> bool {
+///         self.isbn == other.isbn
+///     }
+/// }
+/// impl Eq for Book {}
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Eq: PartialEq<Self> {
     // FIXME #13101: this method is used solely by #[deriving] to
@@ -190,8 +246,49 @@ impl Ordering {
 /// - total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true; and
 /// - transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
 ///
+/// ## Derivable
+///
 /// This trait can be used with `#[derive]`. When `derive`d, it will produce a lexicographic
 /// ordering based on the top-to-bottom declaration order of the struct's members.
+///
+/// ## How can I implement `Ord`?
+///
+/// `Ord` requires that the type also be `PartialOrd` and `Eq` (which requires `PartialEq`).
+///
+/// Then you must define an implementation for `cmp()`. You may find it useful to use
+/// `cmp()` on your type's fields.
+///
+/// Here's an example where you want to sort people by height only, disregarding `id`
+/// and `name`:
+///
+/// ```
+/// use std::cmp::Ordering;
+///
+/// #[derive(Eq)]
+/// struct Person {
+///     id: u32,
+///     name: String,
+///     height: u32,
+/// }
+///
+/// impl Ord for Person {
+///     fn cmp(&self, other: &Person) -> Ordering {
+///         self.height.cmp(&other.height)
+///     }
+/// }
+///
+/// impl PartialOrd for Person {
+///     fn partial_cmp(&self, other: &Person) -> Option<Ordering> {
+///         Some(self.cmp(other))
+///     }
+/// }
+///
+/// impl PartialEq for Person {
+///     fn eq(&self, other: &Person) -> bool {
+///         self.height == other.height
+///     }
+/// }
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Ord: Eq + PartialOrd<Self> {
     /// This method returns an `Ordering` between `self` and `other`.
@@ -242,6 +339,13 @@ impl PartialOrd for Ordering {
 /// transitively: if `T: PartialOrd<U>` and `U: PartialOrd<V>` then `U: PartialOrd<T>` and `T:
 /// PartialOrd<V>`.
 ///
+/// ## Derivable
+///
+/// This trait can be used with `#[derive]`. When `derive`d, it will produce a lexicographic
+/// ordering based on the top-to-bottom declaration order of the struct's members.
+///
+/// ## How can I implement `Ord`?
+///
 /// PartialOrd only requires implementation of the `partial_cmp` method, with the others generated
 /// from default implementations.
 ///
@@ -249,8 +353,64 @@ impl PartialOrd for Ordering {
 /// total order. For example, for floating point numbers, `NaN < 0 == false` and `NaN >= 0 ==
 /// false` (cf. IEEE 754-2008 section 5.11).
 ///
-/// This trait can be used with `#[derive]`. When `derive`d, it will produce an ordering
-/// based on the top-to-bottom declaration order of the struct's members.
+/// `PartialOrd` requires your type to be `PartialEq`.
+///
+/// If your type is `Ord`, you can implement `partial_cmp()` by using `cmp()`:
+///
+/// ```
+/// use std::cmp::Ordering;
+///
+/// #[derive(Eq)]
+/// struct Person {
+///     id: u32,
+///     name: String,
+///     height: u32,
+/// }
+///
+/// impl PartialOrd for Person {
+///     fn partial_cmp(&self, other: &Person) -> Option<Ordering> {
+///         Some(self.cmp(other))
+///     }
+/// }
+///
+/// impl Ord for Person {
+///     fn cmp(&self, other: &Person) -> Ordering {
+///         self.height.cmp(&other.height)
+///     }
+/// }
+///
+/// impl PartialEq for Person {
+///     fn eq(&self, other: &Person) -> bool {
+///         self.height == other.height
+///     }
+/// }
+/// ```
+///
+/// You may also find it useful to use `partial_cmp()` on your type`s fields. Here
+/// is an example of `Person` types who have a floating-point `height` field that
+/// is the only field to be used for sorting:
+///
+/// ```
+/// use std::cmp::Ordering;
+///
+/// struct Person {
+///     id: u32,
+///     name: String,
+///     height: f64,
+/// }
+///
+/// impl PartialOrd for Person {
+///     fn partial_cmp(&self, other: &Person) -> Option<Ordering> {
+///         self.height.partial_cmp(&other.height)
+///     }
+/// }
+///
+/// impl PartialEq for Person {
+///     fn eq(&self, other: &Person) -> bool {
+///         self.height == other.height
+///     }
+/// }
+/// ```
 ///
 /// # Examples
 ///
index 12c4a5ca200ad3b1c7271cc6baff8878764800b0..485ddae07fbff93b4723c3b9ffe8c45f93028e6c 100644 (file)
@@ -9,76 +9,6 @@
 // except according to those terms.
 
 //! The `Default` trait for types which may have meaningful default values.
-//!
-//! Sometimes, you want to fall back to some kind of default value, and
-//! don't particularly care what it is. This comes up often with `struct`s
-//! that define a set of options:
-//!
-//! ```
-//! # #[allow(dead_code)]
-//! struct SomeOptions {
-//!     foo: i32,
-//!     bar: f32,
-//! }
-//! ```
-//!
-//! How can we define some default values? You can use `Default`:
-//!
-//! ```
-//! # #[allow(dead_code)]
-//! #[derive(Default)]
-//! struct SomeOptions {
-//!     foo: i32,
-//!     bar: f32,
-//! }
-//!
-//!
-//! fn main() {
-//!     let options: SomeOptions = Default::default();
-//! }
-//! ```
-//!
-//! Now, you get all of the default values. Rust implements `Default` for various primitives types.
-//! If you have your own type, you need to implement `Default` yourself:
-//!
-//! ```
-//! # #![allow(dead_code)]
-//! enum Kind {
-//!     A,
-//!     B,
-//!     C,
-//! }
-//!
-//! impl Default for Kind {
-//!     fn default() -> Kind { Kind::A }
-//! }
-//!
-//! #[derive(Default)]
-//! struct SomeOptions {
-//!     foo: i32,
-//!     bar: f32,
-//!     baz: Kind,
-//! }
-//!
-//!
-//! fn main() {
-//!     let options: SomeOptions = Default::default();
-//! }
-//! ```
-//!
-//! If you want to override a particular option, but still retain the other defaults:
-//!
-//! ```
-//! # #[allow(dead_code)]
-//! # #[derive(Default)]
-//! # struct SomeOptions {
-//! #     foo: i32,
-//! #     bar: f32,
-//! # }
-//! fn main() {
-//!     let options = SomeOptions { foo: 42, ..Default::default() };
-//! }
-//! ```
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
@@ -86,8 +16,72 @@ use marker::Sized;
 
 /// A trait for giving a type a useful default value.
 ///
-/// A struct can derive default implementations of `Default` for basic types using
-/// `#[derive(Default)]`.
+/// Sometimes, you want to fall back to some kind of default value, and
+/// don't particularly care what it is. This comes up often with `struct`s
+/// that define a set of options:
+///
+/// ```
+/// # #[allow(dead_code)]
+/// struct SomeOptions {
+///     foo: i32,
+///     bar: f32,
+/// }
+/// ```
+///
+/// How can we define some default values? You can use `Default`:
+///
+/// ```
+/// # #[allow(dead_code)]
+/// #[derive(Default)]
+/// struct SomeOptions {
+///     foo: i32,
+///     bar: f32,
+/// }
+///
+///
+/// fn main() {
+///     let options: SomeOptions = Default::default();
+/// }
+/// ```
+///
+/// Now, you get all of the default values. Rust implements `Default` for various primitives types.
+///
+/// If you want to override a particular option, but still retain the other defaults:
+///
+/// ```
+/// # #[allow(dead_code)]
+/// # #[derive(Default)]
+/// # struct SomeOptions {
+/// #     foo: i32,
+/// #     bar: f32,
+/// # }
+/// fn main() {
+///     let options = SomeOptions { foo: 42, ..Default::default() };
+/// }
+/// ```
+///
+/// ## Derivable
+///
+/// This trait can be used with `#[derive]` if all of the type's fields implement
+/// `Default`. When `derive`d, it will use the default value for each field's type.
+///
+/// ## How can I implement `Default`?
+///
+/// Provide an implementation for the `default()` method that returns the value of
+/// your type that should be the default:
+///
+/// ```
+/// # #![allow(dead_code)]
+/// enum Kind {
+///     A,
+///     B,
+///     C,
+/// }
+///
+/// impl Default for Kind {
+///     fn default() -> Kind { Kind::A }
+/// }
+/// ```
 ///
 /// # Examples
 ///
index dde4d03dad8ac01bcfcd6e570c9ca19df2153c8b..895a679fc3dccf76b1d7efae56248e31733b3f54 100644 (file)
@@ -318,7 +318,11 @@ impl<'a> Display for Arguments<'a> {
 ///
 /// [module]: ../../std/fmt/index.html
 ///
-/// This trait can be used with `#[derive]`.
+/// This trait can be used with `#[derive]` if all fields implement `Debug`. When
+/// `derive`d for structs, it will use the name of the `struct`, then `{`, then a
+/// comma-separated list of each field's name and `Debug` value, then `}`. For
+/// `enum`s, it will use the name of the variant and, if applicable, `(`, then the
+/// `Debug` values of the fields, then `)`.
 ///
 /// # Examples
 ///
@@ -836,11 +840,8 @@ pub fn write(output: &mut Write, args: Arguments) -> Result {
     }
 
     // There can be only one trailing string piece left.
-    match pieces.next() {
-        Some(piece) => {
-            formatter.buf.write_str(*piece)?;
-        }
-        None => {}
+    if let Some(piece) = pieces.next() {
+        formatter.buf.write_str(*piece)?;
     }
 
     Ok(())
@@ -1380,7 +1381,7 @@ impl Debug for str {
         for (i, c) in self.char_indices() {
             let esc = c.escape_default();
             // If char needs escaping, flush backlog so far and write, else skip
-            if esc.size_hint() != (1, Some(1)) {
+            if esc.len() != 1 {
                 f.write_str(&self[from..i])?;
                 for c in esc {
                     f.write_char(c)?;
index a944c996c1a1e7532e0aeebde83bbbfc05c182e8..d55e0317a949218af2cca35aebd36da2997d1dc3 100644 (file)
@@ -29,6 +29,7 @@ trait Int: Zero + PartialEq + PartialOrd + Div<Output=Self> + Rem<Output=Self> +
            Sub<Output=Self> + Copy {
     fn from_u8(u: u8) -> Self;
     fn to_u8(&self) -> u8;
+    fn to_u16(&self) -> u16;
     fn to_u32(&self) -> u32;
     fn to_u64(&self) -> u64;
 }
@@ -37,6 +38,7 @@ macro_rules! doit {
     ($($t:ident)*) => ($(impl Int for $t {
         fn from_u8(u: u8) -> $t { u as $t }
         fn to_u8(&self) -> u8 { *self as u8 }
+        fn to_u16(&self) -> u16 { *self as u16 }
         fn to_u32(&self) -> u32 { *self as u32 }
         fn to_u64(&self) -> u64 { *self as u64 }
     })*)
@@ -256,6 +258,8 @@ macro_rules! impl_Display {
 
 impl_Display!(i8, u8, i16, u16, i32, u32: to_u32);
 impl_Display!(i64, u64: to_u64);
+#[cfg(target_pointer_width = "16")]
+impl_Display!(isize, usize: to_u16);
 #[cfg(target_pointer_width = "32")]
 impl_Display!(isize, usize: to_u32);
 #[cfg(target_pointer_width = "64")]
index 4d0fed983343611f32a57a5591275b3e2a606304..9e3f7a4a84a814b318a9833f18fb6352fca5b1e3 100644 (file)
@@ -38,7 +38,7 @@
 //! ```
 //!
 //! If you need more control over how a value is hashed, you need to implement
-//! the trait `Hash`:
+//! the `Hash` trait:
 //!
 //! ```rust
 //! use std::hash::{Hash, Hasher, SipHasher};
@@ -80,6 +80,9 @@ use mem;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::sip::SipHasher;
 
+#[unstable(feature = "sip_hash_13", issue = "29754")]
+pub use self::sip::{SipHasher13, SipHasher24};
+
 mod sip;
 
 /// A hashable type.
@@ -97,7 +100,33 @@ mod sip;
 /// In other words, if two keys are equal, their hashes should also be equal.
 /// `HashMap` and `HashSet` both rely on this behavior.
 ///
-/// This trait can be used with `#[derive]`.
+/// ## Derivable
+///
+/// This trait can be used with `#[derive]` if all fields implement `Hash`.
+/// When `derive`d, the resulting hash will be the combination of the values
+/// from calling `.hash()` on each field.
+///
+/// ## How can I implement `Hash`?
+///
+/// If you need more control over how a value is hashed, you need to implement
+/// the `Hash` trait:
+///
+/// ```
+/// use std::hash::{Hash, Hasher};
+///
+/// struct Person {
+///     id: u32,
+///     name: String,
+///     phone: u64,
+/// }
+///
+/// impl Hash for Person {
+///     fn hash<H: Hasher>(&self, state: &mut H) {
+///         self.id.hash(state);
+///         self.phone.hash(state);
+///     }
+/// }
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Hash {
     /// Feeds this value into the state given, updating the hasher as necessary.
index fd1dab7a1f043b141cf6367ed980c1a3b2cf4a4b..c52c0b0730be785d8d8aad84bdd68577df4023e3 100644 (file)
@@ -8,12 +8,30 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! An implementation of SipHash 2-4.
+//! An implementation of SipHash.
 
 use prelude::v1::*;
 
+use marker::PhantomData;
 use ptr;
-use super::Hasher;
+
+/// An implementation of SipHash 1-3.
+///
+/// See: https://131002.net/siphash/
+#[unstable(feature = "sip_hash_13", issue = "29754")]
+#[derive(Debug, Clone, Default)]
+pub struct SipHasher13 {
+    hasher: Hasher<Sip13Rounds>,
+}
+
+/// An implementation of SipHash 2-4.
+///
+/// See: https://131002.net/siphash/
+#[unstable(feature = "sip_hash_13", issue = "29754")]
+#[derive(Debug, Clone, Default)]
+pub struct SipHasher24 {
+    hasher: Hasher<Sip24Rounds>,
+}
 
 /// An implementation of SipHash 2-4.
 ///
@@ -30,22 +48,31 @@ use super::Hasher;
 /// Although the SipHash algorithm is considered to be generally strong,
 /// it is not intended for cryptographic purposes. As such, all
 /// cryptographic uses of this implementation are _strongly discouraged_.
-#[derive(Debug)]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub struct SipHasher {
+#[derive(Debug, Clone, Default)]
+pub struct SipHasher(SipHasher24);
+
+#[derive(Debug)]
+struct Hasher<S: Sip> {
     k0: u64,
     k1: u64,
     length: usize, // how many bytes we've processed
+    state: State, // hash State
+    tail: u64, // unprocessed bytes le
+    ntail: usize, // how many bytes in tail are valid
+    _marker: PhantomData<S>,
+}
+
+#[derive(Debug, Clone, Copy)]
+struct State {
     // v0, v2 and v1, v3 show up in pairs in the algorithm,
     // and simd implementations of SipHash will use vectors
     // of v02 and v13. By placing them in this order in the struct,
     // the compiler can pick up on just a few simd optimizations by itself.
-    v0: u64, // hash state
+    v0: u64,
     v2: u64,
     v1: u64,
     v3: u64,
-    tail: u64, // unprocessed bytes le
-    ntail: usize, // how many bytes in tail are valid
 }
 
 // sadly, these macro definitions can't appear later,
@@ -93,6 +120,9 @@ macro_rules! rotl {
 }
 
 macro_rules! compress {
+    ($state:expr) => ({
+        compress!($state.v0, $state.v1, $state.v2, $state.v3)
+    });
     ($v0:expr, $v1:expr, $v2:expr, $v3:expr) =>
     ({
         $v0 = $v0.wrapping_add($v1); $v1 = rotl!($v1, 13); $v1 ^= $v0;
@@ -101,7 +131,7 @@ macro_rules! compress {
         $v0 = $v0.wrapping_add($v3); $v3 = rotl!($v3, 21); $v3 ^= $v0;
         $v2 = $v2.wrapping_add($v1); $v1 = rotl!($v1, 17); $v1 ^= $v2;
         $v2 = rotl!($v2, 32);
-    })
+    });
 }
 
 impl SipHasher {
@@ -116,16 +146,63 @@ impl SipHasher {
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher {
-        let mut state = SipHasher {
+        SipHasher(SipHasher24::new_with_keys(key0, key1))
+    }
+}
+
+
+impl SipHasher13 {
+    /// Creates a new `SipHasher13` with the two initial keys set to 0.
+    #[inline]
+    #[unstable(feature = "sip_hash_13", issue = "29754")]
+    pub fn new() -> SipHasher13 {
+        SipHasher13::new_with_keys(0, 0)
+    }
+
+    /// Creates a `SipHasher13` that is keyed off the provided keys.
+    #[inline]
+    #[unstable(feature = "sip_hash_13", issue = "29754")]
+    pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 {
+        SipHasher13 {
+            hasher: Hasher::new_with_keys(key0, key1)
+        }
+    }
+}
+
+impl SipHasher24 {
+    /// Creates a new `SipHasher24` with the two initial keys set to 0.
+    #[inline]
+    #[unstable(feature = "sip_hash_13", issue = "29754")]
+    pub fn new() -> SipHasher24 {
+        SipHasher24::new_with_keys(0, 0)
+    }
+
+    /// Creates a `SipHasher24` that is keyed off the provided keys.
+    #[inline]
+    #[unstable(feature = "sip_hash_13", issue = "29754")]
+    pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher24 {
+        SipHasher24 {
+            hasher: Hasher::new_with_keys(key0, key1)
+        }
+    }
+}
+
+impl<S: Sip> Hasher<S> {
+    #[inline]
+    fn new_with_keys(key0: u64, key1: u64) -> Hasher<S> {
+        let mut state = Hasher {
             k0: key0,
             k1: key1,
             length: 0,
-            v0: 0,
-            v1: 0,
-            v2: 0,
-            v3: 0,
+            state: State {
+                v0: 0,
+                v1: 0,
+                v2: 0,
+                v3: 0,
+            },
             tail: 0,
             ntail: 0,
+            _marker: PhantomData,
         };
         state.reset();
         state
@@ -134,16 +211,54 @@ impl SipHasher {
     #[inline]
     fn reset(&mut self) {
         self.length = 0;
-        self.v0 = self.k0 ^ 0x736f6d6570736575;
-        self.v1 = self.k1 ^ 0x646f72616e646f6d;
-        self.v2 = self.k0 ^ 0x6c7967656e657261;
-        self.v3 = self.k1 ^ 0x7465646279746573;
+        self.state.v0 = self.k0 ^ 0x736f6d6570736575;
+        self.state.v1 = self.k1 ^ 0x646f72616e646f6d;
+        self.state.v2 = self.k0 ^ 0x6c7967656e657261;
+        self.state.v3 = self.k1 ^ 0x7465646279746573;
         self.ntail = 0;
     }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl Hasher for SipHasher {
+impl super::Hasher for SipHasher {
+    #[inline]
+    fn write(&mut self, msg: &[u8]) {
+        self.0.write(msg)
+    }
+
+    #[inline]
+    fn finish(&self) -> u64 {
+        self.0.finish()
+    }
+}
+
+#[unstable(feature = "sip_hash_13", issue = "29754")]
+impl super::Hasher for SipHasher13 {
+    #[inline]
+    fn write(&mut self, msg: &[u8]) {
+        self.hasher.write(msg)
+    }
+
+    #[inline]
+    fn finish(&self) -> u64 {
+        self.hasher.finish()
+    }
+}
+
+#[unstable(feature = "sip_hash_13", issue = "29754")]
+impl super::Hasher for SipHasher24 {
+    #[inline]
+    fn write(&mut self, msg: &[u8]) {
+        self.hasher.write(msg)
+    }
+
+    #[inline]
+    fn finish(&self) -> u64 {
+        self.hasher.finish()
+    }
+}
+
+impl<S: Sip> super::Hasher for Hasher<S> {
     #[inline]
     fn write(&mut self, msg: &[u8]) {
         let length = msg.len();
@@ -161,10 +276,9 @@ impl Hasher for SipHasher {
 
             let m = self.tail | u8to64_le!(msg, 0, needed) << 8 * self.ntail;
 
-            self.v3 ^= m;
-            compress!(self.v0, self.v1, self.v2, self.v3);
-            compress!(self.v0, self.v1, self.v2, self.v3);
-            self.v0 ^= m;
+            self.state.v3 ^= m;
+            S::c_rounds(&mut self.state);
+            self.state.v0 ^= m;
 
             self.ntail = 0;
         }
@@ -177,10 +291,9 @@ impl Hasher for SipHasher {
         while i < len - left {
             let mi = unsafe { load_u64_le(msg, i) };
 
-            self.v3 ^= mi;
-            compress!(self.v0, self.v1, self.v2, self.v3);
-            compress!(self.v0, self.v1, self.v2, self.v3);
-            self.v0 ^= mi;
+            self.state.v3 ^= mi;
+            S::c_rounds(&mut self.state);
+            self.state.v0 ^= mi;
 
             i += 8;
         }
@@ -191,49 +304,81 @@ impl Hasher for SipHasher {
 
     #[inline]
     fn finish(&self) -> u64 {
-        let mut v0 = self.v0;
-        let mut v1 = self.v1;
-        let mut v2 = self.v2;
-        let mut v3 = self.v3;
+        let mut state = self.state;
 
         let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail;
 
-        v3 ^= b;
-        compress!(v0, v1, v2, v3);
-        compress!(v0, v1, v2, v3);
-        v0 ^= b;
+        state.v3 ^= b;
+        S::c_rounds(&mut state);
+        state.v0 ^= b;
 
-        v2 ^= 0xff;
-        compress!(v0, v1, v2, v3);
-        compress!(v0, v1, v2, v3);
-        compress!(v0, v1, v2, v3);
-        compress!(v0, v1, v2, v3);
+        state.v2 ^= 0xff;
+        S::d_rounds(&mut state);
 
-        v0 ^ v1 ^ v2 ^ v3
+        state.v0 ^ state.v1 ^ state.v2 ^ state.v3
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Clone for SipHasher {
+impl<S: Sip> Clone for Hasher<S> {
     #[inline]
-    fn clone(&self) -> SipHasher {
-        SipHasher {
+    fn clone(&self) -> Hasher<S> {
+        Hasher {
             k0: self.k0,
             k1: self.k1,
             length: self.length,
-            v0: self.v0,
-            v1: self.v1,
-            v2: self.v2,
-            v3: self.v3,
+            state: self.state,
             tail: self.tail,
             ntail: self.ntail,
+            _marker: self._marker,
         }
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Default for SipHasher {
-    fn default() -> SipHasher {
-        SipHasher::new()
+impl<S: Sip> Default for Hasher<S> {
+    #[inline]
+    fn default() -> Hasher<S> {
+        Hasher::new_with_keys(0, 0)
+    }
+}
+
+#[doc(hidden)]
+trait Sip {
+    fn c_rounds(&mut State);
+    fn d_rounds(&mut State);
+}
+
+#[derive(Debug, Clone, Default)]
+struct Sip13Rounds;
+
+impl Sip for Sip13Rounds {
+    #[inline]
+    fn c_rounds(state: &mut State) {
+        compress!(state);
+    }
+
+    #[inline]
+    fn d_rounds(state: &mut State) {
+        compress!(state);
+        compress!(state);
+        compress!(state);
+    }
+}
+
+#[derive(Debug, Clone, Default)]
+struct Sip24Rounds;
+
+impl Sip for Sip24Rounds {
+    #[inline]
+    fn c_rounds(state: &mut State) {
+        compress!(state);
+        compress!(state);
+    }
+
+    #[inline]
+    fn d_rounds(state: &mut State) {
+        compress!(state);
+        compress!(state);
+        compress!(state);
+        compress!(state);
     }
 }
index 8a9f662bf83aa08d537cbf2931e1e0abbb027c8c..94baf188bcaeea9d28f582e6071fed9eb30c8afc 100644 (file)
@@ -10,7 +10,7 @@
 
 //! rustc compiler intrinsics.
 //!
-//! The corresponding definitions are in librustc_trans/trans/intrinsic.rs.
+//! The corresponding definitions are in librustc_trans/intrinsic.rs.
 //!
 //! # Volatiles
 //!
@@ -168,6 +168,16 @@ extern "rust-intrinsic" {
     pub fn atomic_singlethreadfence_rel();
     pub fn atomic_singlethreadfence_acqrel();
 
+    /// Magic intrinsic that derives its meaning from attributes
+    /// attached to the function.
+    ///
+    /// For example, dataflow uses this to inject static assertions so
+    /// that `rustc_peek(potentially_uninitialized)` would actually
+    /// double-check that dataflow did indeed compute that it is
+    /// uninitialized at that point in the control flow.
+    #[cfg(not(stage0))]
+    pub fn rustc_peek<T>(_: T) -> T;
+
     /// Aborts the execution of the process.
     pub fn abort() -> !;
 
index b80f77c0d25a9c7afec7135e0ae9146e875fdff4..6b01ccaceea2f31c3726597244a23ccefdda2fc1 100644 (file)
 use clone::Clone;
 use cmp::{Ord, PartialOrd, PartialEq, Ordering};
 use default::Default;
-use marker;
-use num::{Zero, One};
-use ops::{Add, FnMut, Mul};
+use ops::FnMut;
 use option::Option::{self, Some, None};
 use marker::Sized;
 
-use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, FlatMap, Fuse,
-            Inspect, Map, Peekable, Scan, Skip, SkipWhile, Take, TakeWhile, Rev,
-            Zip};
+use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, FlatMap, Fuse};
+use super::{Inspect, Map, Peekable, Scan, Skip, SkipWhile, Take, TakeWhile, Rev};
+use super::{Zip, Sum, Product};
 use super::ChainState;
-use super::{DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator,
-            IntoIterator};
+use super::{DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator};
+use super::{IntoIterator, ZipImpl};
 
 fn _assert_is_object_safe(_: &Iterator<Item=()>) {}
 
@@ -172,6 +170,7 @@ pub trait Iterator {
     /// assert_eq!(a.iter().count(), 5);
     /// ```
     #[inline]
+    #[rustc_inherit_overflow_checks]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn count(self) -> usize where Self: Sized {
         // Might overflow.
@@ -382,7 +381,7 @@ pub trait Iterator {
     fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter> where
         Self: Sized, U: IntoIterator
     {
-        Zip{a: self, b: other.into_iter()}
+        Zip::new(self, other.into_iter())
     }
 
     /// Takes a closure and creates an iterator which calls that closure on each
@@ -1746,23 +1745,9 @@ pub trait Iterator {
         FromB: Default + Extend<B>,
         Self: Sized + Iterator<Item=(A, B)>,
     {
-        struct SizeHint<A>(usize, Option<usize>, marker::PhantomData<A>);
-        impl<A> Iterator for SizeHint<A> {
-            type Item = A;
-
-            fn next(&mut self) -> Option<A> { None }
-            fn size_hint(&self) -> (usize, Option<usize>) {
-                (self.0, self.1)
-            }
-        }
-
-        let (lo, hi) = self.size_hint();
         let mut ts: FromA = Default::default();
         let mut us: FromB = Default::default();
 
-        ts.extend(SizeHint(lo, hi, marker::PhantomData));
-        us.extend(SizeHint(lo, hi, marker::PhantomData));
-
         for (t, u) in self {
             ts.extend(Some(t));
             us.extend(Some(u));
@@ -1833,36 +1818,41 @@ pub trait Iterator {
     ///
     /// An empty iterator returns the zero value of the type.
     ///
+    /// # Panics
+    ///
+    /// When calling `sum` and a primitive integer type is being returned, this
+    /// method will panic if the computation overflows.
+    ///
     /// # Examples
     ///
     /// Basic usage:
     ///
     /// ```
-    /// #![feature(iter_arith)]
-    ///
     /// let a = [1, 2, 3];
     /// let sum: i32 = a.iter().sum();
     ///
     /// assert_eq!(sum, 6);
     /// ```
-    #[unstable(feature = "iter_arith", reason = "bounds recently changed",
-               issue = "27739")]
-    fn sum<S>(self) -> S where
-        S: Add<Self::Item, Output=S> + Zero,
-        Self: Sized,
+    #[stable(feature = "iter_arith", since = "1.11.0")]
+    fn sum<S>(self) -> S
+        where Self: Sized,
+              S: Sum<Self::Item>,
     {
-        self.fold(Zero::zero(), |s, e| s + e)
+        Sum::sum(self)
     }
 
     /// Iterates over the entire iterator, multiplying all the elements
     ///
     /// An empty iterator returns the one value of the type.
     ///
+    /// # Panics
+    ///
+    /// When calling `product` and a primitive integer type is being returned,
+    /// this method will panic if the computation overflows.
+    ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(iter_arith)]
-    ///
     /// fn factorial(n: u32) -> u32 {
     ///     (1..).take_while(|&i| i <= n).product()
     /// }
@@ -1870,13 +1860,12 @@ pub trait Iterator {
     /// assert_eq!(factorial(1), 1);
     /// assert_eq!(factorial(5), 120);
     /// ```
-    #[unstable(feature="iter_arith", reason = "bounds recently changed",
-               issue = "27739")]
-    fn product<P>(self) -> P where
-        P: Mul<Self::Item, Output=P> + One,
-        Self: Sized,
+    #[stable(feature = "iter_arith", since = "1.11.0")]
+    fn product<P>(self) -> P
+        where Self: Sized,
+              P: Product<Self::Item>,
     {
-        self.fold(One::one(), |p, e| p * e)
+        Product::product(self)
     }
 
     /// Lexicographically compares the elements of this `Iterator` with those
index f964527b4b41f6b4773abb2ac7181c43cc5491d5..3ebab266e2ffed5494cc069a90172e64833f9b83 100644 (file)
 
 use clone::Clone;
 use cmp;
+use default::Default;
 use fmt;
+use iter_private::TrustedRandomAccess;
 use ops::FnMut;
 use option::Option::{self, Some, None};
 use usize;
@@ -325,8 +327,9 @@ pub use self::sources::{Empty, empty};
 pub use self::sources::{Once, once};
 
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use self::traits::{FromIterator, IntoIterator, DoubleEndedIterator, Extend,
-                       ExactSizeIterator};
+pub use self::traits::{FromIterator, IntoIterator, DoubleEndedIterator, Extend};
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use self::traits::{ExactSizeIterator, Sum, Product};
 
 mod iterator;
 mod range;
@@ -510,6 +513,7 @@ impl<A, B> Iterator for Chain<A, B> where
     }
 
     #[inline]
+    #[rustc_inherit_overflow_checks]
     fn count(self) -> usize {
         match self.state {
             ChainState::Both => self.a.count() + self.b.count(),
@@ -621,7 +625,8 @@ impl<A, B> DoubleEndedIterator for Chain<A, B> where
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Zip<A, B> {
     a: A,
-    b: B
+    b: B,
+    spec: <(A, B) as ZipImplData>::Data,
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -630,29 +635,13 @@ impl<A, B> Iterator for Zip<A, B> where A: Iterator, B: Iterator
     type Item = (A::Item, B::Item);
 
     #[inline]
-    fn next(&mut self) -> Option<(A::Item, B::Item)> {
-        self.a.next().and_then(|x| {
-            self.b.next().and_then(|y| {
-                Some((x, y))
-            })
-        })
+    fn next(&mut self) -> Option<Self::Item> {
+        ZipImpl::next(self)
     }
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let (a_lower, a_upper) = self.a.size_hint();
-        let (b_lower, b_upper) = self.b.size_hint();
-
-        let lower = cmp::min(a_lower, b_lower);
-
-        let upper = match (a_upper, b_upper) {
-            (Some(x), Some(y)) => Some(cmp::min(x,y)),
-            (Some(x), None) => Some(x),
-            (None, Some(y)) => Some(y),
-            (None, None) => None
-        };
-
-        (lower, upper)
+        ZipImpl::size_hint(self)
     }
 }
 
@@ -663,6 +652,61 @@ impl<A, B> DoubleEndedIterator for Zip<A, B> where
 {
     #[inline]
     fn next_back(&mut self) -> Option<(A::Item, B::Item)> {
+        ZipImpl::next_back(self)
+    }
+}
+
+// Zip specialization trait
+#[doc(hidden)]
+trait ZipImpl<A, B> {
+    type Item;
+    fn new(a: A, b: B) -> Self;
+    fn next(&mut self) -> Option<Self::Item>;
+    fn size_hint(&self) -> (usize, Option<usize>);
+    fn next_back(&mut self) -> Option<Self::Item>
+        where A: DoubleEndedIterator + ExactSizeIterator,
+              B: DoubleEndedIterator + ExactSizeIterator;
+}
+
+// Zip specialization data members
+#[doc(hidden)]
+trait ZipImplData {
+    type Data: 'static + Clone + Default + fmt::Debug;
+}
+
+#[doc(hidden)]
+impl<T> ZipImplData for T {
+    default type Data = ();
+}
+
+// General Zip impl
+#[doc(hidden)]
+impl<A, B> ZipImpl<A, B> for Zip<A, B>
+    where A: Iterator, B: Iterator
+{
+    type Item = (A::Item, B::Item);
+    default fn new(a: A, b: B) -> Self {
+        Zip {
+            a: a,
+            b: b,
+            spec: Default::default(), // unused
+        }
+    }
+
+    #[inline]
+    default fn next(&mut self) -> Option<(A::Item, B::Item)> {
+        self.a.next().and_then(|x| {
+            self.b.next().and_then(|y| {
+                Some((x, y))
+            })
+        })
+    }
+
+    #[inline]
+    default fn next_back(&mut self) -> Option<(A::Item, B::Item)>
+        where A: DoubleEndedIterator + ExactSizeIterator,
+              B: DoubleEndedIterator + ExactSizeIterator
+    {
         let a_sz = self.a.len();
         let b_sz = self.b.len();
         if a_sz != b_sz {
@@ -679,12 +723,106 @@ impl<A, B> DoubleEndedIterator for Zip<A, B> where
             _ => unreachable!(),
         }
     }
+
+    #[inline]
+    default fn size_hint(&self) -> (usize, Option<usize>) {
+        let (a_lower, a_upper) = self.a.size_hint();
+        let (b_lower, b_upper) = self.b.size_hint();
+
+        let lower = cmp::min(a_lower, b_lower);
+
+        let upper = match (a_upper, b_upper) {
+            (Some(x), Some(y)) => Some(cmp::min(x,y)),
+            (Some(x), None) => Some(x),
+            (None, Some(y)) => Some(y),
+            (None, None) => None
+        };
+
+        (lower, upper)
+    }
+}
+
+#[doc(hidden)]
+#[derive(Default, Debug, Clone)]
+struct ZipImplFields {
+    index: usize,
+    len: usize,
+}
+
+#[doc(hidden)]
+impl<A, B> ZipImplData for (A, B)
+    where A: TrustedRandomAccess, B: TrustedRandomAccess
+{
+    type Data = ZipImplFields;
+}
+
+#[doc(hidden)]
+impl<A, B> ZipImpl<A, B> for Zip<A, B>
+    where A: TrustedRandomAccess, B: TrustedRandomAccess
+{
+    fn new(a: A, b: B) -> Self {
+        let len = cmp::min(a.len(), b.len());
+        Zip {
+            a: a,
+            b: b,
+            spec: ZipImplFields {
+                index: 0,
+                len: len,
+            }
+        }
+    }
+
+    #[inline]
+    fn next(&mut self) -> Option<(A::Item, B::Item)> {
+        if self.spec.index < self.spec.len {
+            let i = self.spec.index;
+            self.spec.index += 1;
+            unsafe {
+                Some((self.a.get_unchecked(i), self.b.get_unchecked(i)))
+            }
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let len = self.spec.len - self.spec.index;
+        (len, Some(len))
+    }
+
+    #[inline]
+    fn next_back(&mut self) -> Option<(A::Item, B::Item)>
+        where A: DoubleEndedIterator + ExactSizeIterator,
+              B: DoubleEndedIterator + ExactSizeIterator
+    {
+        if self.spec.index < self.spec.len {
+            self.spec.len -= 1;
+            let i = self.spec.len;
+            unsafe {
+                Some((self.a.get_unchecked(i), self.b.get_unchecked(i)))
+            }
+        } else {
+            None
+        }
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<A, B> ExactSizeIterator for Zip<A, B>
     where A: ExactSizeIterator, B: ExactSizeIterator {}
 
+#[doc(hidden)]
+unsafe impl<A, B> TrustedRandomAccess for Zip<A, B>
+    where A: TrustedRandomAccess,
+          B: TrustedRandomAccess,
+{
+    unsafe fn get_unchecked(&mut self, i: usize) -> (A::Item, B::Item) {
+        (self.a.get_unchecked(i), self.b.get_unchecked(i))
+    }
+
+}
+
 /// An iterator that maps the values of `iter` with `f`.
 ///
 /// This `struct` is created by the [`map()`] method on [`Iterator`]. See its
@@ -932,6 +1070,7 @@ impl<I> Iterator for Enumerate<I> where I: Iterator {
     ///
     /// Might panic if the index of the element overflows a `usize`.
     #[inline]
+    #[rustc_inherit_overflow_checks]
     fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
         self.iter.next().map(|a| {
             let ret = (self.count, a);
@@ -947,6 +1086,7 @@ impl<I> Iterator for Enumerate<I> where I: Iterator {
     }
 
     #[inline]
+    #[rustc_inherit_overflow_checks]
     fn nth(&mut self, n: usize) -> Option<(usize, I::Item)> {
         self.iter.nth(n).map(|a| {
             let i = self.count + n;
@@ -979,6 +1119,15 @@ impl<I> DoubleEndedIterator for Enumerate<I> where
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<I> ExactSizeIterator for Enumerate<I> where I: ExactSizeIterator {}
 
+#[doc(hidden)]
+unsafe impl<I> TrustedRandomAccess for Enumerate<I>
+    where I: TrustedRandomAccess
+{
+    unsafe fn get_unchecked(&mut self, i: usize) -> (usize, I::Item) {
+        (self.count + i, self.iter.get_unchecked(i))
+    }
+}
+
 /// An iterator with a `peek()` that returns an optional reference to the next
 /// element.
 ///
@@ -1008,6 +1157,7 @@ impl<I: Iterator> Iterator for Peekable<I> {
     }
 
     #[inline]
+    #[rustc_inherit_overflow_checks]
     fn count(self) -> usize {
         (if self.peeked.is_some() { 1 } else { 0 }) + self.iter.count()
     }
index 08143567beaf3892cfb82ff66ab5ab94c531fed8..c234ef21db6d12878e09e722ed507d058948a42a 100644 (file)
@@ -11,7 +11,6 @@
 use clone::Clone;
 use cmp::PartialOrd;
 use mem;
-use num::{Zero, One};
 use ops::{self, Add, Sub};
 use option::Option::{self, Some, None};
 use marker::Sized;
@@ -36,6 +35,24 @@ pub trait Step: PartialOrd + Sized {
     /// Returns `None` if it is not possible to calculate `steps_between`
     /// without overflow.
     fn steps_between(start: &Self, end: &Self, by: &Self) -> Option<usize>;
+
+    /// Same as `steps_between`, but with a `by` of 1
+    fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize>;
+
+    /// Tests whether this step is negative or not (going backwards)
+    fn is_negative(&self) -> bool;
+
+    /// Replaces this step with `1`, returning itself
+    fn replace_one(&mut self) -> Self;
+
+    /// Replaces this step with `0`, returning itself
+    fn replace_zero(&mut self) -> Self;
+
+    /// Adds one to this step, returning the result
+    fn add_one(&self) -> Self;
+
+    /// Subtracts one to this step, returning the result
+    fn sub_one(&self) -> Self;
 }
 
 macro_rules! step_impl_unsigned {
@@ -65,6 +82,36 @@ macro_rules! step_impl_unsigned {
                     Some(0)
                 }
             }
+
+            #[inline]
+            fn is_negative(&self) -> bool {
+                false
+            }
+
+            #[inline]
+            fn replace_one(&mut self) -> Self {
+                mem::replace(self, 0)
+            }
+
+            #[inline]
+            fn replace_zero(&mut self) -> Self {
+                mem::replace(self, 1)
+            }
+
+            #[inline]
+            fn add_one(&self) -> Self {
+                *self + 1
+            }
+
+            #[inline]
+            fn sub_one(&self) -> Self {
+                *self - 1
+            }
+
+            #[inline]
+            fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize> {
+                Self::steps_between(start, end, &1)
+            }
         }
     )*)
 }
@@ -106,6 +153,36 @@ macro_rules! step_impl_signed {
                     Some(diff / by_u)
                 }
             }
+
+            #[inline]
+            fn is_negative(&self) -> bool {
+                *self < 0
+            }
+
+            #[inline]
+            fn replace_one(&mut self) -> Self {
+                mem::replace(self, 0)
+            }
+
+            #[inline]
+            fn replace_zero(&mut self) -> Self {
+                mem::replace(self, 1)
+            }
+
+            #[inline]
+            fn add_one(&self) -> Self {
+                *self + 1
+            }
+
+            #[inline]
+            fn sub_one(&self) -> Self {
+                *self - 1
+            }
+
+            #[inline]
+            fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize> {
+                Self::steps_between(start, end, &1)
+            }
         }
     )*)
 }
@@ -124,6 +201,37 @@ macro_rules! step_impl_no_between {
             fn steps_between(_a: &$t, _b: &$t, _by: &$t) -> Option<usize> {
                 None
             }
+
+            #[inline]
+            #[allow(unused_comparisons)]
+            fn is_negative(&self) -> bool {
+                *self < 0
+            }
+
+            #[inline]
+            fn replace_one(&mut self) -> Self {
+                mem::replace(self, 0)
+            }
+
+            #[inline]
+            fn replace_zero(&mut self) -> Self {
+                mem::replace(self, 1)
+            }
+
+            #[inline]
+            fn add_one(&self) -> Self {
+                *self + 1
+            }
+
+            #[inline]
+            fn sub_one(&self) -> Self {
+                *self - 1
+            }
+
+            #[inline]
+            fn steps_between_by_one(start: &Self, end: &Self) -> Option<usize> {
+                Self::steps_between(start, end, &1)
+            }
         }
     )*)
 }
@@ -269,12 +377,12 @@ impl<A> Iterator for StepBy<A, ops::RangeFrom<A>> where
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::Range<A>> {
+impl<A: Step + Clone> Iterator for StepBy<A, ops::Range<A>> {
     type Item = A;
 
     #[inline]
     fn next(&mut self) -> Option<A> {
-        let rev = self.step_by < A::zero();
+        let rev = self.step_by.is_negative();
         if (rev && self.range.start > self.range.end) ||
            (!rev && self.range.start < self.range.end)
         {
@@ -308,7 +416,7 @@ impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::Range<A>> {
 #[unstable(feature = "inclusive_range",
            reason = "recently added, follows RFC",
            issue = "28237")]
-impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::RangeInclusive<A>> {
+impl<A: Step + Clone> Iterator for StepBy<A, ops::RangeInclusive<A>> {
     type Item = A;
 
     #[inline]
@@ -322,8 +430,7 @@ impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::RangeInclusive<A>> {
             Empty { .. } => return None, // empty iterators yield no values
 
             NonEmpty { ref mut start, ref mut end } => {
-                let zero = A::zero();
-                let rev = self.step_by < zero;
+                let rev = self.step_by.is_negative();
 
                 // march start towards (maybe past!) end and yield the old value
                 if (rev && start >= end) ||
@@ -342,7 +449,7 @@ impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::RangeInclusive<A>> {
                     }
                 } else {
                     // found range in inconsistent state (start at or past end), so become empty
-                    (Some(mem::replace(end, zero)), None)
+                    (Some(end.replace_zero()), None)
                 }
             }
         };
@@ -386,7 +493,7 @@ macro_rules! range_exact_iter_impl {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<A: Step + One> Iterator for ops::Range<A> where
+impl<A: Step> Iterator for ops::Range<A> where
     for<'a> &'a A: Add<&'a A, Output = A>
 {
     type Item = A;
@@ -394,7 +501,7 @@ impl<A: Step + One> Iterator for ops::Range<A> where
     #[inline]
     fn next(&mut self) -> Option<A> {
         if self.start < self.end {
-            let mut n = &self.start + &A::one();
+            let mut n = self.start.add_one();
             mem::swap(&mut n, &mut self.start);
             Some(n)
         } else {
@@ -404,7 +511,7 @@ impl<A: Step + One> Iterator for ops::Range<A> where
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        match Step::steps_between(&self.start, &self.end, &A::one()) {
+        match Step::steps_between_by_one(&self.start, &self.end) {
             Some(hint) => (hint, Some(hint)),
             None => (0, None)
         }
@@ -416,14 +523,14 @@ impl<A: Step + One> Iterator for ops::Range<A> where
 range_exact_iter_impl!(usize u8 u16 u32 isize i8 i16 i32);
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<A: Step + One + Clone> DoubleEndedIterator for ops::Range<A> where
+impl<A: Step + Clone> DoubleEndedIterator for ops::Range<A> where
     for<'a> &'a A: Add<&'a A, Output = A>,
     for<'a> &'a A: Sub<&'a A, Output = A>
 {
     #[inline]
     fn next_back(&mut self) -> Option<A> {
         if self.start < self.end {
-            self.end = &self.end - &A::one();
+            self.end = self.end.sub_one();
             Some(self.end.clone())
         } else {
             None
@@ -432,21 +539,21 @@ impl<A: Step + One + Clone> DoubleEndedIterator for ops::Range<A> where
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<A: Step + One> Iterator for ops::RangeFrom<A> where
+impl<A: Step> Iterator for ops::RangeFrom<A> where
     for<'a> &'a A: Add<&'a A, Output = A>
 {
     type Item = A;
 
     #[inline]
     fn next(&mut self) -> Option<A> {
-        let mut n = &self.start + &A::one();
+        let mut n = self.start.add_one();
         mem::swap(&mut n, &mut self.start);
         Some(n)
     }
 }
 
 #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
-impl<A: Step + One> Iterator for ops::RangeInclusive<A> where
+impl<A: Step> Iterator for ops::RangeInclusive<A> where
     for<'a> &'a A: Add<&'a A, Output = A>
 {
     type Item = A;
@@ -463,23 +570,22 @@ impl<A: Step + One> Iterator for ops::RangeInclusive<A> where
 
             NonEmpty { ref mut start, ref mut end } => {
                 if start == end {
-                    (Some(mem::replace(end, A::one())), Some(mem::replace(start, A::one())))
+                    (Some(end.replace_one()), Some(start.replace_one()))
                 } else if start < end {
-                    let one = A::one();
-                    let mut n = &*start + &one;
+                    let mut n = start.add_one();
                     mem::swap(&mut n, start);
 
-                    // if the iterator is done iterating, it will change from NonEmpty to Empty
-                    // to avoid unnecessary drops or clones, we'll reuse either start or end
-                    // (they are equal now, so it doesn't matter which)
-                    // to pull out end, we need to swap something back in -- use the previously
-                    // created A::one() as a dummy value
+                    // if the iterator is done iterating, it will change from
+                    // NonEmpty to Empty to avoid unnecessary drops or clones,
+                    // we'll reuse either start or end (they are equal now, so
+                    // it doesn't matter which) to pull out end, we need to swap
+                    // something back in
 
-                    (if n == *end { Some(mem::replace(end, one)) } else { None },
+                    (if n == *end { Some(end.replace_one()) } else { None },
                     // ^ are we done yet?
                     Some(n)) // < the value to output
                 } else {
-                    (Some(mem::replace(start, A::one())), None)
+                    (Some(start.replace_one()), None)
                 }
             }
         };
@@ -500,7 +606,7 @@ impl<A: Step + One> Iterator for ops::RangeInclusive<A> where
             Empty { .. } => (0, Some(0)),
 
             NonEmpty { ref start, ref end } =>
-                match Step::steps_between(start, end, &A::one()) {
+                match Step::steps_between_by_one(start, end) {
                     Some(hint) => (hint.saturating_add(1), hint.checked_add(1)),
                     None => (0, None),
                 }
@@ -509,7 +615,7 @@ impl<A: Step + One> Iterator for ops::RangeInclusive<A> where
 }
 
 #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
-impl<A: Step + One> DoubleEndedIterator for ops::RangeInclusive<A> where
+impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> where
     for<'a> &'a A: Add<&'a A, Output = A>,
     for<'a> &'a A: Sub<&'a A, Output = A>
 {
@@ -524,16 +630,15 @@ impl<A: Step + One> DoubleEndedIterator for ops::RangeInclusive<A> where
 
             NonEmpty { ref mut start, ref mut end } => {
                 if start == end {
-                    (Some(mem::replace(start, A::one())), Some(mem::replace(end, A::one())))
+                    (Some(start.replace_one()), Some(end.replace_one()))
                 } else if start < end {
-                    let one = A::one();
-                    let mut n = &*end - &one;
+                    let mut n = end.sub_one();
                     mem::swap(&mut n, end);
 
-                    (if n == *start { Some(mem::replace(start, one)) } else { None },
+                    (if n == *start { Some(start.replace_one()) } else { None },
                      Some(n))
                 } else {
-                    (Some(mem::replace(end, A::one())), None)
+                    (Some(end.replace_one()), None)
                 }
             }
         };
index 67503984450a45806a3af0bd1f8667397269a058..3549bd6a3bc68f4fcfa52cab27ed36a9817b5378 100644 (file)
@@ -524,3 +524,104 @@ pub trait ExactSizeIterator: Iterator {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, I: ExactSizeIterator + ?Sized> ExactSizeIterator for &'a mut I {}
 
+/// Trait to represent types that can be created by summing up an iterator.
+///
+/// This trait is used to implement the `sum` method on iterators. Types which
+/// implement the trait can be generated by the `sum` method. Like
+/// `FromIterator` this trait should rarely be called directly and instead
+/// interacted with through `Iterator::sum`.
+#[unstable(feature = "iter_arith_traits", issue = "34529")]
+pub trait Sum<A = Self>: Sized {
+    /// Method which takes an iterator and generates `Self` from the elements by
+    /// "summing up" the items.
+    fn sum<I: Iterator<Item=A>>(iter: I) -> Self;
+}
+
+/// Trait to represent types that can be created by multiplying elements of an
+/// iterator.
+///
+/// This trait is used to implement the `product` method on iterators. Types
+/// which implement the trait can be generated by the `product` method. Like
+/// `FromIterator` this trait should rarely be called directly and instead
+/// interacted with through `Iterator::product`.
+#[unstable(feature = "iter_arith_traits", issue = "34529")]
+pub trait Product<A = Self>: Sized {
+    /// Method which takes an iterator and generates `Self` from the elements by
+    /// multiplying the items.
+    fn product<I: Iterator<Item=A>>(iter: I) -> Self;
+}
+
+macro_rules! integer_sum_product {
+    ($($a:ident)*) => ($(
+        #[unstable(feature = "iter_arith_traits", issue = "34529")]
+        impl Sum for $a {
+            fn sum<I: Iterator<Item=$a>>(iter: I) -> $a {
+                iter.fold(0, |a, b| {
+                    a.checked_add(b).expect("overflow in sum")
+                })
+            }
+        }
+
+        #[unstable(feature = "iter_arith_traits", issue = "34529")]
+        impl Product for $a {
+            fn product<I: Iterator<Item=$a>>(iter: I) -> $a {
+                iter.fold(1, |a, b| {
+                    a.checked_mul(b).expect("overflow in product")
+                })
+            }
+        }
+
+        #[unstable(feature = "iter_arith_traits", issue = "34529")]
+        impl<'a> Sum<&'a $a> for $a {
+            fn sum<I: Iterator<Item=&'a $a>>(iter: I) -> $a {
+                iter.fold(0, |a, b| {
+                    a.checked_add(*b).expect("overflow in sum")
+                })
+            }
+        }
+
+        #[unstable(feature = "iter_arith_traits", issue = "34529")]
+        impl<'a> Product<&'a $a> for $a {
+            fn product<I: Iterator<Item=&'a $a>>(iter: I) -> $a {
+                iter.fold(1, |a, b| {
+                    a.checked_mul(*b).expect("overflow in product")
+                })
+            }
+        }
+    )*)
+}
+
+macro_rules! float_sum_product {
+    ($($a:ident)*) => ($(
+        #[unstable(feature = "iter_arith_traits", issue = "34529")]
+        impl Sum for $a {
+            fn sum<I: Iterator<Item=$a>>(iter: I) -> $a {
+                iter.fold(0.0, |a, b| a + b)
+            }
+        }
+
+        #[unstable(feature = "iter_arith_traits", issue = "34529")]
+        impl Product for $a {
+            fn product<I: Iterator<Item=$a>>(iter: I) -> $a {
+                iter.fold(1.0, |a, b| a * b)
+            }
+        }
+
+        #[unstable(feature = "iter_arith_traits", issue = "34529")]
+        impl<'a> Sum<&'a $a> for $a {
+            fn sum<I: Iterator<Item=&'a $a>>(iter: I) -> $a {
+                iter.fold(0.0, |a, b| a + *b)
+            }
+        }
+
+        #[unstable(feature = "iter_arith_traits", issue = "34529")]
+        impl<'a> Product<&'a $a> for $a {
+            fn product<I: Iterator<Item=&'a $a>>(iter: I) -> $a {
+                iter.fold(1.0, |a, b| a * *b)
+            }
+        }
+    )*)
+}
+
+integer_sum_product! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }
+float_sum_product! { f32 f64 }
diff --git a/src/libcore/iter_private.rs b/src/libcore/iter_private.rs
new file mode 100644 (file)
index 0000000..effe43c
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+use iter::ExactSizeIterator;
+
+/// An iterator whose items are random accessible efficiently
+///
+/// # Safety
+///
+/// The iterator's .len() and size_hint() must be exact.
+///
+/// .get_unchecked() must return distinct mutable references for distinct
+/// indices (if applicable), and must return a valid reference if index is in
+/// 0..self.len().
+#[doc(hidden)]
+pub unsafe trait TrustedRandomAccess : ExactSizeIterator {
+    unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item;
+}
+
index a054e41b2084aa06abfa9736f0043e02aa31f39b..e849369d647c4754967d63f3d7cd627ae0fc8929 100644 (file)
@@ -43,7 +43,6 @@
 // Since libcore defines many fundamental lang items, all tests live in a
 // separate crate, libcoretest, to avoid bizarre issues.
 
-#![cfg_attr(stage0, allow(unused_attributes))]
 #![crate_name = "core"]
 #![stable(feature = "core", since = "1.6.0")]
 #![crate_type = "rlib"]
@@ -60,6 +59,8 @@
 #![deny(missing_debug_implementations)]
 #![cfg_attr(not(stage0), deny(warnings))]
 
+#![cfg_attr(stage0, allow(unused_attributes))]
+
 #![feature(allow_internal_unstable)]
 #![feature(asm)]
 #![feature(associated_type_defaults)]
@@ -157,4 +158,5 @@ pub mod hash;
 pub mod fmt;
 
 // note: does not need to be public
+mod iter_private;
 mod tuple;
index a40608b0762d66a4e74966312d48ff5c1c182e85..376d2792c445c5e31c9ca3be90864f04a2724985 100644 (file)
@@ -94,7 +94,18 @@ macro_rules! assert_eq {
                 }
             }
         }
-    })
+    });
+    ($left:expr , $right:expr, $($arg:tt)*) => ({
+        match (&($left), &($right)) {
+            (left_val, right_val) => {
+                if !(*left_val == *right_val) {
+                    panic!("assertion failed: `(left == right)` \
+                           (left: `{:?}`, right: `{:?}`): {}", left_val, right_val,
+                           format_args!($($arg)*))
+                }
+            }
+        }
+    });
 }
 
 /// Ensure that a boolean expression is `true` at runtime.
index 1ed2a219fac3a5f03499d2a1532b90b064bc516a..c18d230be31af9db758feb8e6f6de9f439d80ba3 100644 (file)
@@ -136,6 +136,26 @@ pub trait Unsize<T: ?Sized> {
 /// the trait `Copy` may not be implemented for this type; field `points` does not implement `Copy`
 /// ```
 ///
+/// ## When can my type _not_ be `Copy`?
+///
+/// Some types can't be copied safely. For example, copying `&mut T` would create an aliased
+/// mutable reference, and copying `String` would result in two attempts to free the same buffer.
+///
+/// Generalizing the latter case, any type implementing `Drop` can't be `Copy`, because it's
+/// managing some resource besides its own `size_of::<T>()` bytes.
+///
+/// ## When should my type be `Copy`?
+///
+/// Generally speaking, if your type _can_ implement `Copy`, it should. There's one important thing
+/// to consider though: if you think your type may _not_ be able to implement `Copy` in the future,
+/// then it might be prudent to not implement `Copy`. This is because removing `Copy` is a breaking
+/// change: that second example would fail to compile if we made `Foo` non-`Copy`.
+///
+/// ## Derivable
+///
+/// This trait can be used with `#[derive]` if all of its components implement `Copy` and the type
+/// implements `Clone`. The implementation will copy the bytes of each field using `memcpy`.
+///
 /// ## How can I implement `Copy`?
 ///
 /// There are two ways to implement `Copy` on your type:
@@ -155,25 +175,6 @@ pub trait Unsize<T: ?Sized> {
 ///
 /// There is a small difference between the two: the `derive` strategy will also place a `Copy`
 /// bound on type parameters, which isn't always desired.
-///
-/// ## When can my type _not_ be `Copy`?
-///
-/// Some types can't be copied safely. For example, copying `&mut T` would create an aliased
-/// mutable reference, and copying `String` would result in two attempts to free the same buffer.
-///
-/// Generalizing the latter case, any type implementing `Drop` can't be `Copy`, because it's
-/// managing some resource besides its own `size_of::<T>()` bytes.
-///
-/// ## When should my type be `Copy`?
-///
-/// Generally speaking, if your type _can_ implement `Copy`, it should. There's one important thing
-/// to consider though: if you think your type may _not_ be able to implement `Copy` in the future,
-/// then it might be prudent to not implement `Copy`. This is because removing `Copy` is a breaking
-/// change: that second example would fail to compile if we made `Foo` non-`Copy`.
-///
-/// # Derivable
-///
-/// This trait can be used with `#[derive]`.
 #[stable(feature = "rust1", since = "1.0.0")]
 #[lang = "copy"]
 pub trait Copy : Clone {
index a0f2a2adcb62cb8cf2587af3170fa96eea412f51..5c2179ccf33a1a6404ef4db66f047c0e7b77e6e7 100644 (file)
@@ -518,6 +518,10 @@ pub fn replace<T>(dest: &mut T, mut src: T) -> T {
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn drop<T>(_x: T) { }
 
+macro_rules! repeat_u8_as_u16 {
+    ($name:expr) => { (($name as u16) <<  8 |
+                       ($name as u16)) }
+}
 macro_rules! repeat_u8_as_u32 {
     ($name:expr) => { (($name as u32) << 24 |
                        ($name as u32) << 16 |
@@ -543,11 +547,18 @@ macro_rules! repeat_u8_as_u64 {
 pub const POST_DROP_U8: u8 = 0x1d;
 #[unstable(feature = "filling_drop", issue = "5016")]
 #[allow(missing_docs)]
+pub const POST_DROP_U16: u16 = repeat_u8_as_u16!(POST_DROP_U8);
+#[unstable(feature = "filling_drop", issue = "5016")]
+#[allow(missing_docs)]
 pub const POST_DROP_U32: u32 = repeat_u8_as_u32!(POST_DROP_U8);
 #[unstable(feature = "filling_drop", issue = "5016")]
 #[allow(missing_docs)]
 pub const POST_DROP_U64: u64 = repeat_u8_as_u64!(POST_DROP_U8);
 
+#[cfg(target_pointer_width = "16")]
+#[unstable(feature = "filling_drop", issue = "5016")]
+#[allow(missing_docs)]
+pub const POST_DROP_USIZE: usize = POST_DROP_U16 as usize;
 #[cfg(target_pointer_width = "32")]
 #[unstable(feature = "filling_drop", issue = "5016")]
 #[allow(missing_docs)]
index c7af46a1e4f6ba62e5598baaf1800f7606654120..4761727cec03ff93594b2fb4d706c960ba99479a 100644 (file)
@@ -321,7 +321,7 @@ pub fn algorithm_m<T: RawFloat>(f: &Big, e: i16) -> T {
             return underflow(x, v, rem);
         }
         if k > T::max_exp_int() {
-            return T::infinity();
+            return T::infinity2();
         }
         if x < min_sig {
             u.mul_pow2(1);
index 022bd84f4c8417e3ef352a0f493f618b4de0749b..ff2d85307b1060ef6cb2c5a2bcc619a80fe2b7f5 100644 (file)
@@ -215,11 +215,11 @@ fn dec2flt<T: RawFloat>(s: &str) -> Result<T, ParseFloatError> {
     let (sign, s) = extract_sign(s);
     let flt = match parse_decimal(s) {
         ParseResult::Valid(decimal) => convert(decimal)?,
-        ParseResult::ShortcutToInf => T::infinity(),
-        ParseResult::ShortcutToZero => T::zero(),
+        ParseResult::ShortcutToInf => T::infinity2(),
+        ParseResult::ShortcutToZero => T::zero2(),
         ParseResult::Invalid => match s {
-            "inf" => T::infinity(),
-            "NaN" => T::nan(),
+            "inf" => T::infinity2(),
+            "NaN" => T::nan2(),
             _ => { return Err(pfe_invalid()); }
         }
     };
@@ -316,7 +316,7 @@ fn bound_intermediate_digits(decimal: &Decimal, e: i64) -> u64 {
 fn trivial_cases<T: RawFloat>(decimal: &Decimal) -> Option<T> {
     // There were zeros but they were stripped by simplify()
     if decimal.integral.is_empty() && decimal.fractional.is_empty() {
-        return Some(T::zero());
+        return Some(T::zero2());
     }
     // This is a crude approximation of ceil(log10(the real value)). We don't need to worry too
     // much about overflow here because the input length is tiny (at least compared to 2^64) and
@@ -324,9 +324,9 @@ fn trivial_cases<T: RawFloat>(decimal: &Decimal) -> Option<T> {
     // (which is still 10^19 short of 2^64).
     let max_place = decimal.exp + decimal.integral.len() as i64;
     if max_place > T::inf_cutoff() {
-        return Some(T::infinity());
+        return Some(T::infinity2());
     } else if max_place < T::zero_cutoff() {
-        return Some(T::zero());
+        return Some(T::zero2());
     }
     None
 }
index 2099c6a7baa7649960065f6688d4fae745339368..68e4dc4b359efa73fb3ee5373004d569f756fcd9 100644 (file)
@@ -61,6 +61,27 @@ impl Unpacked {
 pub trait RawFloat : Float + Copy + Debug + LowerExp
                     + Mul<Output=Self> + Div<Output=Self> + Neg<Output=Self>
 {
+    // suffix of "2" because Float::infinity is deprecated
+    #[allow(deprecated)]
+    fn infinity2() -> Self {
+        Float::infinity()
+    }
+
+    // suffix of "2" because Float::nan is deprecated
+    #[allow(deprecated)]
+    fn nan2() -> Self {
+        Float::nan()
+    }
+
+    // suffix of "2" because Float::zero is deprecated
+    fn zero2() -> Self;
+
+    // suffix of "2" because Float::integer_decode is deprecated
+    #[allow(deprecated)]
+    fn integer_decode2(self) -> (u64, i16, i8) {
+        Float::integer_decode(self)
+    }
+
     /// Get the raw binary representation of the float.
     fn transmute(self) -> u64;
 
@@ -146,6 +167,10 @@ pub trait RawFloat : Float + Copy + Debug + LowerExp
 }
 
 impl RawFloat for f32 {
+    fn zero2() -> Self {
+        0.0
+    }
+
     fn sig_bits() -> u8 {
         24
     }
@@ -169,7 +194,7 @@ impl RawFloat for f32 {
     }
 
     fn unpack(self) -> Unpacked {
-        let (sig, exp, _sig) = self.integer_decode();
+        let (sig, exp, _sig) = self.integer_decode2();
         Unpacked::new(sig, exp)
     }
 
@@ -198,6 +223,10 @@ impl RawFloat for f32 {
 
 
 impl RawFloat for f64 {
+    fn zero2() -> Self {
+        0.0
+    }
+
     fn sig_bits() -> u8 {
         53
     }
@@ -220,7 +249,7 @@ impl RawFloat for f64 {
     }
 
     fn unpack(self) -> Unpacked {
-        let (sig, exp, _sig) = self.integer_decode();
+        let (sig, exp, _sig) = self.integer_decode2();
         Unpacked::new(sig, exp)
     }
 
@@ -351,7 +380,7 @@ pub fn prev_float<T: RawFloat>(x: T) -> T {
 pub fn next_float<T: RawFloat>(x: T) -> T {
     match x.classify() {
         Nan => panic!("next_float: argument is NaN"),
-        Infinite => T::infinity(),
+        Infinite => T::infinity2(),
         // This seems too good to be true, but it works.
         // 0.0 is encoded as the all-zero word. Subnormals are 0x000m...m where m is the mantissa.
         // In particular, the smallest subnormal is 0x0...01 and the largest is 0x000F...F.
index c24eaa3eabc754956e1ef4027328b813632b941e..79e1462eaa135eb58013a157fb584f6823b5485f 100644 (file)
@@ -168,7 +168,7 @@ impl Float for f32 {
     /// Returns `true` if the number is infinite.
     #[inline]
     fn is_infinite(self) -> bool {
-        self == Float::infinity() || self == Float::neg_infinity()
+        self == INFINITY || self == NEG_INFINITY
     }
 
     /// Returns `true` if the number is neither infinite or NaN.
@@ -230,7 +230,7 @@ impl Float for f32 {
     #[inline]
     fn signum(self) -> f32 {
         if self.is_nan() {
-            Float::nan()
+            NAN
         } else {
             unsafe { intrinsics::copysignf32(1.0, self) }
         }
@@ -240,14 +240,14 @@ impl Float for f32 {
     /// `Float::infinity()`.
     #[inline]
     fn is_sign_positive(self) -> bool {
-        self > 0.0 || (1.0 / self) == Float::infinity()
+        self > 0.0 || (1.0 / self) == INFINITY
     }
 
     /// Returns `true` if `self` is negative, including `-0.0` and
     /// `Float::neg_infinity()`.
     #[inline]
     fn is_sign_negative(self) -> bool {
-        self < 0.0 || (1.0 / self) == Float::neg_infinity()
+        self < 0.0 || (1.0 / self) == NEG_INFINITY
     }
 
     /// Returns the reciprocal (multiplicative inverse) of the number.
index beeee80902525f16c0684bda05e2debc1832d99f..35557f61c45420b5ff291aa369876a63e94be7aa 100644 (file)
@@ -168,7 +168,7 @@ impl Float for f64 {
     /// Returns `true` if the number is infinite.
     #[inline]
     fn is_infinite(self) -> bool {
-        self == Float::infinity() || self == Float::neg_infinity()
+        self == INFINITY || self == NEG_INFINITY
     }
 
     /// Returns `true` if the number is neither infinite or NaN.
@@ -230,7 +230,7 @@ impl Float for f64 {
     #[inline]
     fn signum(self) -> f64 {
         if self.is_nan() {
-            Float::nan()
+            NAN
         } else {
             unsafe { intrinsics::copysignf64(1.0, self) }
         }
@@ -240,14 +240,14 @@ impl Float for f64 {
     /// `Float::infinity()`.
     #[inline]
     fn is_sign_positive(self) -> bool {
-        self > 0.0 || (1.0 / self) == Float::infinity()
+        self > 0.0 || (1.0 / self) == INFINITY
     }
 
     /// Returns `true` if `self` is negative, including `-0.0` and
     /// `Float::neg_infinity()`.
     #[inline]
     fn is_sign_negative(self) -> bool {
-        self < 0.0 || (1.0 / self) == Float::neg_infinity()
+        self < 0.0 || (1.0 / self) == NEG_INFINITY
     }
 
     /// Returns the reciprocal (multiplicative inverse) of the number.
index 6265691bde9e9ff278c1d73aa50b76d02653b4ca..5420e7bdd2a5a064092b398387c89ec884c97c6b 100644 (file)
@@ -13,7 +13,8 @@
 use prelude::v1::*;
 
 use {f32, f64};
-use num::{Float, FpCategory};
+use num::FpCategory;
+use num::dec2flt::rawfp::RawFloat;
 
 /// Decoded unsigned finite value, such that:
 ///
@@ -52,7 +53,7 @@ pub enum FullDecoded {
 }
 
 /// A floating point type which can be `decode`d.
-pub trait DecodableFloat: Float + Copy {
+pub trait DecodableFloat: RawFloat + Copy {
     /// The minimum positive normalized value.
     fn min_pos_norm_value() -> Self;
 }
@@ -68,7 +69,7 @@ impl DecodableFloat for f64 {
 /// Returns a sign (true when negative) and `FullDecoded` value
 /// from given floating point number.
 pub fn decode<T: DecodableFloat>(v: T) -> (/*negative?*/ bool, FullDecoded) {
-    let (mant, exp, sign) = v.integer_decode();
+    let (mant, exp, sign) = v.integer_decode2();
     let even = (mant & 1) == 0;
     let decoded = match v.classify() {
         FpCategory::Nan => FullDecoded::Nan,
@@ -82,7 +83,7 @@ pub fn decode<T: DecodableFloat>(v: T) -> (/*negative?*/ bool, FullDecoded) {
                                           exp: exp, inclusive: even })
         }
         FpCategory::Normal => {
-            let minnorm = <T as DecodableFloat>::min_pos_norm_value().integer_decode();
+            let minnorm = <T as DecodableFloat>::min_pos_norm_value().integer_decode2();
             if mant == minnorm.0 {
                 // neighbors: (maxmant, exp - 1) -- (minnormmant, exp) -- (minnormmant + 1, exp)
                 // where maxmant = minnormmant * 2 - 1
index fb1a3bbe3b4fbfeab57972add09e3dd9b260d549..bd6cfc427affd04a936a2a115440c5989eb7606f 100644 (file)
 
 #![doc(hidden)]
 
-#[cfg(stage0)]
-macro_rules! int_module { ($T:ty, $bits:expr) => (
-
-// FIXME(#11621): Should be deprecated once CTFE is implemented in favour of
-// calling the `Bounded::min_value` function.
-#[stable(feature = "rust1", since = "1.0.0")]
-#[allow(missing_docs)]
-pub const MIN: $T = (-1 as $T) << ($bits - 1);
-// FIXME(#9837): Compute MIN like this so the high bits that shouldn't exist are 0.
-// FIXME(#11621): Should be deprecated once CTFE is implemented in favour of
-// calling the `Bounded::max_value` function.
-#[stable(feature = "rust1", since = "1.0.0")]
-#[allow(missing_docs)]
-pub const MAX: $T = !MIN;
-
-) }
-
-#[cfg(not(stage0))]
 macro_rules! int_module { ($T:ident, $bits:expr) => (
 
 #[stable(feature = "rust1", since = "1.0.0")]
index de5b1777f936f6b86fd7beec86472f026b5f6d8c..86bcef4011d02177f10d8109672575c7f0b6b35e 100644 (file)
@@ -14,6 +14,8 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
+#[cfg(target_pointer_width = "16")]
+int_module! { isize, 16 }
 #[cfg(target_pointer_width = "32")]
 int_module! { isize, 32 }
 #[cfg(target_pointer_width = "64")]
index 9b6f6698defe4513e636ac4a47c02b750bdd2d09..0d79398a8f1d51bd26e975a0a45fe16fe5b58aab 100644 (file)
@@ -66,6 +66,34 @@ impl<T: fmt::Display> fmt::Display for Wrapping<T> {
     }
 }
 
+#[stable(feature = "wrapping_fmt", since = "1.11.0")]
+impl<T: fmt::Binary> fmt::Binary for Wrapping<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+#[stable(feature = "wrapping_fmt", since = "1.11.0")]
+impl<T: fmt::Octal> fmt::Octal for Wrapping<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+#[stable(feature = "wrapping_fmt", since = "1.11.0")]
+impl<T: fmt::LowerHex> fmt::LowerHex for Wrapping<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+#[stable(feature = "wrapping_fmt", since = "1.11.0")]
+impl<T: fmt::UpperHex> fmt::UpperHex for Wrapping<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
 mod wrapping;
 
 // All these modules are technically private and only exposed for libcoretest:
@@ -81,6 +109,8 @@ pub mod diy_float;
 #[unstable(feature = "zero_one",
            reason = "unsure of placement, wants to use associated constants",
            issue = "27739")]
+#[rustc_deprecated(since = "1.11.0", reason = "no longer used for \
+                                               Iterator::sum")]
 pub trait Zero: Sized {
     /// The "zero" (usually, additive identity) for this type.
     fn zero() -> Self;
@@ -93,6 +123,8 @@ pub trait Zero: Sized {
 #[unstable(feature = "zero_one",
            reason = "unsure of placement, wants to use associated constants",
            issue = "27739")]
+#[rustc_deprecated(since = "1.11.0", reason = "no longer used for \
+                                               Iterator::product")]
 pub trait One: Sized {
     /// The "one" (usually, multiplicative identity) for this type.
     fn one() -> Self;
@@ -103,6 +135,7 @@ macro_rules! zero_one_impl {
         #[unstable(feature = "zero_one",
                    reason = "unsure of placement, wants to use associated constants",
                    issue = "27739")]
+        #[allow(deprecated)]
         impl Zero for $t {
             #[inline]
             fn zero() -> Self { 0 }
@@ -110,6 +143,7 @@ macro_rules! zero_one_impl {
         #[unstable(feature = "zero_one",
                    reason = "unsure of placement, wants to use associated constants",
                    issue = "27739")]
+        #[allow(deprecated)]
         impl One for $t {
             #[inline]
             fn one() -> Self { 1 }
@@ -123,6 +157,7 @@ macro_rules! zero_one_impl_float {
         #[unstable(feature = "zero_one",
                    reason = "unsure of placement, wants to use associated constants",
                    issue = "27739")]
+        #[allow(deprecated)]
         impl Zero for $t {
             #[inline]
             fn zero() -> Self { 0.0 }
@@ -130,6 +165,7 @@ macro_rules! zero_one_impl_float {
         #[unstable(feature = "zero_one",
                    reason = "unsure of placement, wants to use associated constants",
                    issue = "27739")]
+        #[allow(deprecated)]
         impl One for $t {
             #[inline]
             fn one() -> Self { 1.0 }
@@ -576,7 +612,7 @@ macro_rules! int_impl {
         pub fn saturating_add(self, other: Self) -> Self {
             match self.checked_add(other) {
                 Some(x) => x,
-                None if other >= Self::zero() => Self::max_value(),
+                None if other >= 0 => Self::max_value(),
                 None => Self::min_value(),
             }
         }
@@ -597,7 +633,7 @@ macro_rules! int_impl {
         pub fn saturating_sub(self, other: Self) -> Self {
             match self.checked_sub(other) {
                 Some(x) => x,
-                None if other >= Self::zero() => Self::min_value(),
+                None if other >= 0 => Self::min_value(),
                 None => Self::max_value(),
             }
         }
@@ -1033,10 +1069,10 @@ macro_rules! int_impl {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
-        #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
+        #[rustc_inherit_overflow_checks]
         pub fn pow(self, mut exp: u32) -> Self {
             let mut base = self;
-            let mut acc = Self::one();
+            let mut acc = 1;
 
             while exp > 1 {
                 if (exp & 1) == 1 {
@@ -1075,7 +1111,7 @@ macro_rules! int_impl {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
-        #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
+        #[rustc_inherit_overflow_checks]
         pub fn abs(self) -> Self {
             if self.is_negative() {
                 // Note that the #[inline] above means that the overflow
@@ -1176,6 +1212,15 @@ impl i64 {
         intrinsics::mul_with_overflow }
 }
 
+#[cfg(target_pointer_width = "16")]
+#[lang = "isize"]
+impl isize {
+    int_impl! { i16, u16, 16,
+        intrinsics::add_with_overflow,
+        intrinsics::sub_with_overflow,
+        intrinsics::mul_with_overflow }
+}
+
 #[cfg(target_pointer_width = "32")]
 #[lang = "isize"]
 impl isize {
@@ -2052,10 +2097,10 @@ macro_rules! uint_impl {
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
-        #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
+        #[rustc_inherit_overflow_checks]
         pub fn pow(self, mut exp: u32) -> Self {
             let mut base = self;
-            let mut acc = Self::one();
+            let mut acc = 1;
 
             let mut prev_base = self;
             let mut base_oflo = false;
@@ -2092,8 +2137,7 @@ macro_rules! uint_impl {
         #[stable(feature = "rust1", since = "1.0.0")]
         #[inline]
         pub fn is_power_of_two(self) -> bool {
-            (self.wrapping_sub(Self::one())) & self == Self::zero() &&
-                !(self == Self::zero())
+            (self.wrapping_sub(1)) & self == 0 && !(self == 0)
         }
 
         /// Returns the smallest power of two greater than or equal to `self`.
@@ -2111,7 +2155,7 @@ macro_rules! uint_impl {
         #[inline]
         pub fn next_power_of_two(self) -> Self {
             let bits = size_of::<Self>() * 8;
-            let one: Self = Self::one();
+            let one: Self = 1;
             one << ((bits - self.wrapping_sub(one).leading_zeros() as usize) % bits)
         }
 
@@ -2188,6 +2232,18 @@ impl u64 {
         intrinsics::mul_with_overflow }
 }
 
+#[cfg(target_pointer_width = "16")]
+#[lang = "usize"]
+impl usize {
+    uint_impl! { u16, 16,
+        intrinsics::ctpop,
+        intrinsics::ctlz,
+        intrinsics::cttz,
+        intrinsics::bswap,
+        intrinsics::add_with_overflow,
+        intrinsics::sub_with_overflow,
+        intrinsics::mul_with_overflow }
+}
 #[cfg(target_pointer_width = "32")]
 #[lang = "usize"]
 impl usize {
@@ -2254,26 +2310,44 @@ pub trait Float: Sized {
     /// Returns the NaN value.
     #[unstable(feature = "float_extras", reason = "needs removal",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     fn nan() -> Self;
     /// Returns the infinite value.
     #[unstable(feature = "float_extras", reason = "needs removal",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     fn infinity() -> Self;
     /// Returns the negative infinite value.
     #[unstable(feature = "float_extras", reason = "needs removal",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     fn neg_infinity() -> Self;
     /// Returns -0.0.
     #[unstable(feature = "float_extras", reason = "needs removal",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     fn neg_zero() -> Self;
     /// Returns 0.0.
     #[unstable(feature = "float_extras", reason = "needs removal",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     fn zero() -> Self;
     /// Returns 1.0.
     #[unstable(feature = "float_extras", reason = "needs removal",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     fn one() -> Self;
 
     /// Returns true if this value is NaN and false otherwise.
@@ -2296,6 +2370,9 @@ pub trait Float: Sized {
     /// Returns the mantissa, exponent and sign as integers, respectively.
     #[unstable(feature = "float_extras", reason = "signature is undecided",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     fn integer_decode(self) -> (u64, i16, i8);
 
     /// Computes the absolute value of `self`. Returns `Float::nan()` if the
@@ -2330,12 +2407,10 @@ pub trait Float: Sized {
     fn powi(self, n: i32) -> Self;
 
     /// Convert radians to degrees.
-    #[unstable(feature = "float_extras", reason = "desirability is unclear",
-               issue = "27752")]
+    #[stable(feature = "deg_rad_conversions", since="1.7.0")]
     fn to_degrees(self) -> Self;
     /// Convert degrees to radians.
-    #[unstable(feature = "float_extras", reason = "desirability is unclear",
-               issue = "27752")]
+    #[stable(feature = "deg_rad_conversions", since="1.7.0")]
     fn to_radians(self) -> Self;
 }
 
index af6b1b89f96d571fa9e2971f62e0200f9d5d1d31..2ab2f9548ef1bfd5ec67ccfb79b1a43efa333329 100644 (file)
 
 #![doc(hidden)]
 
-#[cfg(stage0)]
-macro_rules! uint_module { ($T:ty, $bits:expr) => (
-
-#[stable(feature = "rust1", since = "1.0.0")]
-#[allow(missing_docs)]
-pub const MIN: $T = 0 as $T;
-#[stable(feature = "rust1", since = "1.0.0")]
-#[allow(missing_docs)]
-pub const MAX: $T = !0 as $T;
-
-) }
-
-#[cfg(not(stage0))]
 macro_rules! uint_module { ($T:ident, $bits:expr) => (
 
 #[stable(feature = "rust1", since = "1.0.0")]
index 0c7d16a41bc7e8968b6501b6c57378097ee939e0..685c52e271ec0c924ab9ca495b45b231bd7754aa 100644 (file)
@@ -14,6 +14,8 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
+#[cfg(target_pointer_width = "16")]
+uint_module! { usize, 16 }
 #[cfg(target_pointer_width = "32")]
 uint_module! { usize, 32 }
 #[cfg(target_pointer_width = "64")]
index 04e8bc4913bdc96cc5ce66f7a371f475c2dddbf0..4857817e84e4fcc17bdc23401643f1d5ead6e534 100644 (file)
@@ -292,6 +292,12 @@ wrapping_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
 mod shift_max {
     #![allow(non_upper_case_globals)]
 
+    #[cfg(target_pointer_width = "16")]
+    mod platform {
+        pub const usize: u32 = super::u16;
+        pub const isize: u32 = super::i16;
+    }
+
     #[cfg(target_pointer_width = "32")]
     mod platform {
         pub const usize: u32 = super::u32;
index a2f84230afc44e403dc66154e9fde7320033ef38..7753aae147a88d722d532d7198e1d52bf7b3e9cd 100644 (file)
@@ -69,9 +69,7 @@
 
 use cmp::PartialOrd;
 use fmt;
-use convert::From;
 use marker::{Sized, Unsize};
-use num::One;
 
 /// The `Drop` trait is used to run some code when a value goes out of scope.
 /// This is sometimes called a 'destructor'.
@@ -208,6 +206,7 @@ macro_rules! add_impl {
             type Output = $t;
 
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn add(self, other: $t) -> $t { self + other }
         }
 
@@ -261,6 +260,7 @@ macro_rules! sub_impl {
             type Output = $t;
 
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn sub(self, other: $t) -> $t { self - other }
         }
 
@@ -314,6 +314,7 @@ macro_rules! mul_impl {
             type Output = $t;
 
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn mul(self, other: $t) -> $t { self * other }
         }
 
@@ -511,6 +512,7 @@ macro_rules! neg_impl_core {
             type Output = $t;
 
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn neg(self) -> $t { let $id = self; $body }
         }
 
@@ -788,6 +790,7 @@ macro_rules! shl_impl {
             type Output = $t;
 
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn shl(self, other: $f) -> $t {
                 self << other
             }
@@ -859,6 +862,7 @@ macro_rules! shr_impl {
             type Output = $t;
 
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn shr(self, other: $f) -> $t {
                 self >> other
             }
@@ -923,6 +927,7 @@ macro_rules! add_assign_impl {
         #[stable(feature = "op_assign_traits", since = "1.8.0")]
         impl AddAssign for $t {
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn add_assign(&mut self, other: $t) { *self += other }
         }
     )+)
@@ -967,6 +972,7 @@ macro_rules! sub_assign_impl {
         #[stable(feature = "op_assign_traits", since = "1.8.0")]
         impl SubAssign for $t {
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn sub_assign(&mut self, other: $t) { *self -= other }
         }
     )+)
@@ -1011,6 +1017,7 @@ macro_rules! mul_assign_impl {
         #[stable(feature = "op_assign_traits", since = "1.8.0")]
         impl MulAssign for $t {
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn mul_assign(&mut self, other: $t) { *self *= other }
         }
     )+)
@@ -1275,6 +1282,7 @@ macro_rules! shl_assign_impl {
         #[stable(feature = "op_assign_traits", since = "1.8.0")]
         impl ShlAssign<$f> for $t {
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn shl_assign(&mut self, other: $f) {
                 *self <<= other
             }
@@ -1337,6 +1345,7 @@ macro_rules! shr_assign_impl {
         #[stable(feature = "op_assign_traits", since = "1.8.0")]
         impl ShrAssign<$f> for $t {
             #[inline]
+            #[rustc_inherit_overflow_checks]
             fn shr_assign(&mut self, other: $f) {
                 *self >>= other
             }
@@ -1464,7 +1473,7 @@ pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
 ///     assert_eq!(arr[1..3], [  1,2  ]);
 /// }
 /// ```
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct RangeFull;
 
@@ -1483,7 +1492,6 @@ impl fmt::Debug for RangeFull {
 /// # Examples
 ///
 /// ```
-/// #![feature(iter_arith)]
 /// fn main() {
 ///     assert_eq!((3..5), std::ops::Range{ start: 3, end: 5 });
 ///     assert_eq!(3+4+5, (3..6).sum());
@@ -1495,7 +1503,7 @@ impl fmt::Debug for RangeFull {
 ///     assert_eq!(arr[1..3], [  1,2  ]);  // Range
 /// }
 /// ```
-#[derive(Clone, PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq, Hash)]  // not Copy -- see #27186
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Range<Idx> {
     /// The lower bound of the range (inclusive).
@@ -1547,7 +1555,6 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
 /// # Examples
 ///
 /// ```
-/// #![feature(iter_arith)]
 /// fn main() {
 ///     assert_eq!((2..), std::ops::RangeFrom{ start: 2 });
 ///     assert_eq!(2+3+4, (2..).take(3).sum());
@@ -1559,7 +1566,7 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
 ///     assert_eq!(arr[1..3], [  1,2  ]);
 /// }
 /// ```
-#[derive(Clone, PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq, Hash)]  // not Copy -- see #27186
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct RangeFrom<Idx> {
     /// The lower bound of the range (inclusive).
@@ -1597,6 +1604,7 @@ impl<Idx: PartialOrd<Idx>> RangeFrom<Idx> {
 /// See the [`contains()`](#method.contains) method for its characterization.
 ///
 /// It cannot serve as an iterator because it doesn't have a starting point.
+///
 /// ```
 /// fn main() {
 ///     assert_eq!((..5), std::ops::RangeTo{ end: 5 });
@@ -1608,7 +1616,7 @@ impl<Idx: PartialOrd<Idx>> RangeFrom<Idx> {
 ///     assert_eq!(arr[1..3], [  1,2  ]);
 /// }
 /// ```
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct RangeTo<Idx> {
     /// The upper bound of the range (exclusive).
@@ -1648,7 +1656,7 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
 /// # Examples
 ///
 /// ```
-/// #![feature(inclusive_range,inclusive_range_syntax,iter_arith)]
+/// #![feature(inclusive_range,inclusive_range_syntax)]
 /// fn main() {
 ///     assert_eq!((3...5), std::ops::RangeInclusive::NonEmpty{ start: 3, end: 5 });
 ///     assert_eq!(3+4+5, (3...5).sum());
@@ -1658,7 +1666,7 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
 ///     assert_eq!(arr[1...2], [  1,2  ]);  // RangeInclusive
 /// }
 /// ```
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq, Hash)]  // not Copy -- see #27186
 #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
 pub enum RangeInclusive<Idx> {
     /// Empty range (iteration has finished)
@@ -1702,24 +1710,6 @@ impl<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> {
     }
 }
 
-#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
-impl<Idx: PartialOrd + One + Sub<Output=Idx>> From<Range<Idx>> for RangeInclusive<Idx> {
-    fn from(range: Range<Idx>) -> RangeInclusive<Idx> {
-        use self::RangeInclusive::*;
-
-        if range.start < range.end {
-            NonEmpty {
-                start: range.start,
-                end: range.end - Idx::one() // can't underflow because end > start >= MIN
-            }
-        } else {
-            Empty {
-                at: range.start
-            }
-        }
-    }
-}
-
 #[unstable(feature = "range_contains", reason = "recently added as per RFC", issue = "32311")]
 impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
     /// # Examples
@@ -1763,7 +1753,7 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
 ///     assert_eq!(arr[1...2], [  1,2  ]);
 /// }
 /// ```
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
 pub struct RangeToInclusive<Idx> {
     /// The upper bound of the range (inclusive)
index 19226d81f16882000966fcd514a046d1f16abe86..6b2122451db8fbf164190d84d485a09067229113 100644 (file)
 //!
 //! Their definition should always match the ABI defined in `rustc::back::abi`.
 
-use clone::Clone;
-use marker::Copy;
-use mem;
-
-/// The representation of a slice like `&[T]`.
-///
-/// This struct is guaranteed to have the layout of types like `&[T]`,
-/// `&str`, and `Box<[T]>`, but is not the type of such slices
-/// (e.g. the fields are not directly accessible on a `&[T]`) nor does
-/// it control that layout (changing the definition will not change
-/// the layout of a `&[T]`). It is only designed to be used by unsafe
-/// code that needs to manipulate the low-level details.
-///
-/// However, it is not recommended to use this type for such code,
-/// since there are alternatives which may be safer:
-///
-/// - Creating a slice from a data pointer and length can be done with
-///   `std::slice::from_raw_parts` or `std::slice::from_raw_parts_mut`
-///   instead of `std::mem::transmute`ing a value of type `Slice`.
-/// - Extracting the data pointer and length from a slice can be
-///   performed with the `as_ptr` (or `as_mut_ptr`) and `len`
-///   methods.
-///
-/// If one does decide to convert a slice value to a `Slice`, the
-/// `Repr` trait in this module provides a method for a safe
-/// conversion from `&[T]` (and `&str`) to a `Slice`, more type-safe
-/// than a call to `transmute`.
-///
-/// # Examples
-///
-/// ```
-/// #![feature(raw)]
-///
-/// use std::raw::{self, Repr};
-///
-/// let slice: &[u16] = &[1, 2, 3, 4];
-///
-/// let repr: raw::Slice<u16> = slice.repr();
-/// println!("data pointer = {:?}, length = {}", repr.data, repr.len);
-/// ```
-#[repr(C)]
-#[allow(missing_debug_implementations)]
-#[rustc_deprecated(reason = "use raw accessors/constructors in `slice` module",
-                   since = "1.9.0")]
-#[unstable(feature = "raw", issue = "27751")]
-pub struct Slice<T> {
-    pub data: *const T,
-    pub len: usize,
-}
-
-#[allow(deprecated)]
-impl<T> Copy for Slice<T> {}
-#[allow(deprecated)]
-impl<T> Clone for Slice<T> {
-    fn clone(&self) -> Slice<T> { *self }
-}
-
 /// The representation of a trait object like `&SomeTrait`.
 ///
 /// This struct has the same layout as types like `&SomeTrait` and
@@ -154,22 +97,3 @@ pub struct TraitObject {
     pub data: *mut (),
     pub vtable: *mut (),
 }
-
-/// This trait is meant to map equivalences between raw structs and their
-/// corresponding rust values.
-#[rustc_deprecated(reason = "use raw accessors/constructors in `slice` module",
-                   since = "1.9.0")]
-#[unstable(feature = "raw", issue = "27751")]
-pub unsafe trait Repr<T> {
-    /// This function "unwraps" a rust value (without consuming it) into its raw
-    /// struct representation. This can be used to read/write different values
-    /// for the struct. This is a safe method because by default it does not
-    /// enable write-access to the fields of the return value in safe code.
-    #[inline]
-    fn repr(&self) -> T { unsafe { mem::transmute_copy(&self) } }
-}
-
-#[allow(deprecated)]
-unsafe impl<T> Repr<Slice<T>> for [T] {}
-#[allow(deprecated)]
-unsafe impl Repr<Slice<u8>> for str {}
index 4d9f042fddedc03db74c4647848032836efdae04..94c6c636ce8fce6469f374dbfdb730f60051ea3b 100644 (file)
 //! }
 //!
 //! fn write_info(info: &Info) -> io::Result<()> {
-//!     let mut file = try!(File::create("my_best_friends.txt"));
 //!     // Early return on error
+//!     let mut file = match File::create("my_best_friends.txt") {
+//!            Err(e) => return Err(e),
+//!            Ok(f) => f,
+//!     };
 //!     if let Err(e) = file.write_all(format!("name: {}\n", info.name).as_bytes()) {
 //!         return Err(e)
 //!     }
index a0e978f783d73a2932ce8432de28e56ecb8a2600..4f11cac4eb2bec7b7e7ba6c4346e78fd5f3ed354 100644 (file)
@@ -50,6 +50,7 @@ use result::Result::{Ok, Err};
 use ptr;
 use mem;
 use marker::{Copy, Send, Sync, self};
+use iter_private::TrustedRandomAccess;
 
 #[repr(C)]
 struct Repr<T> {
@@ -554,7 +555,6 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! {
     panic!("slice index starts at {} but ends at {}", index, end);
 }
 
-// FIXME implement indexing with inclusive ranges
 
 /// Implements slicing with syntax `&self[begin .. end]`.
 ///
@@ -622,7 +622,7 @@ impl<T> ops::Index<ops::RangeFrom<usize>> for [T] {
 
 /// Implements slicing with syntax `&self[..]`.
 ///
-/// Returns a slice of the whole slice. This operation can not panic.
+/// Returns a slice of the whole slice. This operation cannot panic.
 ///
 /// Equivalent to `&self[0 .. self.len()]`
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -835,7 +835,7 @@ macro_rules! iterator {
 
             #[inline]
             fn count(self) -> usize {
-                self.size_hint().0
+                self.len()
             }
 
             #[inline]
@@ -1444,7 +1444,7 @@ impl<'a, T> Iterator for Windows<'a, T> {
 
     #[inline]
     fn count(self) -> usize {
-        self.size_hint().0
+        self.len()
     }
 
     #[inline]
@@ -1541,7 +1541,7 @@ impl<'a, T> Iterator for Chunks<'a, T> {
 
     #[inline]
     fn count(self) -> usize {
-        self.size_hint().0
+        self.len()
     }
 
     #[inline]
@@ -1632,7 +1632,7 @@ impl<'a, T> Iterator for ChunksMut<'a, T> {
 
     #[inline]
     fn count(self) -> usize {
-        self.size_hint().0
+        self.len()
     }
 
     #[inline]
@@ -1831,6 +1831,9 @@ impl<A> SlicePartialEq<A> for [A]
         if self.len() != other.len() {
             return false;
         }
+        if self.as_ptr() == other.as_ptr() {
+            return true;
+        }
         unsafe {
             let size = mem::size_of_val(self);
             memcmp(self.as_ptr() as *const u8,
@@ -1940,3 +1943,17 @@ macro_rules! impl_marker_for {
 
 impl_marker_for!(BytewiseEquality,
                  u8 i8 u16 i16 u32 i32 u64 i64 usize isize char bool);
+
+#[doc(hidden)]
+unsafe impl<'a, T> TrustedRandomAccess for Iter<'a, T> {
+    unsafe fn get_unchecked(&mut self, i: usize) -> &'a T {
+        &*self.ptr.offset(i as isize)
+    }
+}
+
+#[doc(hidden)]
+unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> {
+    unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T {
+        &mut *self.ptr.offset(i as isize)
+    }
+}
index 2c34caf63b846eeabad7b59d3c0d33cc8a256fbb..a32c9da9815ffc0a19d0cfdeec1138ccd6097b3d 100644 (file)
@@ -17,7 +17,7 @@
 use self::pattern::Pattern;
 use self::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher};
 
-use char::{self, CharExt};
+use char;
 use clone::Clone;
 use convert::AsRef;
 use default::Default;
@@ -354,7 +354,7 @@ fn unwrap_or_0(opt: Option<&u8>) -> u8 {
 /// UTF-8-like encoding).
 #[unstable(feature = "str_internals", issue = "0")]
 #[inline]
-pub fn next_code_point(bytes: &mut slice::Iter<u8>) -> Option<u32> {
+pub fn next_code_point<'a, I: Iterator<Item = &'a u8>>(bytes: &mut I) -> Option<u32> {
     // Decode UTF-8
     let x = match bytes.next() {
         None => return None,
@@ -388,7 +388,8 @@ pub fn next_code_point(bytes: &mut slice::Iter<u8>) -> Option<u32> {
 /// Reads the last code point out of a byte iterator (assuming a
 /// UTF-8-like encoding).
 #[inline]
-fn next_code_point_reverse(bytes: &mut slice::Iter<u8>) -> Option<u32> {
+fn next_code_point_reverse<'a,
+                           I: DoubleEndedIterator<Item = &'a u8>>(bytes: &mut I) -> Option<u32> {
     // Decode UTF-8
     let w = match bytes.next_back() {
         None => return None,
@@ -432,7 +433,7 @@ impl<'a> Iterator for Chars<'a> {
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let (len, _) = self.iter.size_hint();
+        let len = self.iter.len();
         // `(len + 3)` can't overflow, because we know that the `slice::Iter`
         // belongs to a slice in memory which has a maximum length of
         // `isize::MAX` (that's well below `usize::MAX`).
@@ -479,12 +480,12 @@ impl<'a> Iterator for CharIndices<'a> {
 
     #[inline]
     fn next(&mut self) -> Option<(usize, char)> {
-        let (pre_len, _) = self.iter.iter.size_hint();
+        let pre_len = self.iter.iter.len();
         match self.iter.next() {
             None => None,
             Some(ch) => {
                 let index = self.front_offset;
-                let (len, _) = self.iter.iter.size_hint();
+                let len = self.iter.iter.len();
                 self.front_offset += pre_len - len;
                 Some((index, ch))
             }
@@ -504,8 +505,7 @@ impl<'a> DoubleEndedIterator for CharIndices<'a> {
         match self.iter.next_back() {
             None => None,
             Some(ch) => {
-                let (len, _) = self.iter.iter.size_hint();
-                let index = self.front_offset + len;
+                let index = self.front_offset + self.iter.iter.len();
                 Some((index, ch))
             }
         }
@@ -1291,22 +1291,6 @@ static UTF8_CHAR_WIDTH: [u8; 256] = [
 4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
 ];
 
-/// Struct that contains a `char` and the index of the first byte of
-/// the next `char` in a string.  This can be used as a data structure
-/// for iterating over the UTF-8 bytes of a string.
-#[derive(Copy, Clone, Debug)]
-#[unstable(feature = "str_char",
-           reason = "existence of this struct is uncertain as it is frequently \
-                     able to be replaced with char.len_utf8() and/or \
-                     char/char_indices iterators",
-           issue = "27754")]
-pub struct CharRange {
-    /// Current `char`
-    pub ch: char,
-    /// Index of the first byte of the next `char`
-    pub next: usize,
-}
-
 /// Mask of the value bits of a continuation byte
 const CONT_MASK: u8 = 0b0011_1111;
 /// Value of the tag bits (tag mask is !CONT_MASK) of a continuation byte
@@ -1663,40 +1647,6 @@ pub trait StrExt {
         where P::Searcher: ReverseSearcher<'a>;
     #[stable(feature = "is_char_boundary", since = "1.9.0")]
     fn is_char_boundary(&self, index: usize) -> bool;
-    #[unstable(feature = "str_char",
-               reason = "often replaced by char_indices, this method may \
-                         be removed in favor of just char_at() or eventually \
-                         removed altogether",
-               issue = "27754")]
-    #[rustc_deprecated(reason = "use slicing plus chars() plus len_utf8",
-                       since = "1.9.0")]
-    fn char_range_at(&self, start: usize) -> CharRange;
-    #[unstable(feature = "str_char",
-               reason = "often replaced by char_indices, this method may \
-                         be removed in favor of just char_at_reverse() or \
-                         eventually removed altogether",
-               issue = "27754")]
-    #[rustc_deprecated(reason = "use slicing plus chars().rev() plus len_utf8",
-                       since = "1.9.0")]
-    fn char_range_at_reverse(&self, start: usize) -> CharRange;
-    #[unstable(feature = "str_char",
-               reason = "frequently replaced by the chars() iterator, this \
-                         method may be removed or possibly renamed in the \
-                         future; it is normally replaced by chars/char_indices \
-                         iterators or by getting the first char from a \
-                         subslice",
-               issue = "27754")]
-    #[rustc_deprecated(reason = "use slicing plus chars()",
-                       since = "1.9.0")]
-    fn char_at(&self, i: usize) -> char;
-    #[unstable(feature = "str_char",
-               reason = "see char_at for more details, but reverse semantics \
-                         are also somewhat unclear, especially with which \
-                         cases generate panics",
-               issue = "27754")]
-    #[rustc_deprecated(reason = "use slicing plus chars().rev()",
-                       since = "1.9.0")]
-    fn char_at_reverse(&self, i: usize) -> char;
     #[stable(feature = "core", since = "1.6.0")]
     fn as_bytes(&self) -> &[u8];
     #[stable(feature = "core", since = "1.6.0")]
@@ -1709,14 +1659,6 @@ pub trait StrExt {
     fn split_at(&self, mid: usize) -> (&str, &str);
     #[stable(feature = "core", since = "1.6.0")]
     fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str);
-    #[unstable(feature = "str_char",
-               reason = "awaiting conventions about shifting and slices and \
-                         may not be warranted with the existence of the chars \
-                         and/or char_indices iterators",
-               issue = "27754")]
-    #[rustc_deprecated(reason = "use chars() plus Chars::as_str",
-                       since = "1.9.0")]
-    fn slice_shift_char(&self) -> Option<(char, &str)>;
     #[stable(feature = "core", since = "1.6.0")]
     fn as_ptr(&self) -> *const u8;
     #[stable(feature = "core", since = "1.6.0")]
@@ -1945,55 +1887,6 @@ impl StrExt for str {
         }
     }
 
-    #[inline]
-    fn char_range_at(&self, i: usize) -> CharRange {
-        let (c, n) = char_range_at_raw(self.as_bytes(), i);
-        CharRange { ch: unsafe { char::from_u32_unchecked(c) }, next: n }
-    }
-
-    #[inline]
-    fn char_range_at_reverse(&self, start: usize) -> CharRange {
-        let mut prev = start;
-
-        prev = prev.saturating_sub(1);
-        if self.as_bytes()[prev] < 128 {
-            return CharRange{ch: self.as_bytes()[prev] as char, next: prev}
-        }
-
-        // Multibyte case is a fn to allow char_range_at_reverse to inline cleanly
-        fn multibyte_char_range_at_reverse(s: &str, mut i: usize) -> CharRange {
-            // while there is a previous byte == 10......
-            while i > 0 && s.as_bytes()[i] & !CONT_MASK == TAG_CONT_U8 {
-                i -= 1;
-            }
-
-            let first= s.as_bytes()[i];
-            let w = UTF8_CHAR_WIDTH[first as usize];
-            assert!(w != 0);
-
-            let mut val = utf8_first_byte(first, w as u32);
-            val = utf8_acc_cont_byte(val, s.as_bytes()[i + 1]);
-            if w > 2 { val = utf8_acc_cont_byte(val, s.as_bytes()[i + 2]); }
-            if w > 3 { val = utf8_acc_cont_byte(val, s.as_bytes()[i + 3]); }
-
-            CharRange {ch: unsafe { char::from_u32_unchecked(val) }, next: i}
-        }
-
-        multibyte_char_range_at_reverse(self, prev)
-    }
-
-    #[inline]
-    #[allow(deprecated)]
-    fn char_at(&self, i: usize) -> char {
-        self.char_range_at(i).ch
-    }
-
-    #[inline]
-    #[allow(deprecated)]
-    fn char_at_reverse(&self, i: usize) -> char {
-        self.char_range_at_reverse(i).ch
-    }
-
     #[inline]
     fn as_bytes(&self) -> &[u8] {
         unsafe { mem::transmute(self) }
@@ -2040,18 +1933,6 @@ impl StrExt for str {
         }
     }
 
-    #[inline]
-    #[allow(deprecated)]
-    fn slice_shift_char(&self) -> Option<(char, &str)> {
-        if self.is_empty() {
-            None
-        } else {
-            let ch = self.char_at(0);
-            let next_s = unsafe { self.slice_unchecked(ch.len_utf8(), self.len()) };
-            Some((ch, next_s))
-        }
-    }
-
     #[inline]
     fn as_ptr(&self) -> *const u8 {
         self as *const str as *const u8
@@ -2077,31 +1958,6 @@ impl AsRef<[u8]> for str {
     }
 }
 
-/// Pluck a code point out of a UTF-8-like byte slice and return the
-/// index of the next code point.
-#[inline]
-fn char_range_at_raw(bytes: &[u8], i: usize) -> (u32, usize) {
-    if bytes[i] < 128 {
-        return (bytes[i] as u32, i + 1);
-    }
-
-    // Multibyte case is a fn to allow char_range_at to inline cleanly
-    fn multibyte_char_range_at(bytes: &[u8], i: usize) -> (u32, usize) {
-        let first = bytes[i];
-        let w = UTF8_CHAR_WIDTH[first as usize];
-        assert!(w != 0);
-
-        let mut val = utf8_first_byte(first, w as u32);
-        val = utf8_acc_cont_byte(val, bytes[i + 1]);
-        if w > 2 { val = utf8_acc_cont_byte(val, bytes[i + 2]); }
-        if w > 3 { val = utf8_acc_cont_byte(val, bytes[i + 3]); }
-
-        (val, i + w as usize)
-    }
-
-    multibyte_char_range_at(bytes, i)
-}
-
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a> Default for &'a str {
     fn default() -> &'a str { "" }
index b803539e12b1a2fd6d91a26d56fb854851168340..53804c611e66ed52dc196b01e68eee88cce6820b 100644 (file)
@@ -310,9 +310,9 @@ unsafe impl<'a, C: CharEq> Searcher<'a> for CharEqSearcher<'a, C> {
         let s = &mut self.char_indices;
         // Compare lengths of the internal byte slice iterator
         // to find length of current char
-        let (pre_len, _) = s.iter.iter.size_hint();
+        let pre_len = s.iter.iter.len();
         if let Some((i, c)) = s.next() {
-            let (len, _) = s.iter.iter.size_hint();
+            let len = s.iter.iter.len();
             let char_len = pre_len - len;
             if self.char_eq.matches(c) {
                 return SearchStep::Match(i, i + char_len);
@@ -330,9 +330,9 @@ unsafe impl<'a, C: CharEq> ReverseSearcher<'a> for CharEqSearcher<'a, C> {
         let s = &mut self.char_indices;
         // Compare lengths of the internal byte slice iterator
         // to find length of current char
-        let (pre_len, _) = s.iter.iter.size_hint();
+        let pre_len = s.iter.iter.len();
         if let Some((i, c)) = s.next_back() {
-            let (len, _) = s.iter.iter.size_hint();
+            let len = s.iter.iter.len();
             let char_len = pre_len - len;
             if self.char_eq.matches(c) {
                 return SearchStep::Match(i, i + char_len);
index d0a64de07e51c2795045b567f395a9b35fe44d28..47d9deb62ff6556501c8588981dc621b4282ca1f 100644 (file)
@@ -88,13 +88,13 @@ use default::Default;
 use fmt;
 
 /// A boolean type which can be safely shared between threads.
-#[cfg(any(stage0, target_has_atomic = "8"))]
+#[cfg(target_has_atomic = "8")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct AtomicBool {
     v: UnsafeCell<u8>,
 }
 
-#[cfg(any(stage0, target_has_atomic = "8"))]
+#[cfg(target_has_atomic = "8")]
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Default for AtomicBool {
     fn default() -> Self {
@@ -103,18 +103,18 @@ impl Default for AtomicBool {
 }
 
 // Send is implicitly implemented for AtomicBool.
-#[cfg(any(stage0, target_has_atomic = "8"))]
+#[cfg(target_has_atomic = "8")]
 #[stable(feature = "rust1", since = "1.0.0")]
 unsafe impl Sync for AtomicBool {}
 
 /// A raw pointer type which can be safely shared between threads.
-#[cfg(any(stage0, target_has_atomic = "ptr"))]
+#[cfg(target_has_atomic = "ptr")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct AtomicPtr<T> {
     p: UnsafeCell<*mut T>,
 }
 
-#[cfg(any(stage0, target_has_atomic = "ptr"))]
+#[cfg(target_has_atomic = "ptr")]
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Default for AtomicPtr<T> {
     fn default() -> AtomicPtr<T> {
@@ -122,10 +122,10 @@ impl<T> Default for AtomicPtr<T> {
     }
 }
 
-#[cfg(any(stage0, target_has_atomic = "ptr"))]
+#[cfg(target_has_atomic = "ptr")]
 #[stable(feature = "rust1", since = "1.0.0")]
 unsafe impl<T> Send for AtomicPtr<T> {}
-#[cfg(any(stage0, target_has_atomic = "ptr"))]
+#[cfg(target_has_atomic = "ptr")]
 #[stable(feature = "rust1", since = "1.0.0")]
 unsafe impl<T> Sync for AtomicPtr<T> {}
 
@@ -167,11 +167,11 @@ pub enum Ordering {
 }
 
 /// An `AtomicBool` initialized to `false`.
-#[cfg(any(stage0, target_has_atomic = "8"))]
+#[cfg(target_has_atomic = "8")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub const ATOMIC_BOOL_INIT: AtomicBool = AtomicBool::new(false);
 
-#[cfg(any(stage0, target_has_atomic = "8"))]
+#[cfg(target_has_atomic = "8")]
 impl AtomicBool {
     /// Creates a new `AtomicBool`.
     ///
@@ -289,7 +289,7 @@ impl AtomicBool {
     /// Stores a value into the `bool` if the current value is the same as the `current` value.
     ///
     /// The return value is a result indicating whether the new value was written and containing
-    /// the previous value. On success this value is guaranteed to be equal to `new`.
+    /// the previous value. On success this value is guaranteed to be equal to `current`.
     ///
     /// `compare_exchange` takes two `Ordering` arguments to describe the memory ordering of this
     /// operation. The first describes the required ordering if the operation succeeds while the
@@ -508,7 +508,7 @@ impl AtomicBool {
     }
 }
 
-#[cfg(any(stage0, target_has_atomic = "ptr"))]
+#[cfg(target_has_atomic = "ptr")]
 impl<T> AtomicPtr<T> {
     /// Creates a new `AtomicPtr`.
     ///
@@ -633,7 +633,7 @@ impl<T> AtomicPtr<T> {
     /// Stores a value into the pointer if the current value is the same as the `current` value.
     ///
     /// The return value is a result indicating whether the new value was written and containing
-    /// the previous value. On success this value is guaranteed to be equal to `new`.
+    /// the previous value. On success this value is guaranteed to be equal to `current`.
     ///
     /// `compare_exchange` takes two `Ordering` arguments to describe the memory ordering of this
     /// operation. The first describes the required ordering if the operation succeeds while the
@@ -886,7 +886,7 @@ macro_rules! atomic_int {
             ///
             /// The return value is a result indicating whether the new value was written and
             /// containing the previous value. On success this value is guaranteed to be equal to
-            /// `new`.
+            /// `current`.
             ///
             /// `compare_exchange` takes two `Ordering` arguments to describe the memory ordering of
             /// this operation. The first describes the required ordering if the operation succeeds
@@ -1106,14 +1106,14 @@ atomic_int! {
     unstable(feature = "integer_atomics", issue = "32976"),
     u64 AtomicU64 ATOMIC_U64_INIT
 }
-#[cfg(any(stage0, target_has_atomic = "ptr"))]
+#[cfg(target_has_atomic = "ptr")]
 atomic_int!{
     stable(feature = "rust1", since = "1.0.0"),
     stable(feature = "extended_compare_and_swap", since = "1.10.0"),
     stable(feature = "atomic_debug", since = "1.3.0"),
     isize AtomicIsize ATOMIC_ISIZE_INIT
 }
-#[cfg(any(stage0, target_has_atomic = "ptr"))]
+#[cfg(target_has_atomic = "ptr")]
 atomic_int!{
     stable(feature = "rust1", since = "1.0.0"),
     stable(feature = "extended_compare_and_swap", since = "1.10.0"),
@@ -1311,7 +1311,7 @@ pub fn fence(order: Ordering) {
 }
 
 
-#[cfg(any(stage0, target_has_atomic = "8"))]
+#[cfg(target_has_atomic = "8")]
 #[stable(feature = "atomic_debug", since = "1.3.0")]
 impl fmt::Debug for AtomicBool {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -1319,7 +1319,7 @@ impl fmt::Debug for AtomicBool {
     }
 }
 
-#[cfg(any(stage0, target_has_atomic = "ptr"))]
+#[cfg(target_has_atomic = "ptr")]
 #[stable(feature = "atomic_debug", since = "1.3.0")]
 impl<T> fmt::Debug for AtomicPtr<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
index c0b22274ee9d628f09c36b4b7081629844d102f5..a635620d12abd3618e3acda562b0c2ed49686153 100644 (file)
@@ -158,20 +158,6 @@ fn ref_map_accessor() {
     assert_eq!(*d, 7);
 }
 
-#[test]
-#[allow(deprecated)]
-fn ref_filter_map_accessor() {
-    struct X(RefCell<Result<u32, ()>>);
-    impl X {
-        fn accessor(&self) -> Option<Ref<u32>> {
-            Ref::filter_map(self.0.borrow(), |r| r.as_ref().ok())
-        }
-    }
-    let x = X(RefCell::new(Ok(7)));
-    let d: Ref<u32> = x.accessor().unwrap();
-    assert_eq!(*d, 7);
-}
-
 #[test]
 fn ref_mut_map_accessor() {
     struct X(RefCell<(u32, char)>);
@@ -189,24 +175,6 @@ fn ref_mut_map_accessor() {
     assert_eq!(*x.0.borrow(), (8, 'z'));
 }
 
-#[test]
-#[allow(deprecated)]
-fn ref_mut_filter_map_accessor() {
-    struct X(RefCell<Result<u32, ()>>);
-    impl X {
-        fn accessor(&self) -> Option<RefMut<u32>> {
-            RefMut::filter_map(self.0.borrow_mut(), |r| r.as_mut().ok())
-        }
-    }
-    let x = X(RefCell::new(Ok(7)));
-    {
-        let mut d: RefMut<u32> = x.accessor().unwrap();
-        assert_eq!(*d, 7);
-        *d += 1;
-    }
-    assert_eq!(*x.0.borrow(), Ok(8));
-}
-
 #[test]
 fn as_unsafe_cell() {
     let c1: Cell<usize> = Cell::new(0);
index e959e71daf73ff9c9d3d099743f899ac1bcf7753..7da876b945947c646138727c1f8d358dd84956c7 100644 (file)
@@ -276,6 +276,12 @@ fn eu_iterator_specializations() {
             // Check last
             assert_eq!(iter.clone().last(), Some('}'));
 
+            // Check len
+            assert_eq!(iter.len(), len - offset);
+
+            // Check size_hint (= len in ExactSizeIterator)
+            assert_eq!(iter.size_hint(), (iter.len(), Some(iter.len())));
+
             // Check counting
             assert_eq!(iter.clone().count(), len - offset);
 
index 9b6cedd25b741a43071d70c2d71f59bf47388dcb..a5e6005545bd73d8407ca7985c6c4463c3c1f213 100644 (file)
@@ -10,7 +10,7 @@
 use test::{Bencher, black_box};
 
 use core::hash::{Hash, Hasher};
-use core::hash::SipHasher;
+use core::hash::{SipHasher, SipHasher13, SipHasher24};
 
 // Hash just the bytes of the slice, without length prefix
 struct Bytes<'a>(&'a [u8]);
@@ -45,27 +45,117 @@ macro_rules! u8to64_le {
     });
 }
 
-fn hash<T: Hash>(x: &T) -> u64 {
-    let mut st = SipHasher::new();
+fn hash_with<H: Hasher, T: Hash>(mut st: H, x: &T) -> u64 {
     x.hash(&mut st);
     st.finish()
 }
 
-fn hash_with_keys<T: Hash>(k1: u64, k2: u64, x: &T) -> u64 {
-    let mut st = SipHasher::new_with_keys(k1, k2);
-    x.hash(&mut st);
-    st.finish()
+fn hash<T: Hash>(x: &T) -> u64 {
+    hash_with(SipHasher::new(), x)
 }
 
-fn hash_bytes(x: &[u8]) -> u64 {
-    let mut s = SipHasher::default();
+fn hash_bytes<H: Hasher>(mut s: H, x: &[u8]) -> u64 {
     Hasher::write(&mut s, x);
     s.finish()
 }
 
 #[test]
 #[allow(unused_must_use)]
-fn test_siphash() {
+fn test_siphash_1_3() {
+    let vecs : [[u8; 8]; 64] = [
+        [ 0xdc, 0xc4, 0x0f, 0x05, 0x58, 0x01, 0xac, 0xab ],
+        [ 0x93, 0xca, 0x57, 0x7d, 0xf3, 0x9b, 0xf4, 0xc9 ],
+        [ 0x4d, 0xd4, 0xc7, 0x4d, 0x02, 0x9b, 0xcb, 0x82 ],
+        [ 0xfb, 0xf7, 0xdd, 0xe7, 0xb8, 0x0a, 0xf8, 0x8b ],
+        [ 0x28, 0x83, 0xd3, 0x88, 0x60, 0x57, 0x75, 0xcf ],
+        [ 0x67, 0x3b, 0x53, 0x49, 0x2f, 0xd5, 0xf9, 0xde ],
+        [ 0xa7, 0x22, 0x9f, 0xc5, 0x50, 0x2b, 0x0d, 0xc5 ],
+        [ 0x40, 0x11, 0xb1, 0x9b, 0x98, 0x7d, 0x92, 0xd3 ],
+        [ 0x8e, 0x9a, 0x29, 0x8d, 0x11, 0x95, 0x90, 0x36 ],
+        [ 0xe4, 0x3d, 0x06, 0x6c, 0xb3, 0x8e, 0xa4, 0x25 ],
+        [ 0x7f, 0x09, 0xff, 0x92, 0xee, 0x85, 0xde, 0x79 ],
+        [ 0x52, 0xc3, 0x4d, 0xf9, 0xc1, 0x18, 0xc1, 0x70 ],
+        [ 0xa2, 0xd9, 0xb4, 0x57, 0xb1, 0x84, 0xa3, 0x78 ],
+        [ 0xa7, 0xff, 0x29, 0x12, 0x0c, 0x76, 0x6f, 0x30 ],
+        [ 0x34, 0x5d, 0xf9, 0xc0, 0x11, 0xa1, 0x5a, 0x60 ],
+        [ 0x56, 0x99, 0x51, 0x2a, 0x6d, 0xd8, 0x20, 0xd3 ],
+        [ 0x66, 0x8b, 0x90, 0x7d, 0x1a, 0xdd, 0x4f, 0xcc ],
+        [ 0x0c, 0xd8, 0xdb, 0x63, 0x90, 0x68, 0xf2, 0x9c ],
+        [ 0x3e, 0xe6, 0x73, 0xb4, 0x9c, 0x38, 0xfc, 0x8f ],
+        [ 0x1c, 0x7d, 0x29, 0x8d, 0xe5, 0x9d, 0x1f, 0xf2 ],
+        [ 0x40, 0xe0, 0xcc, 0xa6, 0x46, 0x2f, 0xdc, 0xc0 ],
+        [ 0x44, 0xf8, 0x45, 0x2b, 0xfe, 0xab, 0x92, 0xb9 ],
+        [ 0x2e, 0x87, 0x20, 0xa3, 0x9b, 0x7b, 0xfe, 0x7f ],
+        [ 0x23, 0xc1, 0xe6, 0xda, 0x7f, 0x0e, 0x5a, 0x52 ],
+        [ 0x8c, 0x9c, 0x34, 0x67, 0xb2, 0xae, 0x64, 0xf4 ],
+        [ 0x79, 0x09, 0x5b, 0x70, 0x28, 0x59, 0xcd, 0x45 ],
+        [ 0xa5, 0x13, 0x99, 0xca, 0xe3, 0x35, 0x3e, 0x3a ],
+        [ 0x35, 0x3b, 0xde, 0x4a, 0x4e, 0xc7, 0x1d, 0xa9 ],
+        [ 0x0d, 0xd0, 0x6c, 0xef, 0x02, 0xed, 0x0b, 0xfb ],
+        [ 0xf4, 0xe1, 0xb1, 0x4a, 0xb4, 0x3c, 0xd9, 0x88 ],
+        [ 0x63, 0xe6, 0xc5, 0x43, 0xd6, 0x11, 0x0f, 0x54 ],
+        [ 0xbc, 0xd1, 0x21, 0x8c, 0x1f, 0xdd, 0x70, 0x23 ],
+        [ 0x0d, 0xb6, 0xa7, 0x16, 0x6c, 0x7b, 0x15, 0x81 ],
+        [ 0xbf, 0xf9, 0x8f, 0x7a, 0xe5, 0xb9, 0x54, 0x4d ],
+        [ 0x3e, 0x75, 0x2a, 0x1f, 0x78, 0x12, 0x9f, 0x75 ],
+        [ 0x91, 0x6b, 0x18, 0xbf, 0xbe, 0xa3, 0xa1, 0xce ],
+        [ 0x06, 0x62, 0xa2, 0xad, 0xd3, 0x08, 0xf5, 0x2c ],
+        [ 0x57, 0x30, 0xc3, 0xa3, 0x2d, 0x1c, 0x10, 0xb6 ],
+        [ 0xa1, 0x36, 0x3a, 0xae, 0x96, 0x74, 0xf4, 0xb3 ],
+        [ 0x92, 0x83, 0x10, 0x7b, 0x54, 0x57, 0x6b, 0x62 ],
+        [ 0x31, 0x15, 0xe4, 0x99, 0x32, 0x36, 0xd2, 0xc1 ],
+        [ 0x44, 0xd9, 0x1a, 0x3f, 0x92, 0xc1, 0x7c, 0x66 ],
+        [ 0x25, 0x88, 0x13, 0xc8, 0xfe, 0x4f, 0x70, 0x65 ],
+        [ 0xa6, 0x49, 0x89, 0xc2, 0xd1, 0x80, 0xf2, 0x24 ],
+        [ 0x6b, 0x87, 0xf8, 0xfa, 0xed, 0x1c, 0xca, 0xc2 ],
+        [ 0x96, 0x21, 0x04, 0x9f, 0xfc, 0x4b, 0x16, 0xc2 ],
+        [ 0x23, 0xd6, 0xb1, 0x68, 0x93, 0x9c, 0x6e, 0xa1 ],
+        [ 0xfd, 0x14, 0x51, 0x8b, 0x9c, 0x16, 0xfb, 0x49 ],
+        [ 0x46, 0x4c, 0x07, 0xdf, 0xf8, 0x43, 0x31, 0x9f ],
+        [ 0xb3, 0x86, 0xcc, 0x12, 0x24, 0xaf, 0xfd, 0xc6 ],
+        [ 0x8f, 0x09, 0x52, 0x0a, 0xd1, 0x49, 0xaf, 0x7e ],
+        [ 0x9a, 0x2f, 0x29, 0x9d, 0x55, 0x13, 0xf3, 0x1c ],
+        [ 0x12, 0x1f, 0xf4, 0xa2, 0xdd, 0x30, 0x4a, 0xc4 ],
+        [ 0xd0, 0x1e, 0xa7, 0x43, 0x89, 0xe9, 0xfa, 0x36 ],
+        [ 0xe6, 0xbc, 0xf0, 0x73, 0x4c, 0xb3, 0x8f, 0x31 ],
+        [ 0x80, 0xe9, 0xa7, 0x70, 0x36, 0xbf, 0x7a, 0xa2 ],
+        [ 0x75, 0x6d, 0x3c, 0x24, 0xdb, 0xc0, 0xbc, 0xb4 ],
+        [ 0x13, 0x15, 0xb7, 0xfd, 0x52, 0xd8, 0xf8, 0x23 ],
+        [ 0x08, 0x8a, 0x7d, 0xa6, 0x4d, 0x5f, 0x03, 0x8f ],
+        [ 0x48, 0xf1, 0xe8, 0xb7, 0xe5, 0xd0, 0x9c, 0xd8 ],
+        [ 0xee, 0x44, 0xa6, 0xf7, 0xbc, 0xe6, 0xf4, 0xf6 ],
+        [ 0xf2, 0x37, 0x18, 0x0f, 0xd8, 0x9a, 0xc5, 0xae ],
+        [ 0xe0, 0x94, 0x66, 0x4b, 0x15, 0xf6, 0xb2, 0xc3 ],
+        [ 0xa8, 0xb3, 0xbb, 0xb7, 0x62, 0x90, 0x19, 0x9d ]
+    ];
+
+    let k0 = 0x_07_06_05_04_03_02_01_00;
+    let k1 = 0x_0f_0e_0d_0c_0b_0a_09_08;
+    let mut buf = Vec::new();
+    let mut t = 0;
+    let mut state_inc = SipHasher13::new_with_keys(k0, k1);
+
+    while t < 64 {
+        let vec = u8to64_le!(vecs[t], 0);
+        let out = hash_with(SipHasher13::new_with_keys(k0, k1), &Bytes(&buf));
+        assert_eq!(vec, out);
+
+        let full = hash_with(SipHasher13::new_with_keys(k0, k1), &Bytes(&buf));
+        let i = state_inc.finish();
+
+        assert_eq!(full, i);
+        assert_eq!(full, vec);
+
+        buf.push(t as u8);
+        Hasher::write(&mut state_inc, &[t as u8]);
+
+        t += 1;
+    }
+}
+
+#[test]
+#[allow(unused_must_use)]
+fn test_siphash_2_4() {
     let vecs : [[u8; 8]; 64] = [
         [ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, ],
         [ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, ],
@@ -137,14 +227,14 @@ fn test_siphash() {
     let k1 = 0x_0f_0e_0d_0c_0b_0a_09_08;
     let mut buf = Vec::new();
     let mut t = 0;
-    let mut state_inc = SipHasher::new_with_keys(k0, k1);
+    let mut state_inc = SipHasher24::new_with_keys(k0, k1);
 
     while t < 64 {
         let vec = u8to64_le!(vecs[t], 0);
-        let out = hash_with_keys(k0, k1, &Bytes(&buf));
+        let out = hash_with(SipHasher24::new_with_keys(k0, k1), &Bytes(&buf));
         assert_eq!(vec, out);
 
-        let full = hash_with_keys(k0, k1, &Bytes(&buf));
+        let full = hash_with(SipHasher24::new_with_keys(k0, k1), &Bytes(&buf));
         let i = state_inc.finish();
 
         assert_eq!(full, i);
@@ -156,7 +246,6 @@ fn test_siphash() {
         t += 1;
     }
 }
-
 #[test] #[cfg(target_arch = "arm")]
 fn test_hash_usize() {
     let val = 0xdeadbeef_deadbeef_u64;
@@ -289,7 +378,7 @@ fn bench_u32_keyed(b: &mut Bencher) {
     let k1 = black_box(0x1);
     let k2 = black_box(0x2);
     b.iter(|| {
-        hash_with_keys(k1, k2, &u)
+        hash_with(SipHasher::new_with_keys(k1, k2), &u)
     });
     b.bytes = 8;
 }
@@ -308,7 +397,7 @@ fn bench_u64(b: &mut Bencher) {
 fn bench_bytes_4(b: &mut Bencher) {
     let data = black_box([b' '; 4]);
     b.iter(|| {
-        hash_bytes(&data)
+        hash_bytes(SipHasher::default(), &data)
     });
     b.bytes = 4;
 }
@@ -317,7 +406,7 @@ fn bench_bytes_4(b: &mut Bencher) {
 fn bench_bytes_7(b: &mut Bencher) {
     let data = black_box([b' '; 7]);
     b.iter(|| {
-        hash_bytes(&data)
+        hash_bytes(SipHasher::default(), &data)
     });
     b.bytes = 7;
 }
@@ -326,7 +415,7 @@ fn bench_bytes_7(b: &mut Bencher) {
 fn bench_bytes_8(b: &mut Bencher) {
     let data = black_box([b' '; 8]);
     b.iter(|| {
-        hash_bytes(&data)
+        hash_bytes(SipHasher::default(), &data)
     });
     b.bytes = 8;
 }
@@ -335,7 +424,7 @@ fn bench_bytes_8(b: &mut Bencher) {
 fn bench_bytes_a_16(b: &mut Bencher) {
     let data = black_box([b' '; 16]);
     b.iter(|| {
-        hash_bytes(&data)
+        hash_bytes(SipHasher::default(), &data)
     });
     b.bytes = 16;
 }
@@ -344,7 +433,7 @@ fn bench_bytes_a_16(b: &mut Bencher) {
 fn bench_bytes_b_32(b: &mut Bencher) {
     let data = black_box([b' '; 32]);
     b.iter(|| {
-        hash_bytes(&data)
+        hash_bytes(SipHasher::default(), &data)
     });
     b.bytes = 32;
 }
@@ -353,7 +442,7 @@ fn bench_bytes_b_32(b: &mut Bencher) {
 fn bench_bytes_c_128(b: &mut Bencher) {
     let data = black_box([b' '; 128]);
     b.iter(|| {
-        hash_bytes(&data)
+        hash_bytes(SipHasher::default(), &data)
     });
     b.bytes = 128;
 }
index 54fca291e5ec2c464e8a8896c257d422c3eea12a..a2848faa105e9a89cf2d53ac158cc181720eb43c 100644 (file)
@@ -13,6 +13,7 @@ use core::{i8, i16, isize};
 use core::usize;
 
 use test::Bencher;
+use test::black_box;
 
 #[test]
 fn test_lt() {
@@ -1030,3 +1031,33 @@ fn bench_max(b: &mut Bencher) {
         it.map(scatter).max()
     })
 }
+
+pub fn copy_zip(xs: &[u8], ys: &mut [u8]) {
+    for (a, b) in ys.iter_mut().zip(xs) {
+        *a = *b;
+    }
+}
+
+pub fn add_zip(xs: &[f32], ys: &mut [f32]) {
+    for (a, b) in ys.iter_mut().zip(xs) {
+        *a += *b;
+    }
+}
+
+#[bench]
+fn bench_zip_copy(b: &mut Bencher) {
+    let source = vec![0u8; 16 * 1024];
+    let mut dst = black_box(vec![0u8; 16 * 1024]);
+    b.iter(|| {
+        copy_zip(&source, &mut dst)
+    })
+}
+
+#[bench]
+fn bench_zip_add(b: &mut Bencher) {
+    let source = vec![1.; 16 * 1024];
+    let mut dst = vec![0.; 16 * 1024];
+    b.iter(|| {
+        add_zip(&source, &mut dst)
+    });
+}
index 88d73df937f7e26d28ce5810d177421b77274a9a..053158c14bf20ffb5ac4f6618cc23a5685a78796 100644 (file)
 #![feature(core_private_diy_float)]
 #![feature(dec2flt)]
 #![feature(fixed_size_array)]
-#![feature(float_extras)]
 #![feature(flt2dec)]
-#![feature(iter_arith)]
 #![feature(libc)]
 #![feature(nonzero)]
 #![feature(rand)]
 #![feature(raw)]
+#![feature(sip_hash_13)]
 #![feature(slice_patterns)]
 #![feature(step_by)]
 #![feature(test)]
index 5bc08376d257c5ceff5a7e2b1a722bbcdbc15d45..01bafe49a7acd4820e4ca9bc657c9308d5d36670 100644 (file)
@@ -18,6 +18,13 @@ fn size_of_basic() {
     assert_eq!(size_of::<u64>(), 8);
 }
 
+#[test]
+#[cfg(target_pointer_width = "16")]
+fn size_of_16() {
+    assert_eq!(size_of::<usize>(), 2);
+    assert_eq!(size_of::<*const usize>(), 2);
+}
+
 #[test]
 #[cfg(target_pointer_width = "32")]
 fn size_of_32() {
@@ -47,6 +54,13 @@ fn align_of_basic() {
     assert_eq!(align_of::<u32>(), 4);
 }
 
+#[test]
+#[cfg(target_pointer_width = "16")]
+fn align_of_16() {
+    assert_eq!(align_of::<usize>(), 2);
+    assert_eq!(align_of::<*const usize>(), 2);
+}
+
 #[test]
 #[cfg(target_pointer_width = "32")]
 fn align_of_32() {
index 4c0a403e574a3c1a2e312b4f61053aa9c1793b3c..1a3533317dae68c57aed2528469ed575e738e58d 100644 (file)
@@ -9,9 +9,24 @@
 // except according to those terms.
 
 use std::f64;
+use std::mem;
 use core::num::diy_float::Fp;
 use core::num::dec2flt::rawfp::{fp_to_float, prev_float, next_float, round_normal};
 
+fn integer_decode(f: f64) -> (u64, i16, i8) {
+    let bits: u64 = unsafe { mem::transmute(f) };
+    let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
+    let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
+    let mantissa = if exponent == 0 {
+        (bits & 0xfffffffffffff) << 1
+    } else {
+        (bits & 0xfffffffffffff) | 0x10000000000000
+    };
+    // Exponent bias + mantissa shift
+    exponent -= 1023 + 52;
+    (mantissa, exponent, sign)
+}
+
 #[test]
 fn fp_to_float_half_to_even() {
     fn is_normalized(sig: u64) -> bool {
@@ -21,12 +36,12 @@ fn fp_to_float_half_to_even() {
 
     fn conv(sig: u64) -> u64 {
         // The significands are perfectly in range, so the exponent should not matter
-        let (m1, e1, _) = fp_to_float::<f64>(Fp { f: sig, e: 0 }).integer_decode();
+        let (m1, e1, _) = integer_decode(fp_to_float::<f64>(Fp { f: sig, e: 0 }));
         assert_eq!(e1, 0 + 64 - 53);
-        let (m2, e2, _) = fp_to_float::<f64>(Fp { f: sig, e: 55 }).integer_decode();
+        let (m2, e2, _) = integer_decode(fp_to_float::<f64>(Fp { f: sig, e: 55 }));
         assert_eq!(e2, 55 + 64 - 53);
         assert_eq!(m2, m1);
-        let (m3, e3, _) = fp_to_float::<f64>(Fp { f: sig, e: -78 }).integer_decode();
+        let (m3, e3, _) = integer_decode(fp_to_float::<f64>(Fp { f: sig, e: -78 }));
         assert_eq!(e3, -78 + 64 - 53);
         assert_eq!(m3, m2);
         m3
@@ -65,7 +80,7 @@ const SOME_FLOATS: [f64; 9] =
 #[test]
 fn human_f64_roundtrip() {
     for &x in &SOME_FLOATS {
-        let (f, e, _) = x.integer_decode();
+        let (f, e, _) = integer_decode(x);
         let fp = Fp { f: f, e: e};
         assert_eq!(fp_to_float::<f64>(fp), x);
     }
index 21260c520f6235e26f0129dd141dec9a07f58146..857aae72c8a5bd5d92a0088a36bbe3c37fc58b37 100644 (file)
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::f64;
 use core::num::flt2dec::estimator::*;
 
 #[test]
@@ -54,7 +53,7 @@ fn test_estimate_scaling_factor() {
     assert_almost_eq!(estimate_scaling_factor(0x1fffffffffffff, 971), 309);
 
     for i in -1074..972 {
-        let expected = f64::ldexp(1.0, i).log10().ceil();
+        let expected = super::ldexp_f64(1.0, i).log10().ceil();
         assert_almost_eq!(estimate_scaling_factor(1, i as i16), expected as i16);
     }
 }
index 1a592f3ad4249f3489cffec79b70bb8e01bc9432..0f4d19e7092571495405338fb31ef9f3a1f5fd9b 100644 (file)
@@ -89,6 +89,17 @@ macro_rules! try_fixed {
     })
 }
 
+fn ldexp_f32(a: f32, b: i32) -> f32 {
+    ldexp_f64(a as f64, b) as f32
+}
+
+fn ldexp_f64(a: f64, b: i32) -> f64 {
+    extern {
+        fn ldexp(x: f64, n: i32) -> f64;
+    }
+    unsafe { ldexp(a, b) }
+}
+
 fn check_exact<F, T>(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16)
         where T: DecodableFloat, F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
     // use a large enough buffer
@@ -237,7 +248,7 @@ pub fn f32_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8])
     // 10^8 * 0.3355443
     // 10^8 * 0.33554432
     // 10^8 * 0.33554436
-    check_shortest!(f(f32::ldexp(1.0, 25)) => b"33554432", 8);
+    check_shortest!(f(ldexp_f32(1.0, 25)) => b"33554432", 8);
 
     // 10^39 * 0.340282326356119256160033759537265639424
     // 10^39 * 0.34028234663852885981170418348451692544
@@ -252,13 +263,13 @@ pub fn f32_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8])
     // 10^-44 * 0
     // 10^-44 * 0.1401298464324817070923729583289916131280...
     // 10^-44 * 0.2802596928649634141847459166579832262560...
-    let minf32 = f32::ldexp(1.0, -149);
+    let minf32 = ldexp_f32(1.0, -149);
     check_shortest!(f(minf32) => b"1", -44);
 }
 
 pub fn f32_exact_sanity_test<F>(mut f: F)
         where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
-    let minf32 = f32::ldexp(1.0, -149);
+    let minf32 = ldexp_f32(1.0, -149);
 
     check_exact!(f(0.1f32)            => b"100000001490116119384765625             ", 0);
     check_exact!(f(0.5f32)            => b"5                                       ", 0);
@@ -336,7 +347,7 @@ pub fn f64_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8])
     // 10^20 * 0.18446744073709549568
     // 10^20 * 0.18446744073709551616
     // 10^20 * 0.18446744073709555712
-    check_shortest!(f(f64::ldexp(1.0, 64)) => b"18446744073709552", 20);
+    check_shortest!(f(ldexp_f64(1.0, 64)) => b"18446744073709552", 20);
 
     // pathological case: high = 10^23 (exact). tie breaking should always prefer that.
     // 10^24 * 0.099999999999999974834176
@@ -357,13 +368,13 @@ pub fn f64_shortest_sanity_test<F>(mut f: F) where F: FnMut(&Decoded, &mut [u8])
     // 10^-323 * 0
     // 10^-323 * 0.4940656458412465441765687928682213723650...
     // 10^-323 * 0.9881312916824930883531375857364427447301...
-    let minf64 = f64::ldexp(1.0, -1074);
+    let minf64 = ldexp_f64(1.0, -1074);
     check_shortest!(f(minf64) => b"5", -323);
 }
 
 pub fn f64_exact_sanity_test<F>(mut f: F)
         where F: FnMut(&Decoded, &mut [u8], i16) -> (usize, i16) {
-    let minf64 = f64::ldexp(1.0, -1074);
+    let minf64 = ldexp_f64(1.0, -1074);
 
     check_exact!(f(0.1f64)            => b"1000000000000000055511151231257827021181", 0);
     check_exact!(f(0.45f64)           => b"4500000000000000111022302462515654042363", 0);
@@ -616,7 +627,7 @@ pub fn to_shortest_str_test<F>(mut f_: F)
     assert_eq!(to_string(f, f32::MAX, Minus, 1, false), format!("34028235{:0>31}.0", ""));
     assert_eq!(to_string(f, f32::MAX, Minus, 8, false), format!("34028235{:0>31}.00000000", ""));
 
-    let minf32 = f32::ldexp(1.0, -149);
+    let minf32 = ldexp_f32(1.0, -149);
     assert_eq!(to_string(f, minf32, Minus,  0, false), format!("0.{:0>44}1", ""));
     assert_eq!(to_string(f, minf32, Minus, 45, false), format!("0.{:0>44}1", ""));
     assert_eq!(to_string(f, minf32, Minus, 46, false), format!("0.{:0>44}10", ""));
@@ -628,7 +639,7 @@ pub fn to_shortest_str_test<F>(mut f_: F)
     assert_eq!(to_string(f, f64::MAX, Minus, 8, false),
                format!("17976931348623157{:0>292}.00000000", ""));
 
-    let minf64 = f64::ldexp(1.0, -1074);
+    let minf64 = ldexp_f64(1.0, -1074);
     assert_eq!(to_string(f, minf64, Minus,   0, false), format!("0.{:0>323}5", ""));
     assert_eq!(to_string(f, minf64, Minus, 324, false), format!("0.{:0>323}5", ""));
     assert_eq!(to_string(f, minf64, Minus, 325, false), format!("0.{:0>323}50", ""));
@@ -730,7 +741,7 @@ pub fn to_shortest_exp_str_test<F>(mut f_: F)
     assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38");
     assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", ""));
 
-    let minf32 = f32::ldexp(1.0, -149);
+    let minf32 = ldexp_f32(1.0, -149);
     assert_eq!(to_string(f, minf32, Minus, ( -4, 16), false), "1e-45");
     assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45");
     assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", ""));
@@ -742,7 +753,7 @@ pub fn to_shortest_exp_str_test<F>(mut f_: F)
     assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false),
                "1.7976931348623157e308");
 
-    let minf64 = f64::ldexp(1.0, -1074);
+    let minf64 = ldexp_f64(1.0, -1074);
     assert_eq!(to_string(f, minf64, Minus, (  -4,  16), false), "5e-324");
     assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", ""));
     assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324");
@@ -874,7 +885,7 @@ pub fn to_exact_exp_str_test<F>(mut f_: F)
     assert_eq!(to_string(f, f32::MAX, Minus, 64, false),
                "3.402823466385288598117041834845169254400000000000000000000000000e38");
 
-    let minf32 = f32::ldexp(1.0, -149);
+    let minf32 = ldexp_f32(1.0, -149);
     assert_eq!(to_string(f, minf32, Minus,   1, false), "1e-45");
     assert_eq!(to_string(f, minf32, Minus,   2, false), "1.4e-45");
     assert_eq!(to_string(f, minf32, Minus,   4, false), "1.401e-45");
@@ -914,7 +925,7 @@ pub fn to_exact_exp_str_test<F>(mut f_: F)
                  0000000000000000000000000000000000000000000000000000000000000000e308");
 
     // okay, this is becoming tough. fortunately for us, this is almost the worst case.
-    let minf64 = f64::ldexp(1.0, -1074);
+    let minf64 = ldexp_f64(1.0, -1074);
     assert_eq!(to_string(f, minf64, Minus,    1, false), "5e-324");
     assert_eq!(to_string(f, minf64, Minus,    2, false), "4.9e-324");
     assert_eq!(to_string(f, minf64, Minus,    4, false), "4.941e-324");
@@ -1120,7 +1131,7 @@ pub fn to_exact_fixed_str_test<F>(mut f_: F)
     assert_eq!(to_string(f, f32::MAX, Minus, 2, false),
                "340282346638528859811704183484516925440.00");
 
-    let minf32 = f32::ldexp(1.0, -149);
+    let minf32 = ldexp_f32(1.0, -149);
     assert_eq!(to_string(f, minf32, Minus,   0, false), "0");
     assert_eq!(to_string(f, minf32, Minus,   1, false), "0.0");
     assert_eq!(to_string(f, minf32, Minus,   2, false), "0.00");
@@ -1152,7 +1163,7 @@ pub fn to_exact_fixed_str_test<F>(mut f_: F)
                 9440758685084551339423045832369032229481658085593321233482747978\
                 26204144723168738177180919299881250404026184124858368.0000000000");
 
-    let minf64 = f64::ldexp(1.0, -1074);
+    let minf64 = ldexp_f64(1.0, -1074);
     assert_eq!(to_string(f, minf64, Minus, 0, false), "0");
     assert_eq!(to_string(f, minf64, Minus, 1, false), "0.0");
     assert_eq!(to_string(f, minf64, Minus, 10, false), "0.0000000000");
index b578b064d67caece89816bdecee93c709041b2a7..63913f2878c20f07b0dc7d6e4fe720260e0cc747 100644 (file)
@@ -31,7 +31,7 @@
 
 extern crate libc;
 
-use libc::{c_void, size_t, c_int};
+use libc::{c_int, c_void, size_t};
 use std::fmt;
 use std::ops::Deref;
 use std::ptr::Unique;
@@ -76,9 +76,9 @@ impl Drop for Bytes {
 
 #[link(name = "miniz", kind = "static")]
 #[cfg(not(cargobuild))]
-extern {}
+extern "C" {}
 
-extern {
+extern "C" {
     /// Raw miniz compression function.
     fn tdefl_compress_mem_to_heap(psrc_buf: *const c_void,
                                   src_buf_len: size_t,
@@ -154,8 +154,8 @@ pub fn inflate_bytes_zlib(bytes: &[u8]) -> Result<Bytes, Error> {
 #[cfg(test)]
 mod tests {
     #![allow(deprecated)]
-    use super::{inflate_bytes, deflate_bytes};
-    use std::__rand::{thread_rng, Rng};
+    use super::{deflate_bytes, inflate_bytes};
+    use std::__rand::{Rng, thread_rng};
 
     #[test]
     fn test_flate_round_trip() {
index a3626937ba4329e00f6856558ab576691d0b9895..cdfe358488d52d872aca2ac9744f3ae65bfbfd3b 100644 (file)
@@ -163,6 +163,7 @@ fn main() {
         cfg.header("sys/sendfile.h");
         cfg.header("sys/vfs.h");
         cfg.header("sys/syscall.h");
+        cfg.header("sys/sysinfo.h");
         if !musl {
             cfg.header("linux/netlink.h");
             cfg.header("linux/magic.h");
index dbf8b6e5fd43b402cdfaae34960ef9f10ef47f43..6dceb068c42f4de184ce5b03c053d04d302d07d2 100644 (file)
@@ -19,6 +19,7 @@ pub type fsfilcnt_t = ::c_uint;
 pub type speed_t = ::c_ulong;
 pub type tcflag_t = ::c_ulong;
 pub type nl_item = ::c_int;
+pub type id_t = ::c_uint;
 
 pub enum timezone {}
 
@@ -761,6 +762,7 @@ pub const AF_INET6: ::c_int = 30;
 pub const SOCK_STREAM: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 2;
 pub const SOCK_RAW: ::c_int = 3;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 pub const IPPROTO_TCP: ::c_int = 6;
 pub const IPPROTO_IP: ::c_int = 0;
 pub const IPPROTO_IPV6: ::c_int = 41;
@@ -931,7 +933,10 @@ pub const _SC_TRACE_SYS_MAX: ::c_int = 129;
 pub const _SC_TRACE_USER_EVENT_MAX: ::c_int = 130;
 pub const _SC_PASS_MAX: ::c_int = 131;
 
+pub const PTHREAD_MUTEX_NORMAL: ::c_int = 0;
+pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 1;
 pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
+pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_NORMAL;
 pub const _PTHREAD_MUTEX_SIG_init: ::c_long = 0x32AAABA7;
 pub const _PTHREAD_COND_SIG_init: ::c_long = 0x3CB0B1BB;
 pub const _PTHREAD_RWLOCK_SIG_init: ::c_long = 0x2DA8B3B4;
@@ -1238,6 +1243,11 @@ pub const CTL_DEBUG_NAME: ::c_int = 0;
 pub const CTL_DEBUG_VALUE: ::c_int = 1;
 pub const CTL_DEBUG_MAXID: ::c_int = 20;
 
+pub const PRIO_DARWIN_THREAD: ::c_int = 3;
+pub const PRIO_DARWIN_PROCESS: ::c_int = 4;
+pub const PRIO_DARWIN_BG: ::c_int = 0x1000;
+pub const PRIO_DARWIN_NONUI: ::c_int = 0x1001;
+
 f! {
     pub fn WSTOPSIG(status: ::c_int) -> ::c_int {
         status >> 8
@@ -1353,6 +1363,8 @@ extern {
                      base: ::locale_t) -> ::locale_t;
     pub fn uselocale(loc: ::locale_t) -> ::locale_t;
     pub fn querylocale(mask: ::c_int, loc: ::locale_t) -> *const ::c_char;
+    pub fn getpriority(which: ::c_int, who: ::id_t) -> ::c_int;
+    pub fn setpriority(which: ::c_int, who: ::id_t, prio: ::c_int) -> ::c_int;
 }
 
 cfg_if! {
index 43181c0a5ff552e4c1e569bca550d5894b31b05b..a265051f0ad736d4f0ee5147542975a0afa02025 100644 (file)
@@ -10,6 +10,7 @@ pub type pthread_key_t = ::c_int;
 pub type tcflag_t = ::c_uint;
 pub type speed_t = ::c_uint;
 pub type nl_item = ::c_int;
+pub type id_t = i64;
 
 pub enum timezone {}
 
@@ -530,6 +531,7 @@ pub const AF_UNIX: ::c_int = 1;
 pub const SOCK_STREAM: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 2;
 pub const SOCK_RAW: ::c_int = 3;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 pub const IPPROTO_TCP: ::c_int = 6;
 pub const IPPROTO_IP: ::c_int = 0;
 pub const IPPROTO_IPV6: ::c_int = 41;
@@ -646,7 +648,11 @@ pub const _SC_HOST_NAME_MAX: ::c_int = 72;
 pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = 0 as *mut _;
 pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = 0 as *mut _;
 pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = 0 as *mut _;
+pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 1;
 pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
+pub const PTHREAD_MUTEX_NORMAL: ::c_int = 3;
+pub const PTHREAD_MUTEX_ADAPTIVE_NP: ::c_int = 4;
+pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_ERRORCHECK;
 
 pub const SCHED_FIFO: ::c_int = 1;
 pub const SCHED_OTHER: ::c_int = 2;
@@ -668,6 +674,9 @@ pub const LOG_SECURITY: ::c_int = 13 << 3;
 pub const LOG_CONSOLE: ::c_int = 14 << 3;
 pub const LOG_NFACILITIES: ::c_int = 24;
 
+pub const TIOCGWINSZ: ::c_ulong = 0x40087468;
+pub const TIOCSWINSZ: ::c_ulong = 0x80087467;
+
 #[link(name = "util")]
 extern {
     pub fn getnameinfo(sa: *const ::sockaddr,
@@ -742,6 +751,8 @@ extern {
     pub fn pthread_attr_getstack(attr: *const ::pthread_attr_t,
                                  stackaddr: *mut *mut ::c_void,
                                  stacksize: *mut ::size_t) -> ::c_int;
+    pub fn getpriority(which: ::c_int, who: ::c_int) -> ::c_int;
+    pub fn setpriority(which: ::c_int, who: ::c_int, prio: ::c_int) -> ::c_int;
 }
 
 cfg_if! {
index 7b3b5e44cadd6b1418cb02dbb4fbd09f599b452c..3df4df55aeefa10e1c098702427eb1f5057b79ee 100644 (file)
@@ -270,7 +270,12 @@ pub const ST_NOSUID: ::c_ulong = 2;
 pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = 0 as *mut _;
 pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = 0 as *mut _;
 pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = 0 as *mut _;
+
+pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 1;
 pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
+pub const PTHREAD_MUTEX_NORMAL: ::c_int = 3;
+pub const PTHREAD_MUTEX_STRICT_NP: ::c_int = 4;
+pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_STRICT_NP;
 
 pub const TMP_MAX : ::c_uint = 0x7fffffff;
 
index 8bb15cffc08f15c5613683118a8a70997a273fca..1f864f640cbb181ae30a9ad7d8c0c2a5b748f486 100644 (file)
@@ -10,6 +10,7 @@ pub type speed_t = ::c_uint;
 pub type tcflag_t = ::c_uint;
 pub type nl_item = c_long;
 pub type clockid_t = ::c_int;
+pub type id_t = ::uint32_t;
 
 pub enum timezone {}
 
@@ -360,6 +361,7 @@ pub const AF_INET6: ::c_int = 24;
 pub const SOCK_STREAM: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 2;
 pub const SOCK_RAW: ::c_int = 3;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 pub const IPPROTO_TCP: ::c_int = 6;
 pub const IPPROTO_IP: ::c_int = 0;
 pub const IPPROTO_IPV6: ::c_int = 41;
@@ -472,6 +474,8 @@ extern {
                    name: *mut ::c_char,
                    termp: *mut termios,
                    winp: *mut ::winsize) -> ::pid_t;
+    pub fn getpriority(which: ::c_int, who: ::id_t) -> ::c_int;
+    pub fn setpriority(which: ::c_int, who: ::id_t, prio: ::c_int) -> ::c_int;
 }
 
 cfg_if! {
index 330143bcc9f2c2a13465a2cac8f33ef4040bb1ba..c91f40c8f9495a164919487f9a714de15d3cde4d 100644 (file)
@@ -345,7 +345,10 @@ pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
     ptr_owner: 0,
     ptr_private: 0 as *mut _,
 };
+pub const PTHREAD_MUTEX_NORMAL: ::c_int = 0;
+pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 1;
 pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
+pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_NORMAL;
 
 pub const EVFILT_AIO: ::int16_t = 2;
 pub const EVFILT_PROC: ::int16_t = 4;
index 9f8450ee672941ee82a178dc772c6436e9274252..aa98a86eb8e6f832addf5d1dde247cf2b617fec4 100644 (file)
@@ -239,7 +239,12 @@ pub const ST_NOSUID: ::c_ulong = 2;
 pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = 0 as *mut _;
 pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = 0 as *mut _;
 pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = 0 as *mut _;
+
+pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 1;
 pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 2;
+pub const PTHREAD_MUTEX_NORMAL: ::c_int = 3;
+pub const PTHREAD_MUTEX_STRICT_NP: ::c_int = 4;
+pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_STRICT_NP;
 
 pub const TMP_MAX : ::c_uint = 0x7fffffff;
 
index 29de88bf4455c9f6a3a99ff40797b5981b59509c..cc79c6e2ef7f78a56d80de7be02fab656fe3770a 100644 (file)
@@ -181,6 +181,13 @@ pub const LOG_NOWAIT: ::c_int = 0x10;
 pub const LOG_PRIMASK: ::c_int = 7;
 pub const LOG_FACMASK: ::c_int = 0x3f8;
 
+pub const PRIO_PROCESS: ::c_int = 0;
+pub const PRIO_PGRP: ::c_int = 1;
+pub const PRIO_USER: ::c_int = 2;
+
+pub const PRIO_MIN: ::c_int = -20;
+pub const PRIO_MAX: ::c_int = 20;
+
 cfg_if! {
     if #[cfg(dox)] {
         // on dox builds don't pull in anything
@@ -773,6 +780,9 @@ extern {
     pub fn closelog();
     pub fn setlogmask(maskpri: ::c_int) -> ::c_int;
     pub fn syslog(priority: ::c_int, message: *const ::c_char, ...);
+    #[cfg_attr(all(target_os = "macos", target_arch = "x86"),
+               link_name = "nice$UNIX2003")]
+    pub fn nice(incr: ::c_int) -> ::c_int;
 }
 
 cfg_if! {
index 3a295cc720e05ae5ba1d5802c960b900775bcfb9..91a56a3fcaf95c4f19e281ddf634a0301d50f872 100644 (file)
@@ -102,6 +102,23 @@ s! {
         pub f_flags: ::uint32_t,
         pub f_spare: [::uint32_t; 4],
     }
+
+    pub struct sysinfo {
+        pub uptime: ::c_long,
+        pub loads: [::c_ulong; 3],
+        pub totalram: ::c_ulong,
+        pub freeram: ::c_ulong,
+        pub sharedram: ::c_ulong,
+        pub bufferram: ::c_ulong,
+        pub totalswap: ::c_ulong,
+        pub freeswap: ::c_ulong,
+        pub procs: ::c_ushort,
+        pub pad: ::c_ushort,
+        pub totalhigh: ::c_ulong,
+        pub freehigh: ::c_ulong,
+        pub mem_unit: ::c_uint,
+        pub _f: [::c_char; 8],
+    }
 }
 
 pub const SYS_gettid: ::c_long = 224;
@@ -122,6 +139,8 @@ pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
     __reserved: [0; 12],
 };
 pub const PTHREAD_STACK_MIN: ::size_t = 4096 * 2;
+pub const CPU_SETSIZE: ::size_t = 32;
+pub const __CPU_BITS: ::size_t = 32;
 
 extern {
     pub fn timegm64(tm: *const ::tm) -> ::time64_t;
index 98b643f0b079c1176218dd74055000904008d3bd..025dabd45833a9e0e46a2a36d5f2734daa6a416a 100644 (file)
@@ -112,6 +112,23 @@ s! {
         pub f_flags: ::uint64_t,
         pub f_spare: [::uint64_t; 4],
     }
+
+    pub struct sysinfo {
+        pub uptime: ::c_long,
+        pub loads: [::c_ulong; 3],
+        pub totalram: ::c_ulong,
+        pub freeram: ::c_ulong,
+        pub sharedram: ::c_ulong,
+        pub bufferram: ::c_ulong,
+        pub totalswap: ::c_ulong,
+        pub freeswap: ::c_ulong,
+        pub procs: ::c_ushort,
+        pub pad: ::c_ushort,
+        pub totalhigh: ::c_ulong,
+        pub freehigh: ::c_ulong,
+        pub mem_unit: ::c_uint,
+        pub _f: [::c_char; 0],
+    }
 }
 
 pub const SYS_gettid: ::c_long = 178;
@@ -132,6 +149,8 @@ pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
     __reserved: [0; 36],
 };
 pub const PTHREAD_STACK_MIN: ::size_t = 4096 * 4;
+pub const CPU_SETSIZE: ::size_t = 1024;
+pub const __CPU_BITS: ::size_t = 64;
 
 extern {
     pub fn timegm(tm: *const ::tm) -> ::time64_t;
index 7cfde944274061bad3daa9ee9a700595451326fb..f5ad64b9096634ac020d670bec70e60c847fc756 100644 (file)
@@ -21,6 +21,7 @@ pub type nfds_t = ::c_uint;
 pub type rlim_t = ::c_ulong;
 pub type dev_t = ::c_ulong;
 pub type ino_t = ::c_ulong;
+pub type __CPU_BITTYPE = ::c_ulong;
 
 s! {
     pub struct dirent {
@@ -87,6 +88,13 @@ s! {
         pub l_len: ::off_t,
         pub l_pid: ::pid_t,
     }
+
+    pub struct cpu_set_t {
+        #[cfg(target_pointer_width = "64")]
+        __bits: [__CPU_BITTYPE; 16],
+        #[cfg(target_pointer_width = "32")]
+        __bits: [__CPU_BITTYPE; 1],
+    }
 }
 
 pub const BUFSIZ: ::c_uint = 1024;
@@ -157,7 +165,10 @@ pub const _SC_THREAD_PRIO_PROTECT: ::c_int = 84;
 pub const _SC_THREAD_SAFE_FUNCTIONS: ::c_int = 85;
 pub const _SC_NPROCESSORS_ONLN: ::c_int = 97;
 
+pub const PTHREAD_MUTEX_NORMAL: ::c_int = 0;
 pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 1;
+pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 2;
+pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_NORMAL;
 
 pub const FIOCLEX: ::c_int = 0x5451;
 
@@ -313,6 +324,7 @@ pub const ENOTRECOVERABLE: ::c_int = 131;
 
 pub const SOCK_STREAM: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 2;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 
 pub const SOL_SOCKET: ::c_int = 1;
 
@@ -543,6 +555,11 @@ extern {
                        sevlen: ::size_t,
                        flags: ::c_int) -> ::c_int;
     pub fn ptrace(request: ::c_int, ...) -> ::c_long;
+    pub fn getpriority(which: ::c_int, who: ::c_int) -> ::c_int;
+    pub fn setpriority(which: ::c_int, who: ::c_int, prio: ::c_int) -> ::c_int;
+    pub fn __sched_cpualloc(count: ::size_t) -> *mut ::cpu_set_t;
+    pub fn __sched_cpufree(set: *mut ::cpu_set_t);
+    pub fn __sched_cpucount(setsize: ::size_t, set: *mut cpu_set_t) -> ::c_int;
 }
 
 cfg_if! {
index a6611d17fee16c80b82eb62ecd7a0eedc28d0845..bdead3160de12c54f80723c13c17598e249e9700 100644 (file)
@@ -172,6 +172,23 @@ s! {
         pub l_pid: ::pid_t,
         pad: [::c_long; 4],
     }
+
+    pub struct sysinfo {
+        pub uptime: ::c_long,
+        pub loads: [::c_ulong; 3],
+        pub totalram: ::c_ulong,
+        pub freeram: ::c_ulong,
+        pub sharedram: ::c_ulong,
+        pub bufferram: ::c_ulong,
+        pub totalswap: ::c_ulong,
+        pub freeswap: ::c_ulong,
+        pub procs: ::c_ushort,
+        pub pad: ::c_ushort,
+        pub totalhigh: ::c_ulong,
+        pub freehigh: ::c_ulong,
+        pub mem_unit: ::c_uint,
+        pub _f: [::c_char; 8],
+    }
 }
 
 pub const BUFSIZ: ::c_uint = 8192;
@@ -332,6 +349,7 @@ pub const MAP_STACK: ::c_int = 0x40000;
 
 pub const SOCK_STREAM: ::c_int = 2;
 pub const SOCK_DGRAM: ::c_int = 1;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 
 pub const SOL_SOCKET: ::c_int = 0xffff;
 
index 9fd5cf4f6035031307f9b923a14127701b27f98e..568f2a463e2e43629aa172b7f3b543c259a0e422 100644 (file)
@@ -401,7 +401,10 @@ pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
     __align: [],
     size: [0; __SIZEOF_PTHREAD_RWLOCK_T],
 };
+pub const PTHREAD_MUTEX_NORMAL: ::c_int = 0;
 pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 1;
+pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 2;
+pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_NORMAL;
 pub const __SIZEOF_PTHREAD_COND_T: usize = 48;
 
 pub const SCHED_OTHER: ::c_int = 0;
index b87913f3692dc1d3002a335a5ecda17fce2c662c..5d648618e31d4fd1cdf64b9d23332927be8eb3f1 100644 (file)
@@ -121,6 +121,7 @@ pub const MAP_STACK: ::c_int = 0x020000;
 
 pub const SOCK_STREAM: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 2;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 
 pub const SOL_SOCKET: ::c_int = 1;
 
@@ -272,36 +273,36 @@ pub const IEXTEN: ::tcflag_t = 0x00008000;
 pub const TOSTOP: ::tcflag_t = 0x00000100;
 pub const FLUSHO: ::tcflag_t = 0x00001000;
 
-pub const TCGETS: ::c_ulong = 0x5401;
-pub const TCSETS: ::c_ulong = 0x5402;
-pub const TCSETSW: ::c_ulong = 0x5403;
-pub const TCSETSF: ::c_ulong = 0x5404;
-pub const TCGETA: ::c_ulong = 0x5405;
-pub const TCSETA: ::c_ulong = 0x5406;
-pub const TCSETAW: ::c_ulong = 0x5407;
-pub const TCSETAF: ::c_ulong = 0x5408;
-pub const TCSBRK: ::c_ulong = 0x5409;
-pub const TCXONC: ::c_ulong = 0x540A;
-pub const TCFLSH: ::c_ulong = 0x540B;
-pub const TIOCGSOFTCAR: ::c_ulong = 0x5419;
-pub const TIOCSSOFTCAR: ::c_ulong = 0x541A;
-pub const TIOCLINUX: ::c_ulong = 0x541C;
-pub const TIOCGSERIAL: ::c_ulong = 0x541E;
-pub const TIOCEXCL: ::c_ulong = 0x540C;
-pub const TIOCNXCL: ::c_ulong = 0x540D;
-pub const TIOCSCTTY: ::c_ulong = 0x540E;
-pub const TIOCGPGRP: ::c_ulong = 0x540F;
-pub const TIOCSPGRP: ::c_ulong = 0x5410;
-pub const TIOCOUTQ: ::c_ulong = 0x5411;
-pub const TIOCSTI: ::c_ulong = 0x5412;
-pub const TIOCGWINSZ: ::c_ulong = 0x5413;
-pub const TIOCSWINSZ: ::c_ulong = 0x5414;
-pub const TIOCMGET: ::c_ulong = 0x5415;
-pub const TIOCMBIS: ::c_ulong = 0x5416;
-pub const TIOCMBIC: ::c_ulong = 0x5417;
-pub const TIOCMSET: ::c_ulong = 0x5418;
-pub const FIONREAD: ::c_ulong = 0x541B;
-pub const TIOCCONS: ::c_ulong = 0x541D;
+pub const TCGETS: ::c_int = 0x5401;
+pub const TCSETS: ::c_int = 0x5402;
+pub const TCSETSW: ::c_int = 0x5403;
+pub const TCSETSF: ::c_int = 0x5404;
+pub const TCGETA: ::c_int = 0x5405;
+pub const TCSETA: ::c_int = 0x5406;
+pub const TCSETAW: ::c_int = 0x5407;
+pub const TCSETAF: ::c_int = 0x5408;
+pub const TCSBRK: ::c_int = 0x5409;
+pub const TCXONC: ::c_int = 0x540A;
+pub const TCFLSH: ::c_int = 0x540B;
+pub const TIOCGSOFTCAR: ::c_int = 0x5419;
+pub const TIOCSSOFTCAR: ::c_int = 0x541A;
+pub const TIOCLINUX: ::c_int = 0x541C;
+pub const TIOCGSERIAL: ::c_int = 0x541E;
+pub const TIOCEXCL: ::c_int = 0x540C;
+pub const TIOCNXCL: ::c_int = 0x540D;
+pub const TIOCSCTTY: ::c_int = 0x540E;
+pub const TIOCGPGRP: ::c_int = 0x540F;
+pub const TIOCSPGRP: ::c_int = 0x5410;
+pub const TIOCOUTQ: ::c_int = 0x5411;
+pub const TIOCSTI: ::c_int = 0x5412;
+pub const TIOCGWINSZ: ::c_int = 0x5413;
+pub const TIOCSWINSZ: ::c_int = 0x5414;
+pub const TIOCMGET: ::c_int = 0x5415;
+pub const TIOCMBIS: ::c_int = 0x5416;
+pub const TIOCMBIC: ::c_int = 0x5417;
+pub const TIOCMSET: ::c_int = 0x5418;
+pub const FIONREAD: ::c_int = 0x541B;
+pub const TIOCCONS: ::c_int = 0x541D;
 
 pub const SYS_gettid: ::c_long = 224;
 pub const SYS_perf_event_open: ::c_long = 364;
index 13790ef4dfcefa2b3a15e49121e537900a98d1ca..93e4ab6b6dbccc907e4d31205cd99e7b7218701c 100644 (file)
@@ -121,6 +121,7 @@ pub const MAP_STACK: ::c_int = 0x020000;
 
 pub const SOCK_STREAM: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 2;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 
 pub const SOL_SOCKET: ::c_int = 1;
 
@@ -272,35 +273,35 @@ pub const IEXTEN: ::tcflag_t = 0x00008000;
 pub const TOSTOP: ::tcflag_t = 0x00000100;
 pub const FLUSHO: ::tcflag_t = 0x00001000;
 
-pub const TCGETS: ::c_ulong = 0x5401;
-pub const TCSETS: ::c_ulong = 0x5402;
-pub const TCSETSW: ::c_ulong = 0x5403;
-pub const TCSETSF: ::c_ulong = 0x5404;
-pub const TCGETA: ::c_ulong = 0x5405;
-pub const TCSETA: ::c_ulong = 0x5406;
-pub const TCSETAW: ::c_ulong = 0x5407;
-pub const TCSETAF: ::c_ulong = 0x5408;
-pub const TCSBRK: ::c_ulong = 0x5409;
-pub const TCXONC: ::c_ulong = 0x540A;
-pub const TCFLSH: ::c_ulong = 0x540B;
-pub const TIOCGSOFTCAR: ::c_ulong = 0x5419;
-pub const TIOCSSOFTCAR: ::c_ulong = 0x541A;
-pub const TIOCLINUX: ::c_ulong = 0x541C;
-pub const TIOCGSERIAL: ::c_ulong = 0x541E;
-pub const TIOCEXCL: ::c_ulong = 0x540C;
-pub const TIOCNXCL: ::c_ulong = 0x540D;
-pub const TIOCSCTTY: ::c_ulong = 0x540E;
-pub const TIOCGPGRP: ::c_ulong = 0x540F;
-pub const TIOCSPGRP: ::c_ulong = 0x5410;
-pub const TIOCOUTQ: ::c_ulong = 0x5411;
-pub const TIOCSTI: ::c_ulong = 0x5412;
-pub const TIOCGWINSZ: ::c_ulong = 0x5413;
-pub const TIOCSWINSZ: ::c_ulong = 0x5414;
-pub const TIOCMGET: ::c_ulong = 0x5415;
-pub const TIOCMBIS: ::c_ulong = 0x5416;
-pub const TIOCMBIC: ::c_ulong = 0x5417;
-pub const TIOCMSET: ::c_ulong = 0x5418;
-pub const FIONREAD: ::c_ulong = 0x541B;
-pub const TIOCCONS: ::c_ulong = 0x541D;
+pub const TCGETS: ::c_int = 0x5401;
+pub const TCSETS: ::c_int = 0x5402;
+pub const TCSETSW: ::c_int = 0x5403;
+pub const TCSETSF: ::c_int = 0x5404;
+pub const TCGETA: ::c_int = 0x5405;
+pub const TCSETA: ::c_int = 0x5406;
+pub const TCSETAW: ::c_int = 0x5407;
+pub const TCSETAF: ::c_int = 0x5408;
+pub const TCSBRK: ::c_int = 0x5409;
+pub const TCXONC: ::c_int = 0x540A;
+pub const TCFLSH: ::c_int = 0x540B;
+pub const TIOCGSOFTCAR: ::c_int = 0x5419;
+pub const TIOCSSOFTCAR: ::c_int = 0x541A;
+pub const TIOCLINUX: ::c_int = 0x541C;
+pub const TIOCGSERIAL: ::c_int = 0x541E;
+pub const TIOCEXCL: ::c_int = 0x540C;
+pub const TIOCNXCL: ::c_int = 0x540D;
+pub const TIOCSCTTY: ::c_int = 0x540E;
+pub const TIOCGPGRP: ::c_int = 0x540F;
+pub const TIOCSPGRP: ::c_int = 0x5410;
+pub const TIOCOUTQ: ::c_int = 0x5411;
+pub const TIOCSTI: ::c_int = 0x5412;
+pub const TIOCGWINSZ: ::c_int = 0x5413;
+pub const TIOCSWINSZ: ::c_int = 0x5414;
+pub const TIOCMGET: ::c_int = 0x5415;
+pub const TIOCMBIS: ::c_int = 0x5416;
+pub const TIOCMBIC: ::c_int = 0x5417;
+pub const TIOCMSET: ::c_int = 0x5418;
+pub const FIONREAD: ::c_int = 0x541B;
+pub const TIOCCONS: ::c_int = 0x541D;
 
 pub const SYS_gettid: ::c_long = 224; // Valid for arm (32-bit) and x86 (32-bit)
index afe806acec6fefd1a8a3810456c8785ff1b1af63..f52d1953226e9b611d6781479c7f6bc76f7d8efe 100644 (file)
@@ -205,6 +205,7 @@ pub const ERFKILL: ::c_int = 167;
 
 pub const SOCK_STREAM: ::c_int = 2;
 pub const SOCK_DGRAM: ::c_int = 1;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 
 pub const SOL_SOCKET: ::c_int = 65535;
 
@@ -271,36 +272,36 @@ pub const IEXTEN: ::tcflag_t = 0o000400;
 pub const TOSTOP: ::tcflag_t = 0o100000;
 pub const FLUSHO: ::tcflag_t = 0o020000;
 
-pub const TCGETS: ::c_ulong = 0x540D;
-pub const TCSETS: ::c_ulong = 0x540E;
-pub const TCSETSW: ::c_ulong = 0x540F;
-pub const TCSETSF: ::c_ulong = 0x5410;
-pub const TCGETA: ::c_ulong = 0x5401;
-pub const TCSETA: ::c_ulong = 0x5402;
-pub const TCSETAW: ::c_ulong = 0x5403;
-pub const TCSETAF: ::c_ulong = 0x5404;
-pub const TCSBRK: ::c_ulong = 0x5405;
-pub const TCXONC: ::c_ulong = 0x5406;
-pub const TCFLSH: ::c_ulong = 0x5407;
-pub const TIOCGSOFTCAR: ::c_ulong = 0x5481;
-pub const TIOCSSOFTCAR: ::c_ulong = 0x5482;
-pub const TIOCLINUX: ::c_ulong = 0x5483;
-pub const TIOCGSERIAL: ::c_ulong = 0x5484;
-pub const TIOCEXCL: ::c_ulong = 0x740D;
-pub const TIOCNXCL: ::c_ulong = 0x740E;
-pub const TIOCSCTTY: ::c_ulong = 0x5480;
-pub const TIOCGPGRP: ::c_ulong = 0x40047477;
-pub const TIOCSPGRP: ::c_ulong = 0x80047476;
-pub const TIOCOUTQ: ::c_ulong = 0x7472;
-pub const TIOCSTI: ::c_ulong = 0x5472;
-pub const TIOCGWINSZ: ::c_ulong = 0x40087468;
-pub const TIOCSWINSZ: ::c_ulong = 0x80087467;
-pub const TIOCMGET: ::c_ulong = 0x741D;
-pub const TIOCMBIS: ::c_ulong = 0x741B;
-pub const TIOCMBIC: ::c_ulong = 0x741C;
-pub const TIOCMSET: ::c_ulong = 0x741D;
-pub const FIONREAD: ::c_ulong = 0x467F;
-pub const TIOCCONS: ::c_ulong = 0x80047478;
+pub const TCGETS: ::c_int = 0x540D;
+pub const TCSETS: ::c_int = 0x540E;
+pub const TCSETSW: ::c_int = 0x540F;
+pub const TCSETSF: ::c_int = 0x5410;
+pub const TCGETA: ::c_int = 0x5401;
+pub const TCSETA: ::c_int = 0x5402;
+pub const TCSETAW: ::c_int = 0x5403;
+pub const TCSETAF: ::c_int = 0x5404;
+pub const TCSBRK: ::c_int = 0x5405;
+pub const TCXONC: ::c_int = 0x5406;
+pub const TCFLSH: ::c_int = 0x5407;
+pub const TIOCGSOFTCAR: ::c_int = 0x5481;
+pub const TIOCSSOFTCAR: ::c_int = 0x5482;
+pub const TIOCLINUX: ::c_int = 0x5483;
+pub const TIOCGSERIAL: ::c_int = 0x5484;
+pub const TIOCEXCL: ::c_int = 0x740D;
+pub const TIOCNXCL: ::c_int = 0x740E;
+pub const TIOCSCTTY: ::c_int = 0x5480;
+pub const TIOCGPGRP: ::c_int = 0x40047477;
+pub const TIOCSPGRP: ::c_int = 0x80047476;
+pub const TIOCOUTQ: ::c_int = 0x7472;
+pub const TIOCSTI: ::c_int = 0x5472;
+pub const TIOCGWINSZ: ::c_int = 0x40087468;
+pub const TIOCSWINSZ: ::c_int = 0x80087467;
+pub const TIOCMGET: ::c_int = 0x741D;
+pub const TIOCMBIS: ::c_int = 0x741B;
+pub const TIOCMBIC: ::c_int = 0x741C;
+pub const TIOCMSET: ::c_int = 0x741D;
+pub const FIONREAD: ::c_int = 0x467F;
+pub const TIOCCONS: ::c_int = 0x80047478;
 
 pub const SYS_gettid: ::c_long = 4222;   // Valid for O32
 pub const SYS_perf_event_open: ::c_long = 4333;  // Valid for O32
index aae6c045731453f5e814dab8222431e7ce432bde..9d057de7b90586583273250e10a7711f5e8b11e2 100644 (file)
@@ -134,6 +134,7 @@ pub const MAP_STACK: ::c_int = 0x020000;
 
 pub const SOCK_STREAM: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 2;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 
 pub const SOL_SOCKET: ::c_int = 1;
 
@@ -286,36 +287,36 @@ pub const IEXTEN: ::tcflag_t = 0x00008000;
 pub const TOSTOP: ::tcflag_t = 0x00000100;
 pub const FLUSHO: ::tcflag_t = 0x00001000;
 
-pub const TCGETS: ::c_ulong = 0x5401;
-pub const TCSETS: ::c_ulong = 0x5402;
-pub const TCSETSW: ::c_ulong = 0x5403;
-pub const TCSETSF: ::c_ulong = 0x5404;
-pub const TCGETA: ::c_ulong = 0x5405;
-pub const TCSETA: ::c_ulong = 0x5406;
-pub const TCSETAW: ::c_ulong = 0x5407;
-pub const TCSETAF: ::c_ulong = 0x5408;
-pub const TCSBRK: ::c_ulong = 0x5409;
-pub const TCXONC: ::c_ulong = 0x540A;
-pub const TCFLSH: ::c_ulong = 0x540B;
-pub const TIOCGSOFTCAR: ::c_ulong = 0x5419;
-pub const TIOCSSOFTCAR: ::c_ulong = 0x541A;
-pub const TIOCLINUX: ::c_ulong = 0x541C;
-pub const TIOCGSERIAL: ::c_ulong = 0x541E;
-pub const TIOCEXCL: ::c_ulong = 0x540C;
-pub const TIOCNXCL: ::c_ulong = 0x540D;
-pub const TIOCSCTTY: ::c_ulong = 0x540E;
-pub const TIOCGPGRP: ::c_ulong = 0x540F;
-pub const TIOCSPGRP: ::c_ulong = 0x5410;
-pub const TIOCOUTQ: ::c_ulong = 0x5411;
-pub const TIOCSTI: ::c_ulong = 0x5412;
-pub const TIOCGWINSZ: ::c_ulong = 0x5413;
-pub const TIOCSWINSZ: ::c_ulong = 0x5414;
-pub const TIOCMGET: ::c_ulong = 0x5415;
-pub const TIOCMBIS: ::c_ulong = 0x5416;
-pub const TIOCMBIC: ::c_ulong = 0x5417;
-pub const TIOCMSET: ::c_ulong = 0x5418;
-pub const FIONREAD: ::c_ulong = 0x541B;
-pub const TIOCCONS: ::c_ulong = 0x541D;
+pub const TCGETS: ::c_int = 0x5401;
+pub const TCSETS: ::c_int = 0x5402;
+pub const TCSETSW: ::c_int = 0x5403;
+pub const TCSETSF: ::c_int = 0x5404;
+pub const TCGETA: ::c_int = 0x5405;
+pub const TCSETA: ::c_int = 0x5406;
+pub const TCSETAW: ::c_int = 0x5407;
+pub const TCSETAF: ::c_int = 0x5408;
+pub const TCSBRK: ::c_int = 0x5409;
+pub const TCXONC: ::c_int = 0x540A;
+pub const TCFLSH: ::c_int = 0x540B;
+pub const TIOCGSOFTCAR: ::c_int = 0x5419;
+pub const TIOCSSOFTCAR: ::c_int = 0x541A;
+pub const TIOCLINUX: ::c_int = 0x541C;
+pub const TIOCGSERIAL: ::c_int = 0x541E;
+pub const TIOCEXCL: ::c_int = 0x540C;
+pub const TIOCNXCL: ::c_int = 0x540D;
+pub const TIOCSCTTY: ::c_int = 0x540E;
+pub const TIOCGPGRP: ::c_int = 0x540F;
+pub const TIOCSPGRP: ::c_int = 0x5410;
+pub const TIOCOUTQ: ::c_int = 0x5411;
+pub const TIOCSTI: ::c_int = 0x5412;
+pub const TIOCGWINSZ: ::c_int = 0x5413;
+pub const TIOCSWINSZ: ::c_int = 0x5414;
+pub const TIOCMGET: ::c_int = 0x5415;
+pub const TIOCMBIS: ::c_int = 0x5416;
+pub const TIOCMBIC: ::c_int = 0x5417;
+pub const TIOCMSET: ::c_int = 0x5418;
+pub const FIONREAD: ::c_int = 0x541B;
+pub const TIOCCONS: ::c_int = 0x541D;
 
 pub const SYS_gettid: ::c_long = 224;
 pub const SYS_perf_event_open: ::c_long = 336;
index 6946b66eaa6981f7d786d9c2ef7e2f2a28ab9c20..a63aa9bcb2e67ad5aae767fa6e0032d7ba2dc16a 100644 (file)
@@ -143,6 +143,7 @@ pub const MAP_32BIT: ::c_int = 0x0040;
 
 pub const SOCK_STREAM: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 2;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 
 pub const SOL_SOCKET: ::c_int = 1;
 
@@ -294,36 +295,36 @@ pub const IEXTEN: ::tcflag_t = 0x00008000;
 pub const TOSTOP: ::tcflag_t = 0x00000100;
 pub const FLUSHO: ::tcflag_t = 0x00001000;
 
-pub const TCGETS: ::c_ulong = 0x5401;
-pub const TCSETS: ::c_ulong = 0x5402;
-pub const TCSETSW: ::c_ulong = 0x5403;
-pub const TCSETSF: ::c_ulong = 0x5404;
-pub const TCGETA: ::c_ulong = 0x5405;
-pub const TCSETA: ::c_ulong = 0x5406;
-pub const TCSETAW: ::c_ulong = 0x5407;
-pub const TCSETAF: ::c_ulong = 0x5408;
-pub const TCSBRK: ::c_ulong = 0x5409;
-pub const TCXONC: ::c_ulong = 0x540A;
-pub const TCFLSH: ::c_ulong = 0x540B;
-pub const TIOCGSOFTCAR: ::c_ulong = 0x5419;
-pub const TIOCSSOFTCAR: ::c_ulong = 0x541A;
-pub const TIOCLINUX: ::c_ulong = 0x541C;
-pub const TIOCGSERIAL: ::c_ulong = 0x541E;
-pub const TIOCEXCL: ::c_ulong = 0x540C;
-pub const TIOCNXCL: ::c_ulong = 0x540D;
-pub const TIOCSCTTY: ::c_ulong = 0x540E;
-pub const TIOCGPGRP: ::c_ulong = 0x540F;
-pub const TIOCSPGRP: ::c_ulong = 0x5410;
-pub const TIOCOUTQ: ::c_ulong = 0x5411;
-pub const TIOCSTI: ::c_ulong = 0x5412;
-pub const TIOCGWINSZ: ::c_ulong = 0x5413;
-pub const TIOCSWINSZ: ::c_ulong = 0x5414;
-pub const TIOCMGET: ::c_ulong = 0x5415;
-pub const TIOCMBIS: ::c_ulong = 0x5416;
-pub const TIOCMBIC: ::c_ulong = 0x5417;
-pub const TIOCMSET: ::c_ulong = 0x5418;
-pub const FIONREAD: ::c_ulong = 0x541B;
-pub const TIOCCONS: ::c_ulong = 0x541D;
+pub const TCGETS: ::c_int = 0x5401;
+pub const TCSETS: ::c_int = 0x5402;
+pub const TCSETSW: ::c_int = 0x5403;
+pub const TCSETSF: ::c_int = 0x5404;
+pub const TCGETA: ::c_int = 0x5405;
+pub const TCSETA: ::c_int = 0x5406;
+pub const TCSETAW: ::c_int = 0x5407;
+pub const TCSETAF: ::c_int = 0x5408;
+pub const TCSBRK: ::c_int = 0x5409;
+pub const TCXONC: ::c_int = 0x540A;
+pub const TCFLSH: ::c_int = 0x540B;
+pub const TIOCGSOFTCAR: ::c_int = 0x5419;
+pub const TIOCSSOFTCAR: ::c_int = 0x541A;
+pub const TIOCLINUX: ::c_int = 0x541C;
+pub const TIOCGSERIAL: ::c_int = 0x541E;
+pub const TIOCEXCL: ::c_int = 0x540C;
+pub const TIOCNXCL: ::c_int = 0x540D;
+pub const TIOCSCTTY: ::c_int = 0x540E;
+pub const TIOCGPGRP: ::c_int = 0x540F;
+pub const TIOCSPGRP: ::c_int = 0x5410;
+pub const TIOCOUTQ: ::c_int = 0x5411;
+pub const TIOCSTI: ::c_int = 0x5412;
+pub const TIOCGWINSZ: ::c_int = 0x5413;
+pub const TIOCSWINSZ: ::c_int = 0x5414;
+pub const TIOCMGET: ::c_int = 0x5415;
+pub const TIOCMBIS: ::c_int = 0x5416;
+pub const TIOCMBIC: ::c_int = 0x5417;
+pub const TIOCMSET: ::c_int = 0x5418;
+pub const FIONREAD: ::c_int = 0x541B;
+pub const TIOCCONS: ::c_int = 0x541D;
 
 cfg_if! {
     if #[cfg(target_arch = "aarch64")] {
index 631a39a4932f5ab81b96982c9525b75a613585da..47fa8886750086371d07c15c7765f6fe4e5dc36f 100644 (file)
@@ -56,6 +56,23 @@ s! {
         pub l_len: ::off_t,
         pub l_pid: ::pid_t,
     }
+
+    pub struct sysinfo {
+        pub uptime: ::c_ulong,
+        pub loads: [::c_ulong; 3],
+        pub totalram: ::c_ulong,
+        pub freeram: ::c_ulong,
+        pub sharedram: ::c_ulong,
+        pub bufferram: ::c_ulong,
+        pub totalswap: ::c_ulong,
+        pub freeswap: ::c_ulong,
+        pub procs: ::c_ushort,
+        pub pad: ::c_ushort,
+        pub totalhigh: ::c_ulong,
+        pub freehigh: ::c_ulong,
+        pub mem_unit: ::c_uint,
+        pub __reserved: [::c_char; 256],
+    }
 }
 
 pub const BUFSIZ: ::c_uint = 1024;
@@ -143,7 +160,7 @@ pub const TCSANOW: ::c_int = 0;
 pub const TCSADRAIN: ::c_int = 1;
 pub const TCSAFLUSH: ::c_int = 2;
 
-pub const TIOCINQ: ::c_ulong = ::FIONREAD;
+pub const TIOCINQ: ::c_int = ::FIONREAD;
 
 pub const RTLD_GLOBAL: ::c_int = 0x100;
 pub const RTLD_NOLOAD: ::c_int = 0x4;
@@ -156,6 +173,8 @@ pub const CLOCK_TAI: ::clockid_t = 11;
 extern {
     pub fn ioctl(fd: ::c_int, request: ::c_int, ...) -> ::c_int;
     pub fn ptrace(request: ::c_int, ...) -> ::c_long;
+    pub fn getpriority(which: ::c_int, who: ::id_t) -> ::c_int;
+    pub fn setpriority(which: ::c_int, who: ::id_t, prio: ::c_int) -> ::c_int;
 }
 
 cfg_if! {
index b39ff683f60e6a7626183017d21731882b043e22..afa8a8459cbd8a671978da3ce66e9f03d443a35e 100644 (file)
@@ -66,6 +66,23 @@ s! {
     pub struct sigset_t {
         __val: [::c_ulong; 32],
     }
+
+    pub struct sysinfo {
+        pub uptime: ::c_long,
+        pub loads: [::c_ulong; 3],
+        pub totalram: ::c_ulong,
+        pub freeram: ::c_ulong,
+        pub sharedram: ::c_ulong,
+        pub bufferram: ::c_ulong,
+        pub totalswap: ::c_ulong,
+        pub freeswap: ::c_ulong,
+        pub procs: ::c_ushort,
+        pub pad: ::c_ushort,
+        pub totalhigh: ::c_ulong,
+        pub freehigh: ::c_ulong,
+        pub mem_unit: ::c_uint,
+        pub _f: [::c_char; 8],
+    }
 }
 
 pub const __SIZEOF_PTHREAD_MUTEX_T: usize = 24;
index 663b09333cdf7cf32bcfb7135cfe214c44f3eeb7..ccf99881f76cc1502d32c6bce13358a93def5de1 100644 (file)
@@ -14,6 +14,23 @@ s! {
     pub struct sigset_t {
         __val: [::c_ulong; 16],
     }
+
+    pub struct sysinfo {
+        pub uptime: ::c_long,
+        pub loads: [::c_ulong; 3],
+        pub totalram: ::c_ulong,
+        pub freeram: ::c_ulong,
+        pub sharedram: ::c_ulong,
+        pub bufferram: ::c_ulong,
+        pub totalswap: ::c_ulong,
+        pub freeswap: ::c_ulong,
+        pub procs: ::c_ushort,
+        pub pad: ::c_ushort,
+        pub totalhigh: ::c_ulong,
+        pub freehigh: ::c_ulong,
+        pub mem_unit: ::c_uint,
+        pub _f: [::c_char; 0],
+    }
 }
 
 pub const __SIZEOF_PTHREAD_RWLOCK_T: usize = 56;
index 63a3e92017fa9ccd689247949960c72bfa21150c..a6bacabe5f084928f7d5e2a8a280cafbd487909f 100644 (file)
@@ -1,6 +1,7 @@
 pub type fsblkcnt_t = ::c_ulong;
 pub type fsfilcnt_t = ::c_ulong;
 pub type rlim_t = c_ulong;
+pub type __priority_which_t = ::c_uint;
 
 s! {
     pub struct sigaction {
@@ -263,6 +264,7 @@ pub const ERFKILL: ::c_int = 132;
 
 pub const SOCK_STREAM: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 2;
+pub const SOCK_SEQPACKET: ::c_int = 5;
 
 pub const SOL_SOCKET: ::c_int = 1;
 
@@ -495,6 +497,15 @@ extern {
     pub fn pthread_attr_setaffinity_np(attr: *mut ::pthread_attr_t,
                                        cpusetsize: ::size_t,
                                        cpuset: *const ::cpu_set_t) -> ::c_int;
+    pub fn getpriority(which: ::__priority_which_t, who: ::id_t) -> ::c_int;
+    pub fn setpriority(which: ::__priority_which_t, who: ::id_t,
+                                       prio: ::c_int) -> ::c_int;
+    pub fn pthread_getaffinity_np(thread: ::pthread_t,
+                                  cpusetsize: ::size_t,
+                                  cpuset: *mut ::cpu_set_t) -> ::c_int;
+    pub fn pthread_setaffinity_np(thread: ::pthread_t,
+                                  cpusetsize: ::size_t,
+                                  cpuset: *const ::cpu_set_t) -> ::c_int;
 }
 
 cfg_if! {
index 94868d8a434a97b4a947c0ca83219ed0254609eb..5ee8aa1e5ef3008e11bf51876d1df557465902c3 100644 (file)
@@ -6,6 +6,7 @@ pub type speed_t = ::c_uint;
 pub type tcflag_t = ::c_uint;
 pub type loff_t = ::c_longlong;
 pub type clockid_t = ::c_int;
+pub type id_t = ::c_uint;
 
 pub enum timezone {}
 
@@ -652,6 +653,8 @@ pub const LOG_PERROR: ::c_int = 0x20;
 
 pub const PIPE_BUF: usize = 4096;
 
+pub const SI_LOAD_SHIFT: ::c_uint = 16;
+
 f! {
     pub fn FD_CLR(fd: ::c_int, set: *mut fd_set) -> () {
         let fd = fd as usize;
@@ -800,6 +803,7 @@ extern {
     pub fn setrlimit64(resource: ::c_int, rlim: *const rlimit64) -> ::c_int;
     pub fn stat64(path: *const c_char, buf: *mut stat64) -> ::c_int;
     pub fn eventfd(init: ::c_uint, flags: ::c_int) -> ::c_int;
+    pub fn sysinfo (info: *mut ::sysinfo) -> ::c_int;
 }
 
 cfg_if! {
index 0cb7e5443698894fd4e3f50a1114c4936e2b1128..82a7f6e3711c1524b184dbd52b3ca07fa7c17102 100644 (file)
@@ -696,6 +696,8 @@ pub const AF_UNIX: ::c_int = 1;
 pub const SOCK_DGRAM: ::c_int = 1;
 pub const SOCK_STREAM: ::c_int = 2;
 pub const SOCK_RAW: ::c_int = 4;
+pub const SOCK_RDM: ::c_int = 5;
+pub const SOCK_SEQPACKET: ::c_int = 6;
 pub const IPPROTO_TCP: ::c_int = 6;
 pub const IPPROTO_IP: ::c_int = 0;
 pub const IPPROTO_IPV6: ::c_int = 41;
@@ -831,7 +833,10 @@ pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t {
     __pthread_rwlock_readercv: PTHREAD_COND_INITIALIZER,
     __pthread_rwlock_writercv: PTHREAD_COND_INITIALIZER
 };
+pub const PTHREAD_MUTEX_NORMAL: ::c_int = 0;
+pub const PTHREAD_MUTEX_ERRORCHECK: ::c_int = 2;
 pub const PTHREAD_MUTEX_RECURSIVE: ::c_int = 4;
+pub const PTHREAD_MUTEX_DEFAULT: ::c_int = PTHREAD_MUTEX_NORMAL;
 
 f! {
     pub fn FD_CLR(fd: ::c_int, set: *mut fd_set) -> () {
@@ -904,5 +909,7 @@ extern {
     pub fn getprogname() -> *const ::c_char;
     pub fn setprogname(name: *const ::c_char);
     pub fn getloadavg(loadavg: *mut ::c_double, nelem: ::c_int) -> ::c_int;
+    pub fn getpriority(which: ::c_int, who: ::c_int) -> ::c_int;
+    pub fn setpriority(which: ::c_int, who: ::c_int, prio: ::c_int) -> ::c_int;
 }
 
index a71f6efe54ebcaa5b6afe194e90d02ce521b26f6..517cd016e8a35ab7a9d7ad2cfe7ba4c61ceb9906 100644 (file)
@@ -179,7 +179,7 @@ use std::io::prelude::*;
 use std::mem;
 use std::env;
 use std::slice;
-use std::sync::{Once, Mutex, ONCE_INIT};
+use std::sync::{Mutex, ONCE_INIT, Once};
 
 use directive::LOG_LEVEL_NAMES;
 
@@ -290,9 +290,7 @@ pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) {
     // frob the slot while we're doing the logging. This will destroy any logger
     // set during logging.
     let logger = LOCAL_LOGGER.with(|s| s.borrow_mut().take());
-    let mut logger = logger.unwrap_or_else(|| {
-        Box::new(DefaultLogger { handle: io::stderr() })
-    });
+    let mut logger = logger.unwrap_or_else(|| Box::new(DefaultLogger { handle: io::stderr() }));
     logger.log(&LogRecord {
         level: LogLevel(level),
         args: args,
index c085ddeb75b97f407ab552f825c4b66a381b45b7..b87160dd75d046e6c8f5068d886e0e01a4f92be6 100644 (file)
@@ -25,8 +25,8 @@
 
 #![feature(staged_api)]
 
-#![cfg_attr(not(stage0), panic_runtime)]
-#![cfg_attr(not(stage0), feature(panic_runtime))]
+#![panic_runtime]
+#![feature(panic_runtime)]
 #![cfg_attr(unix, feature(libc))]
 #![cfg_attr(windows, feature(core_intrinsics))]
 
@@ -93,7 +93,6 @@ pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 {
 // Essentially this symbol is just defined to get wired up to libcore/libstd
 // binaries, but it should never be called as we don't link in an unwinding
 // runtime at all.
-#[cfg(not(stage0))]
 pub mod personalities {
 
     #[no_mangle]
index 1c3fca98a1f71efdbd4afea1a6429aa85580a804..0ad6a74d1013c60b562ed1d9ff7467b2bedd5075 100644 (file)
 use dwarf::DwarfReader;
 use core::mem;
 
-pub const DW_EH_PE_omit     : u8 = 0xFF;
-pub const DW_EH_PE_absptr   : u8 = 0x00;
-
-pub const DW_EH_PE_uleb128  : u8 = 0x01;
-pub const DW_EH_PE_udata2   : u8 = 0x02;
-pub const DW_EH_PE_udata4   : u8 = 0x03;
-pub const DW_EH_PE_udata8   : u8 = 0x04;
-pub const DW_EH_PE_sleb128  : u8 = 0x09;
-pub const DW_EH_PE_sdata2   : u8 = 0x0A;
-pub const DW_EH_PE_sdata4   : u8 = 0x0B;
-pub const DW_EH_PE_sdata8   : u8 = 0x0C;
-
-pub const DW_EH_PE_pcrel    : u8 = 0x10;
-pub const DW_EH_PE_textrel  : u8 = 0x20;
-pub const DW_EH_PE_datarel  : u8 = 0x30;
-pub const DW_EH_PE_funcrel  : u8 = 0x40;
-pub const DW_EH_PE_aligned  : u8 = 0x50;
-
-pub const DW_EH_PE_indirect : u8 = 0x80;
+pub const DW_EH_PE_omit: u8 = 0xFF;
+pub const DW_EH_PE_absptr: u8 = 0x00;
+
+pub const DW_EH_PE_uleb128: u8 = 0x01;
+pub const DW_EH_PE_udata2: u8 = 0x02;
+pub const DW_EH_PE_udata4: u8 = 0x03;
+pub const DW_EH_PE_udata8: u8 = 0x04;
+pub const DW_EH_PE_sleb128: u8 = 0x09;
+pub const DW_EH_PE_sdata2: u8 = 0x0A;
+pub const DW_EH_PE_sdata4: u8 = 0x0B;
+pub const DW_EH_PE_sdata8: u8 = 0x0C;
+
+pub const DW_EH_PE_pcrel: u8 = 0x10;
+pub const DW_EH_PE_textrel: u8 = 0x20;
+pub const DW_EH_PE_datarel: u8 = 0x30;
+pub const DW_EH_PE_funcrel: u8 = 0x40;
+pub const DW_EH_PE_aligned: u8 = 0x50;
+
+pub const DW_EH_PE_indirect: u8 = 0x80;
 
 #[derive(Copy, Clone)]
 pub struct EHContext {
-    pub ip: usize,         // Current instruction pointer
+    pub ip: usize, // Current instruction pointer
     pub func_start: usize, // Address of the current function
     pub text_start: usize, // Address of the code section
     pub data_start: usize, // Address of the data section
 }
 
-pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext)
-                               -> Option<usize> {
+pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext) -> Option<usize> {
     if lsda.is_null() {
         return None;
     }
@@ -80,7 +79,7 @@ pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext)
     let action_table = reader.ptr.offset(call_site_table_length as isize);
     // Return addresses point 1 byte past the call instruction, which could
     // be in the next IP range.
-    let ip = context.ip-1;
+    let ip = context.ip - 1;
 
     while reader.ptr < action_table {
         let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding);
@@ -90,7 +89,7 @@ pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext)
         // Callsite table is sorted by cs_start, so if we've passed the ip, we
         // may stop searching.
         if ip < func_start + cs_start {
-            break
+            break;
         }
         if ip < func_start + cs_start + cs_len {
             if cs_lpad != 0 {
@@ -114,13 +113,13 @@ fn round_up(unrounded: usize, align: usize) -> usize {
 
 unsafe fn read_encoded_pointer(reader: &mut DwarfReader,
                                context: &EHContext,
-                               encoding: u8) -> usize {
+                               encoding: u8)
+                               -> usize {
     assert!(encoding != DW_EH_PE_omit);
 
     // DW_EH_PE_aligned implies it's an absolute pointer value
     if encoding == DW_EH_PE_aligned {
-        reader.ptr = round_up(reader.ptr as usize,
-                              mem::size_of::<usize>()) as *const u8;
+        reader.ptr = round_up(reader.ptr as usize, mem::size_of::<usize>()) as *const u8;
         return reader.read::<usize>();
     }
 
@@ -134,20 +133,26 @@ unsafe fn read_encoded_pointer(reader: &mut DwarfReader,
         DW_EH_PE_sdata2 => reader.read::<i16>() as usize,
         DW_EH_PE_sdata4 => reader.read::<i32>() as usize,
         DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
-        _ => panic!()
+        _ => panic!(),
     };
 
     result += match encoding & 0x70 {
         DW_EH_PE_absptr => 0,
         // relative to address of the encoded value, despite the name
         DW_EH_PE_pcrel => reader.ptr as usize,
-        DW_EH_PE_textrel => { assert!(context.text_start != 0);
-                              context.text_start },
-        DW_EH_PE_datarel => { assert!(context.data_start != 0);
-                              context.data_start },
-        DW_EH_PE_funcrel => { assert!(context.func_start != 0);
-                              context.func_start },
-        _ => panic!()
+        DW_EH_PE_textrel => {
+            assert!(context.text_start != 0);
+            context.text_start
+        }
+        DW_EH_PE_datarel => {
+            assert!(context.data_start != 0);
+            context.data_start
+        }
+        DW_EH_PE_funcrel => {
+            assert!(context.func_start != 0);
+            context.func_start
+        }
+        _ => panic!(),
     };
 
     if encoding & DW_EH_PE_indirect != 0 {
index cde21f90811dee249bd893e9cb6d92c4180aca27..5c05ac11d307d1c74884c4f18315a6caa0f48991 100644 (file)
@@ -21,25 +21,22 @@ pub mod eh;
 use core::mem;
 
 pub struct DwarfReader {
-    pub ptr : *const u8
+    pub ptr: *const u8,
 }
 
 #[repr(C,packed)]
 struct Unaligned<T>(T);
 
 impl DwarfReader {
-
-    pub fn new(ptr : *const u8) -> DwarfReader {
-        DwarfReader {
-            ptr : ptr
-        }
+    pub fn new(ptr: *const u8) -> DwarfReader {
+        DwarfReader { ptr: ptr }
     }
 
     // DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
     // on a 4-byte boundary. This may cause problems on platforms with strict
     // alignment requirements. By wrapping data in a "packed" struct, we are
     // telling the backend to generate "misalignment-safe" code.
-    pub unsafe fn read<T:Copy>(&mut self) -> T {
+    pub unsafe fn read<T: Copy>(&mut self) -> T {
         let Unaligned(result) = *(self.ptr as *const Unaligned<T>);
         self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
         result
@@ -48,9 +45,9 @@ impl DwarfReader {
     // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
     // Length Data".
     pub unsafe fn read_uleb128(&mut self) -> u64 {
-        let mut shift : usize = 0;
-        let mut result : u64 = 0;
-        let mut byte : u8;
+        let mut shift: usize = 0;
+        let mut result: u64 = 0;
+        let mut byte: u8;
         loop {
             byte = self.read::<u8>();
             result |= ((byte & 0x7F) as u64) << shift;
@@ -63,9 +60,9 @@ impl DwarfReader {
     }
 
     pub unsafe fn read_sleb128(&mut self) -> i64 {
-        let mut shift : usize = 0;
-        let mut result : u64 = 0;
-        let mut byte : u8;
+        let mut shift: usize = 0;
+        let mut result: u64 = 0;
+        let mut byte: u8;
         loop {
             byte = self.read::<u8>();
             result |= ((byte & 0x7F) as u64) << shift;
@@ -84,12 +81,7 @@ impl DwarfReader {
 
 #[test]
 fn dwarf_reader() {
-    let encoded: &[u8] = &[1,
-                           2, 3,
-                           4, 5, 6, 7,
-                           0xE5, 0x8E, 0x26,
-                           0x9B, 0xF1, 0x59,
-                           0xFF, 0xFF];
+    let encoded: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 0xE5, 0x8E, 0x26, 0x9B, 0xF1, 0x59, 0xFF, 0xFF];
 
     let mut reader = DwarfReader::new(encoded.as_ptr());
 
index 50b2e1534d70ddba657a1e3f8f0c294da00adb25..8df68da3786e2b8d28134447dc65784c85c24e5a 100644 (file)
@@ -79,8 +79,8 @@ pub unsafe fn panic(data: Box<Any + Send>) -> u32 {
     let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception;
     return uw::_Unwind_RaiseException(exception_param) as u32;
 
-    extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
-                                exception: *mut uw::_Unwind_Exception) {
+    extern "C" fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
+                                    exception: *mut uw::_Unwind_Exception) {
         unsafe {
             let _: Box<Exception> = Box::from_raw(exception as *mut Exception);
         }
@@ -130,50 +130,41 @@ pub mod eabi {
     use unwind as uw;
     use libc::c_int;
 
-    extern {
+    extern "C" {
         fn __gcc_personality_v0(version: c_int,
                                 actions: uw::_Unwind_Action,
                                 exception_class: uw::_Unwind_Exception_Class,
                                 ue_header: *mut uw::_Unwind_Exception,
                                 context: *mut uw::_Unwind_Context)
-            -> uw::_Unwind_Reason_Code;
+                                -> uw::_Unwind_Reason_Code;
     }
 
     #[lang = "eh_personality"]
     #[no_mangle]
-    extern fn rust_eh_personality(
-        version: c_int,
-        actions: uw::_Unwind_Action,
-        exception_class: uw::_Unwind_Exception_Class,
-        ue_header: *mut uw::_Unwind_Exception,
-        context: *mut uw::_Unwind_Context
-    ) -> uw::_Unwind_Reason_Code
-    {
-        unsafe {
-            __gcc_personality_v0(version, actions, exception_class, ue_header,
-                                 context)
-        }
+    extern "C" fn rust_eh_personality(version: c_int,
+                                      actions: uw::_Unwind_Action,
+                                      exception_class: uw::_Unwind_Exception_Class,
+                                      ue_header: *mut uw::_Unwind_Exception,
+                                      context: *mut uw::_Unwind_Context)
+                                      -> uw::_Unwind_Reason_Code {
+        unsafe { __gcc_personality_v0(version, actions, exception_class, ue_header, context) }
     }
 
     #[lang = "eh_personality_catch"]
     #[no_mangle]
-    pub extern fn rust_eh_personality_catch(
-        version: c_int,
-        actions: uw::_Unwind_Action,
-        exception_class: uw::_Unwind_Exception_Class,
-        ue_header: *mut uw::_Unwind_Exception,
-        context: *mut uw::_Unwind_Context
-    ) -> uw::_Unwind_Reason_Code
-    {
+    pub extern "C" fn rust_eh_personality_catch(version: c_int,
+                                                actions: uw::_Unwind_Action,
+                                                exception_class: uw::_Unwind_Exception_Class,
+                                                ue_header: *mut uw::_Unwind_Exception,
+                                                context: *mut uw::_Unwind_Context)
+                                                -> uw::_Unwind_Reason_Code {
 
-        if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
+        if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 {
+            // search phase
             uw::_URC_HANDLER_FOUND // catch!
-        }
-        else { // cleanup phase
-            unsafe {
-                __gcc_personality_v0(version, actions, exception_class, ue_header,
-                                     context)
-            }
+        } else {
+            // cleanup phase
+            unsafe { __gcc_personality_v0(version, actions, exception_class, ue_header, context) }
         }
     }
 }
@@ -186,49 +177,40 @@ pub mod eabi {
     use unwind as uw;
     use libc::c_int;
 
-    extern {
+    extern "C" {
         fn __gcc_personality_sj0(version: c_int,
-                                actions: uw::_Unwind_Action,
-                                exception_class: uw::_Unwind_Exception_Class,
-                                ue_header: *mut uw::_Unwind_Exception,
-                                context: *mut uw::_Unwind_Context)
-            -> uw::_Unwind_Reason_Code;
+                                 actions: uw::_Unwind_Action,
+                                 exception_class: uw::_Unwind_Exception_Class,
+                                 ue_header: *mut uw::_Unwind_Exception,
+                                 context: *mut uw::_Unwind_Context)
+                                 -> uw::_Unwind_Reason_Code;
     }
 
     #[lang = "eh_personality"]
     #[no_mangle]
-    pub extern fn rust_eh_personality(
-        version: c_int,
-        actions: uw::_Unwind_Action,
-        exception_class: uw::_Unwind_Exception_Class,
-        ue_header: *mut uw::_Unwind_Exception,
-        context: *mut uw::_Unwind_Context
-    ) -> uw::_Unwind_Reason_Code
-    {
-        unsafe {
-            __gcc_personality_sj0(version, actions, exception_class, ue_header,
-                                  context)
-        }
+    pub extern "C" fn rust_eh_personality(version: c_int,
+                                          actions: uw::_Unwind_Action,
+                                          exception_class: uw::_Unwind_Exception_Class,
+                                          ue_header: *mut uw::_Unwind_Exception,
+                                          context: *mut uw::_Unwind_Context)
+                                          -> uw::_Unwind_Reason_Code {
+        unsafe { __gcc_personality_sj0(version, actions, exception_class, ue_header, context) }
     }
 
     #[lang = "eh_personality_catch"]
     #[no_mangle]
-    pub extern fn rust_eh_personality_catch(
-        version: c_int,
-        actions: uw::_Unwind_Action,
-        exception_class: uw::_Unwind_Exception_Class,
-        ue_header: *mut uw::_Unwind_Exception,
-        context: *mut uw::_Unwind_Context
-    ) -> uw::_Unwind_Reason_Code
-    {
-        if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
+    pub extern "C" fn rust_eh_personality_catch(version: c_int,
+                                                actions: uw::_Unwind_Action,
+                                                exception_class: uw::_Unwind_Exception_Class,
+                                                ue_header: *mut uw::_Unwind_Exception,
+                                                context: *mut uw::_Unwind_Context)
+                                                -> uw::_Unwind_Reason_Code {
+        if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 {
+            // search phase
             uw::_URC_HANDLER_FOUND // catch!
-        }
-        else { // cleanup phase
-            unsafe {
-                __gcc_personality_sj0(version, actions, exception_class, ue_header,
-                                      context)
-            }
+        } else {
+            // cleanup phase
+            unsafe { __gcc_personality_sj0(version, actions, exception_class, ue_header, context) }
         }
     }
 }
@@ -241,47 +223,40 @@ pub mod eabi {
     use unwind as uw;
     use libc::c_int;
 
-    extern {
+    extern "C" {
         fn __gcc_personality_v0(state: uw::_Unwind_State,
                                 ue_header: *mut uw::_Unwind_Exception,
                                 context: *mut uw::_Unwind_Context)
-            -> uw::_Unwind_Reason_Code;
+                                -> uw::_Unwind_Reason_Code;
     }
 
     #[lang = "eh_personality"]
     #[no_mangle]
-    extern fn rust_eh_personality(
-        state: uw::_Unwind_State,
-        ue_header: *mut uw::_Unwind_Exception,
-        context: *mut uw::_Unwind_Context
-    ) -> uw::_Unwind_Reason_Code
-    {
-        unsafe {
-            __gcc_personality_v0(state, ue_header, context)
-        }
+    extern "C" fn rust_eh_personality(state: uw::_Unwind_State,
+                                      ue_header: *mut uw::_Unwind_Exception,
+                                      context: *mut uw::_Unwind_Context)
+                                      -> uw::_Unwind_Reason_Code {
+        unsafe { __gcc_personality_v0(state, ue_header, context) }
     }
 
     #[lang = "eh_personality_catch"]
     #[no_mangle]
-    pub extern fn rust_eh_personality_catch(
-        state: uw::_Unwind_State,
-        ue_header: *mut uw::_Unwind_Exception,
-        context: *mut uw::_Unwind_Context
-    ) -> uw::_Unwind_Reason_Code
-    {
+    pub extern "C" fn rust_eh_personality_catch(state: uw::_Unwind_State,
+                                                ue_header: *mut uw::_Unwind_Exception,
+                                                context: *mut uw::_Unwind_Context)
+                                                -> uw::_Unwind_Reason_Code {
         // Backtraces on ARM will call the personality routine with
         // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
         // we want to continue unwinding the stack, otherwise all our backtraces
         // would end at __rust_try.
-        if (state as c_int & uw::_US_ACTION_MASK as c_int)
-                           == uw::_US_VIRTUAL_UNWIND_FRAME as c_int
-               && (state as c_int & uw::_US_FORCE_UNWIND as c_int) == 0 { // search phase
+        if (state as c_int & uw::_US_ACTION_MASK as c_int) ==
+           uw::_US_VIRTUAL_UNWIND_FRAME as c_int &&
+           (state as c_int & uw::_US_FORCE_UNWIND as c_int) == 0 {
+            // search phase
             uw::_URC_HANDLER_FOUND // catch!
-        }
-        else { // cleanup phase
-            unsafe {
-                __gcc_personality_v0(state, ue_header, context)
-            }
+        } else {
+            // cleanup phase
+            unsafe { __gcc_personality_v0(state, ue_header, context) }
         }
     }
 }
@@ -290,7 +265,7 @@ pub mod eabi {
 #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
 #[lang = "eh_unwind_resume"]
 #[unwind]
-unsafe extern fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
+unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
     uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception);
 }
 
@@ -314,22 +289,21 @@ unsafe extern fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
 pub mod eh_frame_registry {
     #[link(name = "gcc_eh")]
     #[cfg(not(cargobuild))]
-    extern {}
+    extern "C" {}
 
-    extern {
+    extern "C" {
         fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8);
         fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8);
     }
 
     #[no_mangle]
-    pub unsafe extern fn rust_eh_register_frames(eh_frame_begin: *const u8,
-                                                 object: *mut u8) {
+    pub unsafe extern "C" fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8) {
         __register_frame_info(eh_frame_begin, object);
     }
 
     #[no_mangle]
-    pub  unsafe extern fn rust_eh_unregister_frames(eh_frame_begin: *const u8,
-                                                   object: *mut u8) {
+    pub unsafe extern "C" fn rust_eh_unregister_frames(eh_frame_begin: *const u8,
+                                                       object: *mut u8) {
         __deregister_frame_info(eh_frame_begin, object);
     }
 }
index 17cbd2e0d4c39b299a2530e12f7e2d05487874cf..b765ee6f81cf9a70468f9e02cfe3849c73a8ff08 100644 (file)
@@ -42,8 +42,8 @@
 #![feature(unwind_attributes)]
 #![cfg_attr(target_env = "msvc", feature(raw))]
 
-#![cfg_attr(not(stage0), panic_runtime)]
-#![cfg_attr(not(stage0), feature(panic_runtime))]
+#![panic_runtime]
+#![feature(panic_runtime)]
 
 extern crate alloc;
 extern crate libc;
@@ -82,11 +82,11 @@ mod windows;
 // hairy and tightly coupled, for more information see the compiler's
 // implementation of this.
 #[no_mangle]
-pub unsafe extern fn __rust_maybe_catch_panic(f: fn(*mut u8),
-                                              data: *mut u8,
-                                              data_ptr: *mut usize,
-                                              vtable_ptr: *mut usize)
-                                              -> u32 {
+pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
+                                                  data: *mut u8,
+                                                  data_ptr: *mut usize,
+                                                  vtable_ptr: *mut usize)
+                                                  -> u32 {
     let mut payload = imp::payload();
     if intrinsics::try(f, data, &mut payload as *mut _ as *mut _) == 0 {
         0
@@ -101,7 +101,7 @@ pub unsafe extern fn __rust_maybe_catch_panic(f: fn(*mut u8),
 // Entry point for raising an exception, just delegates to the platform-specific
 // implementation.
 #[no_mangle]
-pub unsafe extern fn __rust_start_panic(data: usize, vtable: usize) -> u32 {
+pub unsafe extern "C" fn __rust_start_panic(data: usize, vtable: usize) -> u32 {
     imp::panic(mem::transmute(raw::TraitObject {
         data: data as *mut (),
         vtable: vtable as *mut (),
index 04a3f7b9663fbe203ad1a87a30f3dc34329b7077..dd6e92fe9ae19efc4779af3f57a773fe46fa8a8a 100644 (file)
@@ -128,7 +128,7 @@ mod imp {
     pub const NAME1: [u8; 7] = [b'.', b'P', b'E', b'A', b'_', b'K', 0];
     pub const NAME2: [u8; 7] = [b'.', b'P', b'E', b'A', b'X', 0, 0];
 
-    extern {
+    extern "C" {
         pub static __ImageBase: u8;
     }
 
@@ -186,10 +186,7 @@ static mut THROW_INFO: _ThrowInfo = _ThrowInfo {
 
 static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray = _CatchableTypeArray {
     nCatchableTypes: 2,
-    arrayOfCatchableTypes: [
-        ptr!(0),
-        ptr!(0),
-    ],
+    arrayOfCatchableTypes: [ptr!(0), ptr!(0)],
 };
 
 static mut CATCHABLE_TYPE1: _CatchableType = _CatchableType {
@@ -216,7 +213,7 @@ static mut CATCHABLE_TYPE2: _CatchableType = _CatchableType {
     copy_function: ptr!(0),
 };
 
-extern {
+extern "C" {
     // The leading `\x01` byte here is actually a magical signal to LLVM to
     // *not* apply any other mangling like prefixing with a `_` character.
     //
@@ -233,8 +230,7 @@ extern {
 // an argument to the C++ personality function.
 //
 // Again, I'm not entirely sure what this is describing, it just seems to work.
-#[cfg_attr(all(not(test), not(stage0)),
-           lang = "msvc_try_filter")]
+#[cfg_attr(not(test), lang = "msvc_try_filter")]
 static mut TYPE_DESCRIPTOR1: _TypeDescriptor = _TypeDescriptor {
     pVFTable: &TYPE_INFO_VTABLE as *const _ as *const _,
     spare: 0 as *mut _,
@@ -308,13 +304,6 @@ pub unsafe fn cleanup(payload: [u64; 2]) -> Box<Any + Send> {
     })
 }
 
-#[lang = "msvc_try_filter"]
-#[cfg(stage0)]
-unsafe extern fn __rust_try_filter(_eh_ptrs: *mut u8,
-                                   _payload: *mut u8) -> i32 {
-    return 0
-}
-
 // This is required by the compiler to exist (e.g. it's a lang item), but
 // it's never actually called by the compiler because __C_specific_handler
 // or _except_handler3 is the personality function that is always used.
index adb38d857eac7ff6588f303ea2c1f2bd6b0779e3..12e1a764c5f9403865045990f0d4ae4602673a5b 100644 (file)
@@ -32,11 +32,11 @@ use windows as c;
 const ETYPE: c::DWORD = 0b1110_u32 << 28;
 const MAGIC: c::DWORD = 0x525354; // "RST"
 
-const RUST_PANIC: c::DWORD  = ETYPE | (1 << 24) | MAGIC;
+const RUST_PANIC: c::DWORD = ETYPE | (1 << 24) | MAGIC;
 
 #[repr(C)]
 struct PanicData {
-    data: Box<Any + Send>
+    data: Box<Any + Send>,
 }
 
 pub unsafe fn panic(data: Box<Any + Send>) -> u32 {
@@ -82,30 +82,29 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> {
 
 #[lang = "eh_personality_catch"]
 #[cfg(not(test))]
-unsafe extern fn rust_eh_personality_catch(
-    exceptionRecord: *mut c::EXCEPTION_RECORD,
-    establisherFrame: c::LPVOID,
-    contextRecord: *mut c::CONTEXT,
-    dispatcherContext: *mut c::DISPATCHER_CONTEXT
-) -> c::EXCEPTION_DISPOSITION
-{
-    rust_eh_personality(exceptionRecord, establisherFrame,
-                        contextRecord, dispatcherContext)
+unsafe extern "C" fn rust_eh_personality_catch(exceptionRecord: *mut c::EXCEPTION_RECORD,
+                                               establisherFrame: c::LPVOID,
+                                               contextRecord: *mut c::CONTEXT,
+                                               dispatcherContext: *mut c::DISPATCHER_CONTEXT)
+                                               -> c::EXCEPTION_DISPOSITION {
+    rust_eh_personality(exceptionRecord,
+                        establisherFrame,
+                        contextRecord,
+                        dispatcherContext)
 }
 
 #[lang = "eh_personality"]
 #[cfg(not(test))]
-unsafe extern fn rust_eh_personality(
-    exceptionRecord: *mut c::EXCEPTION_RECORD,
-    establisherFrame: c::LPVOID,
-    contextRecord: *mut c::CONTEXT,
-    dispatcherContext: *mut c::DISPATCHER_CONTEXT
-) -> c::EXCEPTION_DISPOSITION
-{
+unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut c::EXCEPTION_RECORD,
+                                         establisherFrame: c::LPVOID,
+                                         contextRecord: *mut c::CONTEXT,
+                                         dispatcherContext: *mut c::DISPATCHER_CONTEXT)
+                                         -> c::EXCEPTION_DISPOSITION {
     let er = &*exceptionRecord;
     let dc = &*dispatcherContext;
 
-    if er.ExceptionFlags & c::EXCEPTION_UNWIND == 0 { // we are in the dispatch phase
+    if er.ExceptionFlags & c::EXCEPTION_UNWIND == 0 {
+        // we are in the dispatch phase
         if er.ExceptionCode == RUST_PANIC {
             if let Some(lpad) = find_landing_pad(dc) {
                 c::RtlUnwindEx(establisherFrame,
@@ -122,7 +121,7 @@ unsafe extern fn rust_eh_personality(
 
 #[lang = "eh_unwind_resume"]
 #[unwind]
-unsafe extern fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! {
+unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! {
     let params = [panic_ctx as c::ULONG_PTR];
     c::RaiseException(RUST_PANIC,
                       c::EXCEPTION_NONCONTINUABLE,
@@ -136,7 +135,7 @@ unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> {
         ip: dc.ControlPc as usize,
         func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
         text_start: dc.ImageBase as usize,
-        data_start: 0
+        data_start: 0,
     };
     eh::find_landing_pad(dc.HandlerData, &eh_ctx)
 }
index 9cca018ff111a52ba465dd43019891230454e14b..fd8429d262e6ec2c99d384b7803b1ab33cb1243c 100644 (file)
@@ -12,7 +12,7 @@
 #![allow(dead_code)]
 #![cfg(windows)]
 
-use libc::{c_void, c_ulong, c_long, c_ulonglong};
+use libc::{c_long, c_ulong, c_ulonglong, c_void};
 
 pub type DWORD = c_ulong;
 pub type LONG = c_long;
@@ -25,8 +25,7 @@ pub const EXCEPTION_UNWINDING: DWORD = 0x2;        // Unwind is in progress
 pub const EXCEPTION_EXIT_UNWIND: DWORD = 0x4;      // Exit unwind is in progress
 pub const EXCEPTION_TARGET_UNWIND: DWORD = 0x20;   // Target unwind in progress
 pub const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call
-pub const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING |
-                                    EXCEPTION_EXIT_UNWIND |
+pub const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND |
                                     EXCEPTION_TARGET_UNWIND |
                                     EXCEPTION_COLLIDED_UNWIND;
 
@@ -37,7 +36,7 @@ pub struct EXCEPTION_RECORD {
     pub ExceptionRecord: *mut EXCEPTION_RECORD,
     pub ExceptionAddress: LPVOID,
     pub NumberParameters: DWORD,
-    pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS]
+    pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS],
 }
 
 #[repr(C)]
@@ -75,7 +74,7 @@ pub enum EXCEPTION_DISPOSITION {
     ExceptionContinueExecution,
     ExceptionContinueSearch,
     ExceptionNestedException,
-    ExceptionCollidedUnwind
+    ExceptionCollidedUnwind,
 }
 pub use self::EXCEPTION_DISPOSITION::*;
 
@@ -93,6 +92,5 @@ extern "system" {
                        OriginalContext: *const CONTEXT,
                        HistoryTable: *const UNWIND_HISTORY_TABLE);
     #[unwind]
-    pub fn _CxxThrowException(pExceptionObject: *mut c_void,
-                              pThrowInfo: *mut u8);
+    pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8);
 }
index cd099c69005f3a1ece79fad054ef70c48b7bdfd4..5fba44a1c38f13e1f35de30498f5064a1b8fc9b5 100644 (file)
@@ -10,7 +10,7 @@
 
 //! The ChaCha random number generator.
 
-use {Rng, SeedableRng, Rand};
+use {Rand, Rng, SeedableRng};
 
 const KEY_WORDS: usize = 8; // 8 words for the 256-bit key
 const STATE_WORDS: usize = 16;
@@ -216,7 +216,8 @@ mod tests {
         let s = ::test::rng().gen_iter::<u32>().take(8).collect::<Vec<u32>>();
         let mut ra: ChaChaRng = SeedableRng::from_seed(&*s);
         let mut rb: ChaChaRng = SeedableRng::from_seed(&*s);
-        assert!(ra.gen_ascii_chars().take(100)
+        assert!(ra.gen_ascii_chars()
+                  .take(100)
                   .eq(rb.gen_ascii_chars().take(100)));
     }
 
@@ -225,7 +226,8 @@ mod tests {
         let seed: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
         let mut ra: ChaChaRng = SeedableRng::from_seed(seed);
         let mut rb: ChaChaRng = SeedableRng::from_seed(seed);
-        assert!(ra.gen_ascii_chars().take(100)
+        assert!(ra.gen_ascii_chars()
+                  .take(100)
                   .eq(rb.gen_ascii_chars().take(100)));
     }
 
index 12dbbfdb0ed4d9d07a0034121df13ff126bf9c4a..5a8558efc024437b82300161ec3133c147795a8a 100644 (file)
@@ -13,8 +13,8 @@
 #[cfg(not(test))] // only necessary for no_std
 use FloatMath;
 
-use {Rng, Rand};
-use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample};
+use {Rand, Rng};
+use distributions::{IndependentSample, Sample, ziggurat, ziggurat_tables};
 
 /// A wrapper around an `f64` to generate Exp(1) random numbers.
 ///
@@ -88,7 +88,7 @@ impl IndependentSample<f64> for Exp {
 
 #[cfg(test)]
 mod tests {
-    use distributions::{Sample, IndependentSample};
+    use distributions::{IndependentSample, Sample};
     use super::Exp;
 
     #[test]
index cf48823656044503ddf57af6bc2becaea7f2ee71..9ca13e85b533304f2f2c5c7bc5657da138556343 100644 (file)
@@ -16,9 +16,9 @@ use self::ChiSquaredRepr::*;
 #[cfg(not(test))] // only necessary for no_std
 use FloatMath;
 
-use {Rng, Open01};
+use {Open01, Rng};
 use super::normal::StandardNormal;
-use super::{IndependentSample, Sample, Exp};
+use super::{Exp, IndependentSample, Sample};
 
 /// The Gamma distribution `Gamma(shape, scale)` distribution.
 ///
@@ -291,8 +291,8 @@ impl IndependentSample<f64> for StudentT {
 
 #[cfg(test)]
 mod tests {
-    use distributions::{Sample, IndependentSample};
-    use super::{ChiSquared, StudentT, FisherF};
+    use distributions::{IndependentSample, Sample};
+    use super::{ChiSquared, FisherF, StudentT};
 
     #[test]
     fn test_chi_squared_one() {
index 2557d39c550f5c838c10c500b22c6dd5bfb605b3..36c9f783ff5e03aef4707f3af5f4eb661e14b09f 100644 (file)
@@ -22,11 +22,11 @@ use core::num::Float;
 
 use core::marker::PhantomData;
 
-use {Rng, Rand};
+use {Rand, Rng};
 
 pub use self::range::Range;
-pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT};
-pub use self::normal::{Normal, LogNormal};
+pub use self::gamma::{ChiSquared, FisherF, Gamma, StudentT};
+pub use self::normal::{LogNormal, Normal};
 pub use self::exponential::Exp;
 
 pub mod range;
@@ -266,8 +266,8 @@ fn ziggurat<R: Rng, P, Z>(rng: &mut R,
 
 #[cfg(test)]
 mod tests {
-    use {Rng, Rand};
-    use super::{RandSample, WeightedChoice, Weighted, Sample, IndependentSample};
+    use {Rand, Rng};
+    use super::{IndependentSample, RandSample, Sample, Weighted, WeightedChoice};
 
     #[derive(PartialEq, Debug)]
     struct ConstRand(usize);
index 86840c568e018f41acbfe33e1eac93871596b530..811d5b14c71129c40bd51f580e53c02d463fcfd0 100644 (file)
@@ -13,8 +13,8 @@
 #[cfg(not(test))] // only necessary for no_std
 use FloatMath;
 
-use {Rng, Rand, Open01};
-use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample};
+use {Open01, Rand, Rng};
+use distributions::{IndependentSample, Sample, ziggurat, ziggurat_tables};
 
 /// A wrapper around an `f64` to generate N(0, 1) random numbers
 /// (a.k.a.  a standard normal, or Gaussian).
@@ -145,8 +145,8 @@ impl IndependentSample<f64> for LogNormal {
 
 #[cfg(test)]
 mod tests {
-    use distributions::{Sample, IndependentSample};
-    use super::{Normal, LogNormal};
+    use distributions::{IndependentSample, Sample};
+    use super::{LogNormal, Normal};
 
     #[test]
     fn test_normal() {
index f94ef059dae8fc2a5f6fe5f57d0069e30825cd05..ba8554a979b899309a3ebae5f4a1a869a1ddcf64 100644 (file)
@@ -14,7 +14,7 @@
 
 use core::marker::Sized;
 use Rng;
-use distributions::{Sample, IndependentSample};
+use distributions::{IndependentSample, Sample};
 
 /// Sample values uniformly between two bounds.
 ///
@@ -148,7 +148,7 @@ float_impl! { f64 }
 
 #[cfg(test)]
 mod tests {
-    use distributions::{Sample, IndependentSample};
+    use distributions::{IndependentSample, Sample};
     use super::Range;
 
     #[should_panic]
index 28eff87bde3b762246e11abec3c6039d93c51b21..e8cc7b5cc2dacc76448b4bda3a492c9fbe7e6b41 100644 (file)
@@ -16,7 +16,7 @@ use core::slice;
 use core::iter::repeat;
 use core::num::Wrapping as w;
 
-use {Rng, SeedableRng, Rand};
+use {Rand, Rng, SeedableRng};
 
 type w32 = w<u32>;
 type w64 = w<u64>;
@@ -591,14 +591,15 @@ mod tests {
     use std::prelude::v1::*;
 
     use {Rng, SeedableRng};
-    use super::{IsaacRng, Isaac64Rng};
+    use super::{Isaac64Rng, IsaacRng};
 
     #[test]
     fn test_rng_32_rand_seeded() {
         let s = ::test::rng().gen_iter::<u32>().take(256).collect::<Vec<u32>>();
         let mut ra: IsaacRng = SeedableRng::from_seed(&s[..]);
         let mut rb: IsaacRng = SeedableRng::from_seed(&s[..]);
-        assert!(ra.gen_ascii_chars().take(100)
+        assert!(ra.gen_ascii_chars()
+                  .take(100)
                   .eq(rb.gen_ascii_chars().take(100)));
     }
     #[test]
@@ -606,7 +607,8 @@ mod tests {
         let s = ::test::rng().gen_iter::<u64>().take(256).collect::<Vec<u64>>();
         let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]);
         let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]);
-        assert!(ra.gen_ascii_chars().take(100)
+        assert!(ra.gen_ascii_chars()
+                  .take(100)
                   .eq(rb.gen_ascii_chars().take(100)));
     }
 
@@ -615,7 +617,8 @@ mod tests {
         let seed: &[_] = &[1, 23, 456, 7890, 12345];
         let mut ra: IsaacRng = SeedableRng::from_seed(seed);
         let mut rb: IsaacRng = SeedableRng::from_seed(seed);
-        assert!(ra.gen_ascii_chars().take(100)
+        assert!(ra.gen_ascii_chars()
+                  .take(100)
                   .eq(rb.gen_ascii_chars().take(100)));
     }
     #[test]
@@ -623,7 +626,8 @@ mod tests {
         let seed: &[_] = &[1, 23, 456, 7890, 12345];
         let mut ra: Isaac64Rng = SeedableRng::from_seed(seed);
         let mut rb: Isaac64Rng = SeedableRng::from_seed(seed);
-        assert!(ra.gen_ascii_chars().take(100)
+        assert!(ra.gen_ascii_chars()
+                  .take(100)
                   .eq(rb.gen_ascii_chars().take(100)));
     }
 
index d8517fb4c5714e3909e19f555930f43d97dd8eb0..c31a0ed53207d0385a5c831cb795206a13e76e15 100644 (file)
@@ -47,10 +47,10 @@ use core::f64;
 use core::intrinsics;
 use core::marker::PhantomData;
 
-pub use isaac::{IsaacRng, Isaac64Rng};
+pub use isaac::{Isaac64Rng, IsaacRng};
 pub use chacha::ChaChaRng;
 
-use distributions::{Range, IndependentSample};
+use distributions::{IndependentSample, Range};
 use distributions::range::SampleRange;
 
 #[cfg(test)]
@@ -67,7 +67,7 @@ mod rand_impls;
 // depend on libstd.  This will go away when librand is integrated
 // into libstd.
 #[doc(hidden)]
-trait FloatMath : Sized {
+trait FloatMath: Sized {
     fn exp(self) -> Self;
     fn ln(self) -> Self;
     fn sqrt(self) -> Self;
@@ -102,14 +102,14 @@ impl FloatMath for f64 {
 
 /// A type that can be randomly generated using an `Rng`.
 #[doc(hidden)]
-pub trait Rand : Sized {
+pub trait Rand: Sized {
     /// Generates a random instance of this type using the specified source of
     /// randomness.
     fn rand<R: Rng>(rng: &mut R) -> Self;
 }
 
 /// A random number generator.
-pub trait Rng : Sized {
+pub trait Rng: Sized {
     /// Return the next random u32.
     ///
     /// This rarely needs to be called directly, prefer `r.gen()` to
index 1185ad25485bd32ca3efbe67dd32871ed1c7025d..41ad089ecd235d0f753de2b6da8d50043ac4463b 100644 (file)
@@ -144,9 +144,8 @@ impl Rand for char {
             // Rejection sampling. About 0.2% of numbers with at most
             // 21-bits are invalid codepoints (surrogates), so this
             // will succeed first go almost every time.
-            match char::from_u32(rng.next_u32() & CHAR_MASK) {
-                Some(c) => return c,
-                None => {}
+            if let Some(c) = char::from_u32(rng.next_u32() & CHAR_MASK) {
+                return c;
             }
         }
     }
index db5e0213726d90951538ed28540ad5deed47a9fe..c7d560eb1f8e253ebd0bcc1b7c8431954f93e447 100644 (file)
@@ -83,8 +83,8 @@ impl<S, R: SeedableRng<S>, Rsdr: Reseeder<R> + Default>
         self.bytes_generated = 0;
     }
 
-    /// Create a new `ReseedingRng` from the given reseeder and
-    /// seed. This uses a default value for `generation_threshold`.
+/// Create a new `ReseedingRng` from the given reseeder and
+/// seed. This uses a default value for `generation_threshold`.
     fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng<R, Rsdr> {
         ReseedingRng {
             rng: SeedableRng::from_seed(seed),
@@ -122,8 +122,8 @@ impl Default for ReseedWithDefault {
 mod tests {
     use std::prelude::v1::*;
 
-    use super::{ReseedingRng, ReseedWithDefault};
-    use {SeedableRng, Rng};
+    use super::{ReseedWithDefault, ReseedingRng};
+    use {Rng, SeedableRng};
 
     struct Counter {
         i: u32,
@@ -166,7 +166,8 @@ mod tests {
     fn test_rng_seeded() {
         let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2));
         let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2));
-        assert!(ra.gen_ascii_chars().take(100)
+        assert!(ra.gen_ascii_chars()
+                  .take(100)
                   .eq(rb.gen_ascii_chars().take(100)));
     }
 
index 9291227a734f78b7978aedfe79368c5ab1402257..aaef8e8423cbec1bce8d247b33c1aa732cadf0f8 100644 (file)
@@ -19,6 +19,8 @@ rustc_back = { path = "../librustc_back" }
 rustc_bitflags = { path = "../librustc_bitflags" }
 rustc_const_math = { path = "../librustc_const_math" }
 rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
 rustc_llvm = { path = "../librustc_llvm" }
 serialize = { path = "../libserialize" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
index 76699f13959eacc4f251d5176c8080faa9c34b79..18ea17f48162f1b66c168c00d3246d8b9b7712b3 100644 (file)
@@ -99,8 +99,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
 
     fn pat(&mut self, pat: &hir::Pat, pred: CFGIndex) -> CFGIndex {
         match pat.node {
-            PatKind::Ident(_, _, None) |
-            PatKind::TupleStruct(_, None) |
+            PatKind::Binding(_, _, None) |
             PatKind::Path(..) |
             PatKind::QPath(..) |
             PatKind::Lit(..) |
@@ -111,13 +110,13 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
 
             PatKind::Box(ref subpat) |
             PatKind::Ref(ref subpat, _) |
-            PatKind::Ident(_, _, Some(ref subpat)) => {
+            PatKind::Binding(_, _, Some(ref subpat)) => {
                 let subpat_exit = self.pat(&subpat, pred);
                 self.add_ast_node(pat.id, &[subpat_exit])
             }
 
-            PatKind::TupleStruct(_, Some(ref subpats)) |
-            PatKind::Tup(ref subpats) => {
+            PatKind::TupleStruct(_, ref subpats, _) |
+            PatKind::Tuple(ref subpats, _) => {
                 let pats_exit = self.pats_all(subpats.iter(), pred);
                 self.add_ast_node(pat.id, &[pats_exit])
             }
@@ -457,8 +456,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
                     // Visit the guard expression
                     let guard_exit = self.expr(&guard, guard_start);
 
-                    let this_has_bindings = pat_util::pat_contains_bindings_or_wild(
-                        &self.tcx.def_map.borrow(), &pat);
+                    let this_has_bindings = pat_util::pat_contains_bindings_or_wild(&pat);
 
                     // If both this pattern and the previous pattern
                     // were free of bindings, they must consist only
@@ -576,8 +574,8 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
             return *self.loop_scopes.last().unwrap();
         }
 
-        match self.tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def()) {
-            Some(Def::Label(loop_id)) => {
+        match self.tcx.expect_def(expr.id) {
+            Def::Label(loop_id) => {
                 for l in &self.loop_scopes {
                     if l.loop_id == loop_id {
                         return *l;
index 84c84a7ed57a2656af76de3a0a0428d53c0d10f3..73b96651b05e27826c031d55267cff16415a7bf1 100644 (file)
 
 use std::fmt::Debug;
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+macro_rules! try_opt {
+    ($e:expr) => (
+        match $e {
+            Some(r) => r,
+            None => return None,
+        }
+    )
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub enum DepNode<D: Clone + Debug> {
     // The `D` type is "how definitions are identified".
     // During compilation, it is always `DefId`, but when serializing
@@ -116,7 +125,7 @@ pub enum DepNode<D: Clone + Debug> {
     // which would yield an overly conservative dep-graph.
     TraitItems(D),
     ReprHints(D),
-    TraitSelect(D),
+    TraitSelect(D, Vec<D>),
 }
 
 impl<D: Clone + Debug> DepNode<D> {
@@ -212,7 +221,11 @@ impl<D: Clone + Debug> DepNode<D> {
             TraitImpls(ref d) => op(d).map(TraitImpls),
             TraitItems(ref d) => op(d).map(TraitItems),
             ReprHints(ref d) => op(d).map(ReprHints),
-            TraitSelect(ref d) => op(d).map(TraitSelect),
+            TraitSelect(ref d, ref type_ds) => {
+                let d = try_opt!(op(d));
+                let type_ds = try_opt!(type_ds.iter().map(|d| op(d)).collect());
+                Some(TraitSelect(d, type_ds))
+            }
         }
     }
 }
index 93248edb197c625e9f4fec25559171ff4245ece5..7a780c1d4ae2478bc6d80d2e9c932bb0063e4b1e 100644 (file)
@@ -47,26 +47,26 @@ impl<D: Clone + Debug + Hash + Eq> DepGraphQuery<D> {
         self.indices.contains_key(&node)
     }
 
-    pub fn nodes(&self) -> Vec<DepNode<D>> {
+    pub fn nodes(&self) -> Vec<&DepNode<D>> {
         self.graph.all_nodes()
                   .iter()
-                  .map(|n| n.data.clone())
+                  .map(|n| &n.data)
                   .collect()
     }
 
-    pub fn edges(&self) -> Vec<(DepNode<D>,DepNode<D>)> {
+    pub fn edges(&self) -> Vec<(&DepNode<D>,&DepNode<D>)> {
         self.graph.all_edges()
                   .iter()
                   .map(|edge| (edge.source(), edge.target()))
-                  .map(|(s, t)| (self.graph.node_data(s).clone(),
-                                 self.graph.node_data(t).clone()))
+                  .map(|(s, t)| (self.graph.node_data(s),
+                                 self.graph.node_data(t)))
                   .collect()
     }
 
-    fn reachable_nodes(&self, node: DepNode<D>, direction: Direction) -> Vec<DepNode<D>> {
-        if let Some(&index) = self.indices.get(&node) {
+    fn reachable_nodes(&self, node: &DepNode<D>, direction: Direction) -> Vec<&DepNode<D>> {
+        if let Some(&index) = self.indices.get(node) {
             self.graph.depth_traverse(index, direction)
-                      .map(|s| self.graph.node_data(s).clone())
+                      .map(|s| self.graph.node_data(s))
                       .collect()
         } else {
             vec![]
@@ -75,20 +75,20 @@ impl<D: Clone + Debug + Hash + Eq> DepGraphQuery<D> {
 
     /// All nodes reachable from `node`. In other words, things that
     /// will have to be recomputed if `node` changes.
-    pub fn transitive_successors(&self, node: DepNode<D>) -> Vec<DepNode<D>> {
+    pub fn transitive_successors(&self, node: &DepNode<D>) -> Vec<&DepNode<D>> {
         self.reachable_nodes(node, OUTGOING)
     }
 
     /// All nodes that can reach `node`.
-    pub fn transitive_predecessors(&self, node: DepNode<D>) -> Vec<DepNode<D>> {
+    pub fn transitive_predecessors(&self, node: &DepNode<D>) -> Vec<&DepNode<D>> {
         self.reachable_nodes(node, INCOMING)
     }
 
     /// Just the outgoing edges from `node`.
-    pub fn immediate_successors(&self, node: DepNode<D>) -> Vec<DepNode<D>> {
+    pub fn immediate_successors(&self, node: &DepNode<D>) -> Vec<&DepNode<D>> {
         if let Some(&index) = self.indices.get(&node) {
             self.graph.successor_nodes(index)
-                      .map(|s| self.graph.node_data(s).clone())
+                      .map(|s| self.graph.node_data(s))
                       .collect()
         } else {
             vec![]
index 13151d169fc3f78843405d5a1829ec9d2823d985..c43d493d176757b023c0b6a73435bf4b999a4d94 100644 (file)
@@ -14,20 +14,20 @@ use super::thread::{DepGraphThreadData, DepMessage};
 
 pub struct DepTask<'graph> {
     data: &'graph DepGraphThreadData,
-    key: DepNode<DefId>,
+    key: Option<DepNode<DefId>>,
 }
 
 impl<'graph> DepTask<'graph> {
     pub fn new(data: &'graph DepGraphThreadData, key: DepNode<DefId>)
                -> DepTask<'graph> {
-        data.enqueue(DepMessage::PushTask(key));
-        DepTask { data: data, key: key }
+        data.enqueue(DepMessage::PushTask(key.clone()));
+        DepTask { data: data, key: Some(key) }
     }
 }
 
 impl<'graph> Drop for DepTask<'graph> {
     fn drop(&mut self) {
-        self.data.enqueue(DepMessage::PopTask(self.key));
+        self.data.enqueue(DepMessage::PopTask(self.key.take().unwrap()));
     }
 }
 
index 70d0a4e315c3743501a9e2a1afc43c9baf5b6449..4e16fae187070ee13880b1a8c189cf91c724d409 100644 (file)
@@ -118,8 +118,6 @@ impl DepGraphThreadData {
     /// the buffer is full, this may swap.)
     #[inline]
     pub fn enqueue(&self, message: DepMessage) {
-        debug!("enqueue: {:?} tasks_pushed={}", message, self.tasks_pushed.get());
-
         // Regardless of whether dep graph construction is enabled, we
         // still want to check that we always have a valid task on the
         // stack when a read/write/etc event occurs.
index 9133b4d22eeb2ec0693757d90045b6f8e0140817..5dd71db2f1832870b8e87171499b772c32acf02e 100644 (file)
@@ -39,7 +39,7 @@ pub fn visit_all_items_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         fn visit_item(&mut self, i: &'tcx hir::Item) {
             let item_def_id = self.tcx.map.local_def_id(i.id);
             let task_id = (self.dep_node_fn)(item_def_id);
-            let _task = self.tcx.dep_graph.in_task(task_id);
+            let _task = self.tcx.dep_graph.in_task(task_id.clone());
             debug!("Started task {:?}", task_id);
             self.tcx.dep_graph.read(DepNode::Hir(item_def_id));
             self.visitor.visit_item(i);
index c64f0ddac50d0f2bd9a2c507d498165e64e7a43a..9040e4bf8db5f23661174bb68a27822e63aa5d88 100644 (file)
@@ -20,6 +20,8 @@ remainder of a zero divisor) in a static or constant expression. Erroneous
 code example:
 
 ```compile_fail
+#[deny(const_err)]
+
 const X: i32 = 42 / 0;
 // error: attempted to divide by zero in a constant expression
 ```
@@ -66,7 +68,7 @@ this restriction.
 
 This happens when a trait has a method like the following:
 
-```compile_fail
+```
 trait Trait {
     fn foo(&self) -> Self;
 }
@@ -296,7 +298,7 @@ the pointer, the size of the type would need to be unbounded.
 
 Consider the following erroneous definition of a type for a list of bytes:
 
-```compile_fail
+```compile_fail,E0072
 // error, invalid recursive struct type
 struct ListNode {
     head: u8,
@@ -329,7 +331,7 @@ E0109: r##"
 You tried to give a type parameter to a type which doesn't need it. Erroneous
 code example:
 
-```compile_fail
+```compile_fail,E0109
 type X = u32<i32>; // error: type parameters are not allowed on this type
 ```
 
@@ -350,7 +352,7 @@ E0110: r##"
 You tried to give a lifetime parameter to a type which doesn't need it.
 Erroneous code example:
 
-```compile_fail
+```compile_fail,E0110
 type X = u32<'static>; // error: lifetime parameters are not allowed on
                        //        this type
 ```
@@ -364,6 +366,18 @@ type X = u32; // ok!
 "##,
 
 E0133: r##"
+Unsafe code was used outside of an unsafe function or block.
+
+Erroneous code example:
+
+```compile_fail,E0133
+unsafe fn f() { return; } // This is the unsafe code
+
+fn main() {
+    f(); // error: call to unsafe function requires unsafe function or block
+}
+```
+
 Using unsafe functionality is potentially dangerous and disallowed by safety
 checks. Examples:
 
@@ -378,7 +392,7 @@ unsafe instructions with an `unsafe` block. For instance:
 unsafe fn f() { return; }
 
 fn main() {
-    unsafe { f(); }
+    unsafe { f(); } // ok!
 }
 ```
 
@@ -392,19 +406,61 @@ function `main()`. If there are multiple such functions, please rename one.
 "##,
 
 E0137: r##"
+More than one function was declared with the `#[main]` attribute.
+
+Erroneous code example:
+
+```compile_fail,E0137
+#![feature(main)]
+
+#[main]
+fn foo() {}
+
+#[main]
+fn f() {} // error: multiple functions with a #[main] attribute
+```
+
 This error indicates that the compiler found multiple functions with the
 `#[main]` attribute. This is an error because there must be a unique entry
-point into a Rust program.
+point into a Rust program. Example:
+
+```
+#![feature(main)]
+
+#[main]
+fn f() {} // ok!
+```
 "##,
 
 E0138: r##"
+More than one function was declared with the `#[start]` attribute.
+
+Erroneous code example:
+
+```compile_fail,E0138
+#![feature(start)]
+
+#[start]
+fn foo(argc: isize, argv: *const *const u8) -> isize {}
+
+#[start]
+fn f(argc: isize, argv: *const *const u8) -> isize {}
+// error: multiple 'start' functions
+```
+
 This error indicates that the compiler found multiple functions with the
 `#[start]` attribute. This is an error because there must be a unique entry
-point into a Rust program.
+point into a Rust program. Example:
+
+```
+#![feature(start)]
+
+#[start]
+fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } // ok!
+```
 "##,
 
-// FIXME link this to the relevant turpl chapters for instilling fear of the
-//       transmute gods in the user
+// isn't thrown anymore
 E0139: r##"
 There are various restrictions on transmuting between types in Rust; for example
 types being transmuted must have the same size. To apply all these restrictions,
@@ -413,11 +469,13 @@ parameters are involved, this cannot always be done.
 
 So, for example, the following is not allowed:
 
-```compile_fail
+```
+use std::mem::transmute;
+
 struct Foo<T>(Vec<T>);
 
 fn foo<T>(x: Vec<T>) {
-    // we are transmuting between Vec<T> and Foo<T> here
+    // we are transmuting between Vec<T> and Foo<F> here
     let y: Foo<T> = unsafe { transmute(x) };
     // do something with y
 }
@@ -481,6 +539,17 @@ call to `mem::forget(v)` in case you want to avoid destructors being called.
 "##,
 
 E0152: r##"
+A lang item was redefined.
+
+Erroneous code example:
+
+```compile_fail,E0152
+#![feature(lang_items)]
+
+#[lang = "panic_fmt"]
+struct Foo; // error: duplicate lang item found: `panic_fmt`
+```
+
 Lang items are already implemented in the standard library. Unless you are
 writing a free-standing application (e.g. a kernel), you do not need to provide
 them yourself.
@@ -499,7 +568,7 @@ E0229: r##"
 An associated type binding was done outside of the type parameter declaration
 and `where` clause. Erroneous code example:
 
-```compile_fail
+```compile_fail,E0229
 pub trait Foo {
     type A;
     fn boo(&self) -> <Self as Foo>::A;
@@ -536,7 +605,7 @@ used.
 
 These two examples illustrate the problem:
 
-```compile_fail
+```compile_fail,E0261
 // error, use of undeclared lifetime name `'a`
 fn foo(x: &'a str) { }
 
@@ -562,7 +631,7 @@ Declaring certain lifetime names in parameters is disallowed. For example,
 because the `'static` lifetime is a special built-in lifetime name denoting
 the lifetime of the entire program, this is an error:
 
-```compile_fail
+```compile_fail,E0262
 // error, invalid lifetime parameter name `'static`
 fn foo<'static>(x: &'static str) { }
 ```
@@ -572,7 +641,7 @@ E0263: r##"
 A lifetime name cannot be declared more than once in the same scope. For
 example:
 
-```compile_fail
+```compile_fail,E0263
 // error, lifetime name `'a` declared twice in the same scope
 fn foo<'a, 'b, 'a>(x: &'a str, y: &'b str) { }
 ```
@@ -581,7 +650,7 @@ fn foo<'a, 'b, 'a>(x: &'a str, y: &'b str) { }
 E0264: r##"
 An unknown external lang item was used. Erroneous code example:
 
-```compile_fail
+```compile_fail,E0264
 #![feature(lang_items)]
 
 extern "C" {
@@ -604,48 +673,35 @@ extern "C" {
 "##,
 
 E0269: r##"
-Functions must eventually return a value of their return type. For example, in
-the following function:
+A returned value was expected but not all control paths return one.
 
-```compile_fail
-fn foo(x: u8) -> u8 {
-    if x > 0 {
-        x // alternatively, `return x`
-    }
-    // nothing here
+Erroneous code example:
+
+```compile_fail,E0269
+fn abracada_FAIL() -> String {
+    "this won't work".to_string();
+    // error: not all control paths return a value
 }
 ```
 
-If the condition is true, the value `x` is returned, but if the condition is
-false, control exits the `if` block and reaches a place where nothing is being
-returned. All possible control paths must eventually return a `u8`, which is not
-happening here.
-
-An easy fix for this in a complicated function is to specify a default return
-value, if possible:
+In the previous code, the function is supposed to return a `String`, however,
+the code returns nothing (because of the ';'). Another erroneous code would be:
 
-```ignore
-fn foo(x: u8) -> u8 {
-    if x > 0 {
-        x // alternatively, `return x`
+```compile_fail
+fn abracada_FAIL(b: bool) -> u32 {
+    if b {
+        0
+    } else {
+        "a" // It fails because an `u32` was expected and something else is
+            // returned.
     }
-    // lots of other if branches
-    0 // return 0 if all else fails
 }
 ```
 
 It is advisable to find out what the unhandled cases are and check for them,
 returning an appropriate value or panicking if necessary. Check if you need
-to remove a semicolon from the last expression, like in this case:
-
-```ignore
-fn foo(x: u8) -> u8 {
-    inner(2*x + 1);
-}
-```
-
-The semicolon discards the return value of `inner`, instead of returning
-it from `foo`.
+to remove a semicolon from the last expression, like in the first erroneous
+code example.
 "##,
 
 E0270: r##"
@@ -738,7 +794,7 @@ Examples follow.
 
 Here is a basic example:
 
-```compile_fail
+```compile_fail,E0271
 trait Trait { type AssociatedType; }
 
 fn foo<T>(t: T) where T: Trait<AssociatedType=u32> {
@@ -879,6 +935,8 @@ position that needs that trait. For example, when the following code is
 compiled:
 
 ```compile_fail
+#![feature(on_unimplemented)]
+
 fn foo<T: Index<u8>>(x: T){}
 
 #[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
@@ -909,6 +967,8 @@ position that needs that trait. For example, when the following code is
 compiled:
 
 ```compile_fail
+#![feature(on_unimplemented)]
+
 fn foo<T: Index<u8>>(x: T){}
 
 #[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
@@ -937,6 +997,8 @@ position that needs that trait. For example, when the following code is
 compiled:
 
 ```compile_fail
+#![feature(on_unimplemented)]
+
 fn foo<T: Index<u8>>(x: T){}
 
 #[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
@@ -960,7 +1022,7 @@ recursion in resolving some type bounds.
 
 For example, in the following code:
 
-```compile_fail
+```compile_fail,E0275
 trait Foo {}
 
 struct Bar<T>(T);
@@ -980,7 +1042,7 @@ E0276: r##"
 This error occurs when a bound in an implementation of a trait does not match
 the bounds specified in the original trait. For example:
 
-```compile_fail
+```compile_fail,E0276
 trait Foo {
     fn foo<T>(x: T);
 }
@@ -1002,7 +1064,7 @@ E0277: r##"
 You tried to use a type which doesn't implement some trait in a place which
 expected that trait. Erroneous code example:
 
-```compile_fail
+```compile_fail,E0277
 // here we declare the Foo trait with a bar method
 trait Foo {
     fn bar(&self);
@@ -1044,7 +1106,8 @@ fn main() {
 ```
 
 Or in a generic context, an erroneous code example would look like:
-```compile_fail
+
+```compile_fail,E0277
 fn some_func<T>(foo: T) {
     println!("{:?}", foo); // error: the trait `core::fmt::Debug` is not
                            //        implemented for the type `T`
@@ -1062,6 +1125,7 @@ we only call it with a parameter that does implement `Debug`, the compiler
 still rejects the function: It must work with all possible input types. In
 order to make this example compile, we need to restrict the generic type we're
 accepting:
+
 ```
 use std::fmt;
 
@@ -1078,11 +1142,10 @@ fn main() {
     // struct WithoutDebug;
     // some_func(WithoutDebug);
 }
+```
 
 Rust only looks at the signature of the called function, as such it must
 already specify all requirements that will be used for every type parameter.
-```
-
 "##,
 
 E0281: r##"
@@ -1090,7 +1153,7 @@ You tried to supply a type which doesn't implement some trait in a location
 which expected that trait. This error typically occurs when working with
 `Fn`-based types. Erroneous code example:
 
-```compile_fail
+```compile_fail,E0281
 fn foo<F: Fn()>(x: F) { }
 
 fn main() {
@@ -1116,7 +1179,7 @@ parameter with a `FromIterator` bound, which for a `char` iterator is
 implemented by `Vec` and `String` among others. Consider the following snippet
 that reverses the characters of a string:
 
-```compile_fail
+```compile_fail,E0282
 let x = "hello".chars().rev().collect();
 ```
 
@@ -1153,7 +1216,7 @@ occur when a type parameter of a struct or trait cannot be inferred. In that
 case it is not always possible to use a type annotation, because all candidates
 have the same return type. For instance:
 
-```compile_fail
+```compile_fail,E0282
 struct Foo<T> {
     num: T,
 }
@@ -1179,7 +1242,7 @@ to unambiguously choose an implementation.
 
 For example:
 
-```compile_fail
+```compile_fail,E0283
 trait Generator {
     fn create() -> u32;
 }
@@ -1227,10 +1290,22 @@ fn main() {
 
 E0296: r##"
 This error indicates that the given recursion limit could not be parsed. Ensure
-that the value provided is a positive integer between quotes, like so:
+that the value provided is a positive integer between quotes.
+
+Erroneous code example:
+
+```compile_fail,E0296
+#![recursion_limit]
+
+fn main() {}
+```
+
+And a working example:
 
 ```
 #![recursion_limit="1000"]
+
+fn main() {}
 ```
 "##,
 
@@ -1243,7 +1318,7 @@ variable.
 
 For example:
 
-```compile_fail
+```compile_fail,E0308
 let x: i32 = "I am not a number!";
 //     ~~~   ~~~~~~~~~~~~~~~~~~~~
 //      |             |
@@ -1256,7 +1331,7 @@ let x: i32 = "I am not a number!";
 Another situation in which this occurs is when you attempt to use the `try!`
 macro inside a function that does not return a `Result<T, E>`:
 
-```compile_fail
+```compile_fail,E0308
 use std::fs::File;
 
 fn main() {
@@ -1284,7 +1359,7 @@ how long the data stored within them is guaranteed to be live. This lifetime
 must be as long as the data needs to be alive, and missing the constraint that
 denotes this will cause this error.
 
-```compile_fail
+```compile_fail,E0309
 // This won't compile because T is not constrained, meaning the data
 // stored in it is not guaranteed to last as long as the reference
 struct Foo<'a, T> {
@@ -1307,12 +1382,13 @@ how long the data stored within them is guaranteed to be live. This lifetime
 must be as long as the data needs to be alive, and missing the constraint that
 denotes this will cause this error.
 
-```compile_fail
+```compile_fail,E0310
 // This won't compile because T is not constrained to the static lifetime
 // the reference needs
 struct Foo<T> {
     foo: &'static T
 }
+```
 
 This will compile, because it has the constraint on the type parameter:
 
@@ -1360,7 +1436,7 @@ references (with a maximum lifetime of `'a`).
 E0452: r##"
 An invalid lint attribute has been given. Erroneous code example:
 
-```compile_fail
+```compile_fail,E0452
 #![allow(foo = "")] // error: malformed lint attribute
 ```
 
@@ -1374,10 +1450,55 @@ lint name). Ensure the attribute is of this form:
 ```
 "##,
 
+E0453: r##"
+A lint check attribute was overruled by a `forbid` directive set as an
+attribute on an enclosing scope, or on the command line with the `-F` option.
+
+Example of erroneous code:
+
+```compile_fail,E0453
+#![forbid(non_snake_case)]
+
+#[allow(non_snake_case)]
+fn main() {
+    let MyNumber = 2; // error: allow(non_snake_case) overruled by outer
+                      //        forbid(non_snake_case)
+}
+```
+
+The `forbid` lint setting, like `deny`, turns the corresponding compiler
+warning into a hard error. Unlike `deny`, `forbid` prevents itself from being
+overridden by inner attributes.
+
+If you're sure you want to override the lint check, you can change `forbid` to
+`deny` (or use `-D` instead of `-F` if the `forbid` setting was given as a
+command-line option) to allow the inner lint check attribute:
+
+```
+#![deny(non_snake_case)]
+
+#[allow(non_snake_case)]
+fn main() {
+    let MyNumber = 2; // ok!
+}
+```
+
+Otherwise, edit the code to pass the lint check, and remove the overruled
+attribute:
+
+```
+#![forbid(non_snake_case)]
+
+fn main() {
+    let my_number = 2;
+}
+```
+"##,
+
 E0496: r##"
 A lifetime name is shadowing another lifetime name. Erroneous code example:
 
-```compile_fail
+```compile_fail,E0496
 struct Foo<'a> {
     a: &'a i32,
 }
@@ -1424,7 +1545,7 @@ E0512: r##"
 Transmute with two differently sized types was attempted. Erroneous code
 example:
 
-```compile_fail
+```compile_fail,E0512
 fn takes_u8(_: u8) {}
 
 fn main() {
@@ -1452,7 +1573,7 @@ unsupported item.
 
 Examples of erroneous code:
 
-```compile_fail
+```compile_fail,E0517
 #[repr(C)]
 type Foo = u8;
 
@@ -1500,7 +1621,7 @@ on something other than a function or method.
 
 Examples of erroneous code:
 
-```compile_fail
+```compile_fail,E0518
 #[inline(always)]
 struct Foo;
 
@@ -1527,7 +1648,7 @@ how the compiler behaves, as well as special functions that may be automatically
 invoked (such as the handler for out-of-bounds accesses when indexing a slice).
 Erroneous code example:
 
-```compile_fail
+```compile_fail,E0522
 #![feature(lang_items)]
 
 #[lang = "cookie"]
@@ -1558,7 +1679,6 @@ register_diagnostics! {
     E0314, // closure outlives stack frame
     E0315, // cannot invoke closure outside of its lifetime
     E0316, // nested quantification of lifetimes
-    E0453, // overruled by outer forbid
     E0473, // dereference of reference outside its lifetime
     E0474, // captured variable `..` does not outlive the enclosing closure
     E0475, // index of slice outside its lifetime
@@ -1579,5 +1699,5 @@ register_diagnostics! {
     E0490, // a value of type `..` is borrowed for too long
     E0491, // in type `..`, reference has a longer lifetime than the data it...
     E0495, // cannot infer an appropriate lifetime due to conflicting requirements
-    E0525, // expected a closure that implements `..` but this closure only implements `..`
+    E0525  // expected a closure that implements `..` but this closure only implements `..`
 }
index 085acc198d16adcdbccd2e1d94ff24b0bd75dc19..a1c04dfcab5e66be6616e59a307ff81eaa5fbff4 100644 (file)
@@ -95,7 +95,7 @@ impl<'a> CheckAttrVisitor<'a> {
     }
 }
 
-impl<'a, 'v> Visitor<'v> for CheckAttrVisitor<'a> {
+impl<'a> Visitor for CheckAttrVisitor<'a> {
     fn visit_item(&mut self, item: &ast::Item) {
         let target = Target::from_item(item);
         for attr in &item.attrs {
index a056ba588b807998d4675e4fe18156b1c0cb7a42..72261c473e5c5d6a252733f988b43956f681ef78 100644 (file)
@@ -67,6 +67,10 @@ pub struct PathResolution {
 }
 
 impl PathResolution {
+    pub fn new(def: Def) -> PathResolution {
+        PathResolution { base_def: def, depth: 0 }
+    }
+
     /// Get the definition, if fully resolved, otherwise panic.
     pub fn full_def(&self) -> Def {
         if self.depth != 0 {
@@ -75,17 +79,11 @@ impl PathResolution {
         self.base_def
     }
 
-    /// Get the DefId, if fully resolved, otherwise panic.
-    pub fn def_id(&self) -> DefId {
-        self.full_def().def_id()
-    }
-
-    pub fn new(base_def: Def,
-               depth: usize)
-               -> PathResolution {
-        PathResolution {
-            base_def: base_def,
-            depth: depth,
+    pub fn kind_name(&self) -> &'static str {
+        if self.depth != 0 {
+            "associated item"
+        } else {
+            self.base_def.kind_name()
         }
     }
 }
@@ -161,8 +159,8 @@ impl Def {
             Def::Struct(..) => "struct",
             Def::Trait(..) => "trait",
             Def::Method(..) => "method",
-            Def::Const(..) => "const",
-            Def::AssociatedConst(..) => "associated const",
+            Def::Const(..) => "constant",
+            Def::AssociatedConst(..) => "associated constant",
             Def::TyParam(..) => "type parameter",
             Def::PrimTy(..) => "builtin type",
             Def::Local(..) => "local variable",
index a91d16f25a2b89a484966ced3a62a6b06f2d550e..78fd2bbbe0d2590e033ea4d87da820c69aad227f 100644 (file)
@@ -14,9 +14,9 @@
 use hir::*;
 use syntax::ast::{Name, NodeId, DUMMY_NODE_ID, Attribute, Attribute_, MetaItem};
 use syntax::ast::MetaItemKind;
-use syntax::attr::ThinAttributesExt;
 use hir;
-use syntax::codemap::{respan, Span, Spanned};
+use syntax_pos::Span;
+use syntax::codemap::{respan, Spanned};
 use syntax::ptr::P;
 use syntax::parse::token::keywords;
 use syntax::util::move_map::MoveMap;
@@ -292,8 +292,11 @@ pub fn noop_fold_view_path<T: Folder>(view_path: P<ViewPath>, fld: &mut T) -> P<
     })
 }
 
-pub fn fold_attrs<T: Folder>(attrs: HirVec<Attribute>, fld: &mut T) -> HirVec<Attribute> {
-    attrs.move_flat_map(|x| fld.fold_attribute(x))
+pub fn fold_attrs<T, F>(attrs: T, fld: &mut F) -> T
+    where T: Into<Vec<Attribute>> + From<Vec<Attribute>>,
+          F: Folder,
+{
+    attrs.into().move_flat_map(|x| fld.fold_attribute(x)).into()
 }
 
 pub fn noop_fold_arm<T: Folder>(Arm { attrs, pats, guard, body }: Arm, fld: &mut T) -> Arm {
@@ -461,7 +464,7 @@ pub fn noop_fold_local<T: Folder>(l: P<Local>, fld: &mut T) -> P<Local> {
             pat: fld.fold_pat(pat),
             init: init.map(|e| fld.fold_expr(e)),
             span: fld.new_span(span),
-            attrs: attrs.map_thin_attrs(|attrs| fold_attrs(attrs.into(), fld).into()),
+            attrs: fold_attrs(attrs, fld),
         }
     })
 }
@@ -914,8 +917,8 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
             id: folder.new_id(id),
             node: match node {
                 PatKind::Wild => PatKind::Wild,
-                PatKind::Ident(binding_mode, pth1, sub) => {
-                    PatKind::Ident(binding_mode,
+                PatKind::Binding(binding_mode, pth1, sub) => {
+                    PatKind::Binding(binding_mode,
                              Spanned {
                                  span: folder.new_span(pth1.span),
                                  node: folder.fold_name(pth1.node),
@@ -923,9 +926,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                              sub.map(|x| folder.fold_pat(x)))
                 }
                 PatKind::Lit(e) => PatKind::Lit(folder.fold_expr(e)),
-                PatKind::TupleStruct(pth, pats) => {
+                PatKind::TupleStruct(pth, pats, ddpos) => {
                     PatKind::TupleStruct(folder.fold_path(pth),
-                            pats.map(|pats| pats.move_map(|x| folder.fold_pat(x))))
+                            pats.move_map(|x| folder.fold_pat(x)), ddpos)
                 }
                 PatKind::Path(pth) => {
                     PatKind::Path(folder.fold_path(pth))
@@ -948,7 +951,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                     });
                     PatKind::Struct(pth, fs, etc)
                 }
-                PatKind::Tup(elts) => PatKind::Tup(elts.move_map(|x| folder.fold_pat(x))),
+                PatKind::Tuple(elts, ddpos) => {
+                    PatKind::Tuple(elts.move_map(|x| folder.fold_pat(x)), ddpos)
+                }
                 PatKind::Box(inner) => PatKind::Box(folder.fold_pat(inner)),
                 PatKind::Ref(inner, mutbl) => PatKind::Ref(folder.fold_pat(inner), mutbl),
                 PatKind::Range(e1, e2) => {
@@ -1009,11 +1014,15 @@ pub fn noop_fold_expr<T: Folder>(Expr { id, node, span, attrs }: Expr, folder: &
             ExprWhile(cond, body, opt_name) => {
                 ExprWhile(folder.fold_expr(cond),
                           folder.fold_block(body),
-                          opt_name.map(|i| folder.fold_name(i)))
+                          opt_name.map(|label| {
+                              respan(folder.new_span(label.span), folder.fold_name(label.node))
+                          }))
             }
             ExprLoop(body, opt_name) => {
                 ExprLoop(folder.fold_block(body),
-                         opt_name.map(|i| folder.fold_name(i)))
+                         opt_name.map(|label| {
+                             respan(folder.new_span(label.span), folder.fold_name(label.node))
+                         }))
             }
             ExprMatch(expr, arms, source) => {
                 ExprMatch(folder.fold_expr(expr),
@@ -1072,7 +1081,7 @@ pub fn noop_fold_expr<T: Folder>(Expr { id, node, span, attrs }: Expr, folder: &
             }
         },
         span: folder.new_span(span),
-        attrs: attrs.map_thin_attrs(|attrs| fold_attrs(attrs.into(), folder).into()),
+        attrs: fold_attrs(attrs, folder),
     }
 }
 
index 2e9e433b830fcb88b7c497f66e988f13a8ccf8bc..2d5c4ebf8d898bbc3bf0bff24f945a712f48045c 100644 (file)
@@ -27,8 +27,8 @@
 
 use syntax::abi::Abi;
 use syntax::ast::{NodeId, CRATE_NODE_ID, Name, Attribute};
-use syntax::attr::ThinAttributesExt;
-use syntax::codemap::Span;
+use syntax::codemap::Spanned;
+use syntax_pos::Span;
 use hir::*;
 
 use std::cmp;
@@ -132,6 +132,9 @@ pub trait Visitor<'v> : Sized {
     fn visit_generics(&mut self, g: &'v Generics) {
         walk_generics(self, g)
     }
+    fn visit_where_predicate(&mut self, predicate: &'v WherePredicate) {
+        walk_where_predicate(self, predicate)
+    }
     fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Block, s: Span, _: NodeId) {
         walk_fn(self, fk, fd, b, s)
     }
@@ -203,11 +206,17 @@ pub trait Visitor<'v> : Sized {
 }
 
 pub fn walk_opt_name<'v, V: Visitor<'v>>(visitor: &mut V, span: Span, opt_name: Option<Name>) {
-    for name in opt_name {
+    if let Some(name) = opt_name {
         visitor.visit_name(span, name);
     }
 }
 
+pub fn walk_opt_sp_name<'v, V: Visitor<'v>>(visitor: &mut V, opt_sp_name: &Option<Spanned<Name>>) {
+    if let Some(ref sp_name) = *opt_sp_name {
+        visitor.visit_name(sp_name.span, sp_name.node);
+    }
+}
+
 /// Walks the contents of a crate. See also `Crate::visit_all_items`.
 pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate) {
     visitor.visit_mod(&krate.module, krate.span, CRATE_NODE_ID);
@@ -274,12 +283,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
                     visitor.visit_path(path, item.id);
                 }
                 ViewPathList(ref prefix, ref list) => {
-                    if !list.is_empty() {
-                        for item in list {
-                            visitor.visit_path_list_item(prefix, item)
-                        }
-                    } else {
-                        visitor.visit_path(prefix, item.id);
+                    visitor.visit_path(prefix, item.id);
+                    for item in list {
+                        visitor.visit_path_list_item(prefix, item)
                     }
                 }
             }
@@ -413,12 +419,8 @@ pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path) {
 }
 
 pub fn walk_path_list_item<'v, V: Visitor<'v>>(visitor: &mut V,
-                                               prefix: &'v Path,
+                                               _prefix: &'v Path,
                                                item: &'v PathListItem) {
-    for segment in &prefix.segments {
-        visitor.visit_path_segment(prefix.span, segment);
-    }
-
     walk_opt_name(visitor, item.span, item.node.name());
     walk_opt_name(visitor, item.span, item.node.rename());
 }
@@ -454,11 +456,9 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V,
 
 pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
     match pattern.node {
-        PatKind::TupleStruct(ref path, ref opt_children) => {
+        PatKind::TupleStruct(ref path, ref children, _) => {
             visitor.visit_path(path, pattern.id);
-            if let Some(ref children) = *opt_children {
-                walk_list!(visitor, visit_pat, children);
-            }
+            walk_list!(visitor, visit_pat, children);
         }
         PatKind::Path(ref path) => {
             visitor.visit_path(path, pattern.id);
@@ -474,14 +474,14 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
                 visitor.visit_pat(&field.node.pat)
             }
         }
-        PatKind::Tup(ref tuple_elements) => {
+        PatKind::Tuple(ref tuple_elements, _) => {
             walk_list!(visitor, visit_pat, tuple_elements);
         }
         PatKind::Box(ref subpattern) |
         PatKind::Ref(ref subpattern, _) => {
             visitor.visit_pat(subpattern)
         }
-        PatKind::Ident(_, ref pth1, ref optional_subpattern) => {
+        PatKind::Binding(_, ref pth1, ref optional_subpattern) => {
             visitor.visit_name(pth1.span, pth1.node);
             walk_list!(visitor, visit_pat, optional_subpattern);
         }
@@ -532,29 +532,34 @@ pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics
         walk_list!(visitor, visit_ty, &param.default);
     }
     walk_list!(visitor, visit_lifetime_def, &generics.lifetimes);
-    for predicate in &generics.where_clause.predicates {
-        match predicate {
-            &WherePredicate::BoundPredicate(WhereBoundPredicate{ref bounded_ty,
-                                                                          ref bounds,
-                                                                          ref bound_lifetimes,
-                                                                          ..}) => {
-                visitor.visit_ty(bounded_ty);
-                walk_list!(visitor, visit_ty_param_bound, bounds);
-                walk_list!(visitor, visit_lifetime_def, bound_lifetimes);
-            }
-            &WherePredicate::RegionPredicate(WhereRegionPredicate{ref lifetime,
-                                                                            ref bounds,
-                                                                            ..}) => {
-                visitor.visit_lifetime(lifetime);
-                walk_list!(visitor, visit_lifetime, bounds);
-            }
-            &WherePredicate::EqPredicate(WhereEqPredicate{id,
-                                                                    ref path,
-                                                                    ref ty,
-                                                                    ..}) => {
-                visitor.visit_path(path, id);
-                visitor.visit_ty(ty);
-            }
+    walk_list!(visitor, visit_where_predicate, &generics.where_clause.predicates);
+}
+
+pub fn walk_where_predicate<'v, V: Visitor<'v>>(
+    visitor: &mut V,
+    predicate: &'v WherePredicate)
+{
+    match predicate {
+        &WherePredicate::BoundPredicate(WhereBoundPredicate{ref bounded_ty,
+                                                            ref bounds,
+                                                            ref bound_lifetimes,
+                                                            ..}) => {
+            visitor.visit_ty(bounded_ty);
+            walk_list!(visitor, visit_ty_param_bound, bounds);
+            walk_list!(visitor, visit_lifetime_def, bound_lifetimes);
+        }
+        &WherePredicate::RegionPredicate(WhereRegionPredicate{ref lifetime,
+                                                              ref bounds,
+                                                              ..}) => {
+            visitor.visit_lifetime(lifetime);
+            walk_list!(visitor, visit_lifetime, bounds);
+        }
+        &WherePredicate::EqPredicate(WhereEqPredicate{id,
+                                                      ref path,
+                                                      ref ty,
+                                                      ..}) => {
+            visitor.visit_path(path, id);
+            visitor.visit_ty(ty);
         }
     }
 }
@@ -737,21 +742,21 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             visitor.visit_block(if_block);
             walk_list!(visitor, visit_expr, optional_else);
         }
-        ExprWhile(ref subexpression, ref block, opt_name) => {
+        ExprWhile(ref subexpression, ref block, ref opt_sp_name) => {
             visitor.visit_expr(subexpression);
             visitor.visit_block(block);
-            walk_opt_name(visitor, expression.span, opt_name)
+            walk_opt_sp_name(visitor, opt_sp_name);
         }
-        ExprLoop(ref block, opt_name) => {
+        ExprLoop(ref block, ref opt_sp_name) => {
             visitor.visit_block(block);
-            walk_opt_name(visitor, expression.span, opt_name)
+            walk_opt_sp_name(visitor, opt_sp_name);
         }
         ExprMatch(ref subexpression, ref arms, _) => {
             visitor.visit_expr(subexpression);
             walk_list!(visitor, visit_arm, arms);
         }
         ExprClosure(_, ref function_declaration, ref body, _fn_decl_span) => {
-            visitor.visit_fn(FnKind::Closure(expression.attrs.as_attr_slice()),
+            visitor.visit_fn(FnKind::Closure(&expression.attrs),
                              function_declaration,
                              body,
                              expression.span,
@@ -784,9 +789,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             visitor.visit_path(path, expression.id)
         }
         ExprBreak(ref opt_sp_name) | ExprAgain(ref opt_sp_name) => {
-            for sp_name in opt_sp_name {
-                visitor.visit_name(sp_name.span, sp_name.node);
-            }
+            walk_opt_sp_name(visitor, opt_sp_name);
         }
         ExprRet(ref optional_expression) => {
             walk_list!(visitor, visit_expr, optional_expression);
index 28506fd20fe53bc3884290069474916c1a39a23f..2cc39412182dc5a771417a8f7e714cdf6cad4a3d 100644 (file)
@@ -50,18 +50,18 @@ use session::Session;
 use std::collections::BTreeMap;
 use std::iter;
 use syntax::ast::*;
-use syntax::attr::{ThinAttributes, ThinAttributesExt};
-use syntax::ext::mtwt;
+use syntax::errors;
 use syntax::ptr::P;
-use syntax::codemap::{respan, Spanned, Span};
-use syntax::parse::token::{self, keywords};
+use syntax::codemap::{respan, Spanned};
+use syntax::parse::token;
 use syntax::std_inject;
 use syntax::visit::{self, Visitor};
+use syntax_pos::Span;
 
 pub struct LoweringContext<'a> {
     crate_root: Option<&'static str>,
     // Use to assign ids to hir nodes that do not directly correspond to an ast node
-    id_assigner: &'a NodeIdAssigner,
+    sess: Option<&'a Session>,
     // As we walk the AST we must keep track of the current 'parent' def id (in
     // the form of a DefIndex) so that if we create a new node which introduces
     // a definition, then we can properly create the def id.
@@ -100,7 +100,6 @@ impl Resolver for DummyResolver {
 
 pub fn lower_crate(sess: &Session,
                    krate: &Crate,
-                   id_assigner: &NodeIdAssigner,
                    resolver: &mut Resolver)
                    -> hir::Crate {
     // We're constructing the HIR here; we don't care what we will
@@ -116,17 +115,17 @@ pub fn lower_crate(sess: &Session,
         } else {
             Some("std")
         },
-        id_assigner: id_assigner,
+        sess: Some(sess),
         parent_def: None,
         resolver: resolver,
     }.lower_crate(krate)
 }
 
 impl<'a> LoweringContext<'a> {
-    pub fn testing_context(id_assigner: &'a NodeIdAssigner, resolver: &'a mut Resolver) -> Self {
+    pub fn testing_context(resolver: &'a mut Resolver) -> Self {
         LoweringContext {
             crate_root: None,
-            id_assigner: id_assigner,
+            sess: None,
             parent_def: None,
             resolver: resolver,
         }
@@ -138,8 +137,8 @@ impl<'a> LoweringContext<'a> {
             lctx: &'lcx mut LoweringContext<'interner>,
         }
 
-        impl<'lcx, 'interner> Visitor<'lcx> for ItemLowerer<'lcx, 'interner> {
-            fn visit_item(&mut self, item: &'lcx Item) {
+        impl<'lcx, 'interner> Visitor for ItemLowerer<'lcx, 'interner> {
+            fn visit_item(&mut self, item: &Item) {
                 self.items.insert(item.id, self.lctx.lower_item(item));
                 visit::walk_item(self, item);
             }
@@ -162,7 +161,12 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn next_id(&self) -> NodeId {
-        self.id_assigner.next_node_id()
+        self.sess.map(Session::next_node_id).unwrap_or(0)
+    }
+
+    fn diagnostic(&self) -> &errors::Handler {
+        self.sess.map(Session::diagnostic)
+                 .unwrap_or_else(|| panic!("this lowerer cannot emit diagnostics"))
     }
 
     fn str_to_ident(&self, s: &'static str) -> Name {
@@ -184,12 +188,8 @@ impl<'a> LoweringContext<'a> {
         result
     }
 
-    fn lower_ident(&mut self, ident: Ident) -> Name {
-        if ident.name != keywords::Invalid.name() {
-            mtwt::resolve(ident)
-        } else {
-            ident.name
-        }
+    fn lower_opt_sp_ident(&mut self, o_id: Option<Spanned<Ident>>) -> Option<Spanned<Name>> {
+        o_id.map(|sp_ident| respan(sp_ident.span, sp_ident.node.name))
     }
 
     fn lower_attrs(&mut self, attrs: &Vec<Attribute>) -> hir::HirVec<Attribute> {
@@ -242,19 +242,6 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
-    fn lower_decl(&mut self, d: &Decl) -> P<hir::Decl> {
-        match d.node {
-            DeclKind::Local(ref l) => P(Spanned {
-                node: hir::DeclLocal(self.lower_local(l)),
-                span: d.span,
-            }),
-            DeclKind::Item(ref it) => P(Spanned {
-                node: hir::DeclItem(self.lower_item_id(it)),
-                span: d.span,
-            }),
-        }
-    }
-
     fn lower_ty_binding(&mut self, b: &TypeBinding) -> hir::TypeBinding {
         hir::TypeBinding {
             id: b.id,
@@ -269,7 +256,7 @@ impl<'a> LoweringContext<'a> {
         P(hir::Ty {
             id: t.id,
             node: match t.node {
-                Infer => hir::TyInfer,
+                Infer | ImplicitSelf => hir::TyInfer,
                 Vec(ref ty) => hir::TyVec(self.lower_ty(ty)),
                 Ptr(ref mt) => hir::TyPtr(self.lower_mt(mt)),
                 Rptr(ref region, ref mt) => {
@@ -334,18 +321,14 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
-    fn lower_path_full(&mut self, p: &Path, rename: bool) -> hir::Path {
+    fn lower_path(&mut self, p: &Path) -> hir::Path {
         hir::Path {
             global: p.global,
             segments: p.segments
                        .iter()
                        .map(|&PathSegment { identifier, ref parameters }| {
                            hir::PathSegment {
-                               name: if rename {
-                                   self.lower_ident(identifier)
-                               } else {
-                                   identifier.name
-                               },
+                               name: identifier.name,
                                parameters: self.lower_path_parameters(parameters),
                            }
                        })
@@ -354,10 +337,6 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
-    fn lower_path(&mut self, p: &Path) -> hir::Path {
-        self.lower_path_full(p, false)
-    }
-
     fn lower_path_parameters(&mut self, path_parameters: &PathParameters) -> hir::PathParameters {
         match *path_parameters {
             PathParameters::AngleBracketed(ref data) =>
@@ -600,10 +579,23 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn lower_block(&mut self, b: &Block) -> P<hir::Block> {
+        let mut stmts = Vec::new();
+        let mut expr = None;
+
+        if let Some((last, rest)) = b.stmts.split_last() {
+            stmts = rest.iter().map(|s| self.lower_stmt(s)).collect::<Vec<_>>();
+            let last = self.lower_stmt(last);
+            if let hir::StmtExpr(e, _) = last.node {
+                expr = Some(e);
+            } else {
+                stmts.push(last);
+            }
+        }
+
         P(hir::Block {
             id: b.id,
-            stmts: b.stmts.iter().map(|s| self.lower_stmt(s)).collect(),
-            expr: b.expr.as_ref().map(|ref x| self.lower_expr(x)),
+            stmts: stmts.into(),
+            expr: expr,
             rules: self.lower_block_check_mode(&b.rules),
             span: b.span,
         })
@@ -696,6 +688,7 @@ impl<'a> LoweringContext<'a> {
                         hir::TypeTraitItem(this.lower_bounds(bounds),
                                            default.as_ref().map(|x| this.lower_ty(x)))
                     }
+                    TraitItemKind::Macro(..) => panic!("Shouldn't exist any more"),
                 },
                 span: i.span,
             }
@@ -787,23 +780,24 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn lower_method_sig(&mut self, sig: &MethodSig) -> hir::MethodSig {
-        // Check for `self: _` and `self: &_`
-        if let SelfKind::Explicit(ref ty, _) = sig.explicit_self.node {
-            match sig.decl.inputs.get(0).and_then(Arg::to_self).map(|eself| eself.node) {
-                Some(SelfKind::Value(..)) | Some(SelfKind::Region(..)) => {
-                    self.id_assigner.diagnostic().span_err(ty.span,
-                        "the type placeholder `_` is not allowed within types on item signatures");
-                }
-                _ => {}
-            }
-        }
-        hir::MethodSig {
+        let hir_sig = hir::MethodSig {
             generics: self.lower_generics(&sig.generics),
             abi: sig.abi,
             unsafety: self.lower_unsafety(sig.unsafety),
             constness: self.lower_constness(sig.constness),
             decl: self.lower_fn_decl(&sig.decl),
+        };
+        // Check for `self: _` and `self: &_`
+        if let Some(SelfKind::Explicit(..)) = sig.decl.get_self().map(|eself| eself.node) {
+            match hir_sig.decl.get_self().map(|eself| eself.node) {
+                Some(hir::SelfKind::Value(..)) | Some(hir::SelfKind::Region(..)) => {
+                    self.diagnostic().span_err(sig.decl.inputs[0].ty.span,
+                        "the type placeholder `_` is not allowed within types on item signatures");
+                }
+                _ => {}
+            }
         }
+        hir_sig
     }
 
     fn lower_unsafety(&mut self, u: Unsafety) -> hir::Unsafety {
@@ -861,26 +855,27 @@ impl<'a> LoweringContext<'a> {
                 PatKind::Wild => hir::PatKind::Wild,
                 PatKind::Ident(ref binding_mode, pth1, ref sub) => {
                     self.with_parent_def(p.id, |this| {
-                        let name = match this.resolver.get_resolution(p.id).map(|d| d.full_def()) {
-                            // Only pattern bindings are renamed
-                            None | Some(Def::Local(..)) => this.lower_ident(pth1.node),
-                            _ => pth1.node.name,
-                        };
-                        hir::PatKind::Ident(this.lower_binding_mode(binding_mode),
-                                            respan(pth1.span, name),
-                                            sub.as_ref().map(|x| this.lower_pat(x)))
+                        match this.resolver.get_resolution(p.id).map(|d| d.base_def) {
+                            // `None` can occur in body-less function signatures
+                            None | Some(Def::Local(..)) => {
+                                hir::PatKind::Binding(this.lower_binding_mode(binding_mode),
+                                                      respan(pth1.span, pth1.node.name),
+                                                      sub.as_ref().map(|x| this.lower_pat(x)))
+                            }
+                            _ => hir::PatKind::Path(hir::Path::from_name(pth1.span, pth1.node.name))
+                        }
                     })
                 }
                 PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)),
-                PatKind::TupleStruct(ref pth, ref pats) => {
+                PatKind::TupleStruct(ref pth, ref pats, ddpos) => {
                     hir::PatKind::TupleStruct(self.lower_path(pth),
-                                 pats.as_ref()
-                                     .map(|pats| pats.iter().map(|x| self.lower_pat(x)).collect()))
+                                              pats.iter().map(|x| self.lower_pat(x)).collect(),
+                                              ddpos)
                 }
-                PatKind::Path(ref pth) => {
+                PatKind::Path(None, ref pth) => {
                     hir::PatKind::Path(self.lower_path(pth))
                 }
-                PatKind::QPath(ref qself, ref pth) => {
+                PatKind::Path(Some(ref qself), ref pth) => {
                     let qself = hir::QSelf {
                         ty: self.lower_ty(&qself.ty),
                         position: qself.position,
@@ -903,8 +898,8 @@ impl<'a> LoweringContext<'a> {
                                    .collect();
                     hir::PatKind::Struct(pth, fs, etc)
                 }
-                PatKind::Tup(ref elts) => {
-                    hir::PatKind::Tup(elts.iter().map(|x| self.lower_pat(x)).collect())
+                PatKind::Tuple(ref elts, ddpos) => {
+                    hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
                 }
                 PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
                 PatKind::Ref(ref inner, mutbl) => {
@@ -973,16 +968,16 @@ impl<'a> LoweringContext<'a> {
 
                     let make_call = |this: &mut LoweringContext, p, args| {
                         let path = this.core_path(e.span, p);
-                        let path = this.expr_path(path, None);
-                        this.expr_call(e.span, path, args, None)
+                        let path = this.expr_path(path, ThinVec::new());
+                        this.expr_call(e.span, path, args)
                     };
 
                     let mk_stmt_let = |this: &mut LoweringContext, bind, expr| {
-                        this.stmt_let(e.span, false, bind, expr, None)
+                        this.stmt_let(e.span, false, bind, expr)
                     };
 
                     let mk_stmt_let_mut = |this: &mut LoweringContext, bind, expr| {
-                        this.stmt_let(e.span, true, bind, expr, None)
+                        this.stmt_let(e.span, true, bind, expr)
                     };
 
                     // let placer = <placer_expr> ;
@@ -991,21 +986,21 @@ impl<'a> LoweringContext<'a> {
                                                                  placer_expr,
                                                                  e.span,
                                                                  hir::PopUnstableBlock,
-                                                                 None);
+                                                                 ThinVec::new());
                         mk_stmt_let(self, placer_ident, placer_expr)
                     };
 
                     // let mut place = Placer::make_place(placer);
                     let (s2, place_binding) = {
-                        let placer = self.expr_ident(e.span, placer_ident, None, placer_binding);
+                        let placer = self.expr_ident(e.span, placer_ident, placer_binding);
                         let call = make_call(self, &make_place, hir_vec![placer]);
                         mk_stmt_let_mut(self, place_ident, call)
                     };
 
                     // let p_ptr = Place::pointer(&mut place);
                     let (s3, p_ptr_binding) = {
-                        let agent = self.expr_ident(e.span, place_ident, None, place_binding);
-                        let args = hir_vec![self.expr_mut_addr_of(e.span, agent, None)];
+                        let agent = self.expr_ident(e.span, place_ident, place_binding);
+                        let args = hir_vec![self.expr_mut_addr_of(e.span, agent)];
                         let call = make_call(self, &place_pointer, args);
                         mk_stmt_let(self, p_ptr_ident, call)
                     };
@@ -1016,11 +1011,12 @@ impl<'a> LoweringContext<'a> {
                                                                 value_expr,
                                                                 e.span,
                                                                 hir::PopUnstableBlock,
-                                                                None);
+                                                                ThinVec::new());
                         self.signal_block_expr(hir_vec![],
                                                value_expr,
                                                e.span,
-                                               hir::PopUnsafeBlock(hir::CompilerGenerated), None)
+                                               hir::PopUnsafeBlock(hir::CompilerGenerated),
+                                               ThinVec::new())
                     };
 
                     // push_unsafe!({
@@ -1028,19 +1024,20 @@ impl<'a> LoweringContext<'a> {
                     //     InPlace::finalize(place)
                     // })
                     let expr = {
-                        let ptr = self.expr_ident(e.span, p_ptr_ident, None, p_ptr_binding);
+                        let ptr = self.expr_ident(e.span, p_ptr_ident, p_ptr_binding);
                         let call_move_val_init =
                             hir::StmtSemi(
                                 make_call(self, &move_val_init, hir_vec![ptr, pop_unsafe_expr]),
                                 self.next_id());
                         let call_move_val_init = respan(e.span, call_move_val_init);
 
-                        let place = self.expr_ident(e.span, place_ident, None, place_binding);
+                        let place = self.expr_ident(e.span, place_ident, place_binding);
                         let call = make_call(self, &inplace_finalize, hir_vec![place]);
                         self.signal_block_expr(hir_vec![call_move_val_init],
                                                call,
                                                e.span,
-                                               hir::PushUnsafeBlock(hir::CompilerGenerated), None)
+                                               hir::PushUnsafeBlock(hir::CompilerGenerated),
+                                               ThinVec::new())
                     };
 
                     return self.signal_block_expr(hir_vec![s1, s2, s3],
@@ -1112,7 +1109,7 @@ impl<'a> LoweringContext<'a> {
                                     rules: hir::DefaultBlock,
                                     span: span,
                                 });
-                                self.expr_block(blk, None)
+                                self.expr_block(blk, ThinVec::new())
                             }
                             _ => self.lower_expr(els),
                         }
@@ -1122,11 +1119,10 @@ impl<'a> LoweringContext<'a> {
                 }
                 ExprKind::While(ref cond, ref body, opt_ident) => {
                     hir::ExprWhile(self.lower_expr(cond), self.lower_block(body),
-                                   opt_ident.map(|ident| self.lower_ident(ident)))
+                                   self.lower_opt_sp_ident(opt_ident))
                 }
                 ExprKind::Loop(ref body, opt_ident) => {
-                    hir::ExprLoop(self.lower_block(body),
-                                  opt_ident.map(|ident| self.lower_ident(ident)))
+                    hir::ExprLoop(self.lower_block(body), self.lower_opt_sp_ident(opt_ident))
                 }
                 ExprKind::Match(ref expr, ref arms) => {
                     hir::ExprMatch(self.lower_expr(expr),
@@ -1180,7 +1176,7 @@ impl<'a> LoweringContext<'a> {
                                                                           expr,
                                                                           e.span,
                                                                           hir::PopUnstableBlock,
-                                                                          None);
+                                                                          ThinVec::new());
                                 this.field(token::intern(s), signal_block, ast_expr.span)
                             }).collect();
                             let attrs = ast_expr.attrs.clone();
@@ -1192,7 +1188,7 @@ impl<'a> LoweringContext<'a> {
                                                hir_expr,
                                                ast_expr.span,
                                                hir::PushUnstableBlock,
-                                               None)
+                                               ThinVec::new())
                     }
 
                     use syntax::ast::RangeLimits::*;
@@ -1221,7 +1217,7 @@ impl<'a> LoweringContext<'a> {
                             make_struct(self, e, &["RangeInclusive", "NonEmpty"],
                                                  &[("start", e1), ("end", e2)]),
 
-                        _ => panic!(self.id_assigner.diagnostic()
+                        _ => panic!(self.diagnostic()
                                         .span_fatal(e.span, "inclusive range with no end")),
                     };
                 }
@@ -1232,23 +1228,10 @@ impl<'a> LoweringContext<'a> {
                             position: position,
                         }
                     });
-                    let rename = if path.segments.len() == 1 {
-                        // Only local variables are renamed
-                        match self.resolver.get_resolution(e.id).map(|d| d.full_def()) {
-                            Some(Def::Local(..)) | Some(Def::Upvar(..)) => true,
-                            _ => false,
-                        }
-                    } else {
-                        false
-                    };
-                    hir::ExprPath(hir_qself, self.lower_path_full(path, rename))
+                    hir::ExprPath(hir_qself, self.lower_path(path))
                 }
-                ExprKind::Break(opt_ident) => hir::ExprBreak(opt_ident.map(|sp_ident| {
-                    respan(sp_ident.span, self.lower_ident(sp_ident.node))
-                })),
-                ExprKind::Again(opt_ident) => hir::ExprAgain(opt_ident.map(|sp_ident| {
-                    respan(sp_ident.span, self.lower_ident(sp_ident.node))
-                })),
+                ExprKind::Break(opt_ident) => hir::ExprBreak(self.lower_opt_sp_ident(opt_ident)),
+                ExprKind::Continue(opt_ident) => hir::ExprAgain(self.lower_opt_sp_ident(opt_ident)),
                 ExprKind::Ret(ref e) => hir::ExprRet(e.as_ref().map(|x| self.lower_expr(x))),
                 ExprKind::InlineAsm(InlineAsm {
                         ref inputs,
@@ -1292,9 +1275,9 @@ impl<'a> LoweringContext<'a> {
                             ex.span = e.span;
                         }
                         // merge attributes into the inner expression.
-                        ex.attrs.update(|attrs| {
-                            attrs.prepend(e.attrs.clone())
-                        });
+                        let mut attrs = e.attrs.clone();
+                        attrs.extend::<Vec<_>>(ex.attrs.into());
+                        ex.attrs = attrs;
                         ex
                     });
                 }
@@ -1313,7 +1296,7 @@ impl<'a> LoweringContext<'a> {
                     // `<pat> => <body>`
                     let pat_arm = {
                         let body = self.lower_block(body);
-                        let body_expr = self.expr_block(body, None);
+                        let body_expr = self.expr_block(body, ThinVec::new());
                         let pat = self.lower_pat(pat);
                         self.arm(hir_vec![pat], body_expr)
                     };
@@ -1333,7 +1316,7 @@ impl<'a> LoweringContext<'a> {
                                                 attrs: hir_vec![],
                                                 pats: hir_vec![pat_under],
                                                 guard: Some(cond),
-                                                body: self.expr_block(then, None),
+                                                body: self.expr_block(then, ThinVec::new()),
                                             });
                                             else_opt.map(|else_opt| (else_opt, true))
                                         }
@@ -1364,7 +1347,7 @@ impl<'a> LoweringContext<'a> {
                     let else_arm = {
                         let pat_under = self.pat_wild(e.span);
                         let else_expr =
-                            else_opt.unwrap_or_else(|| self.expr_tuple(e.span, hir_vec![], None));
+                            else_opt.unwrap_or_else(|| self.expr_tuple(e.span, hir_vec![]));
                         self.arm(hir_vec![pat_under], else_expr)
                     };
 
@@ -1399,7 +1382,7 @@ impl<'a> LoweringContext<'a> {
                     // `<pat> => <body>`
                     let pat_arm = {
                         let body = self.lower_block(body);
-                        let body_expr = self.expr_block(body, None);
+                        let body_expr = self.expr_block(body, ThinVec::new());
                         let pat = self.lower_pat(pat);
                         self.arm(hir_vec![pat], body_expr)
                     };
@@ -1407,7 +1390,7 @@ impl<'a> LoweringContext<'a> {
                     // `_ => break`
                     let break_arm = {
                         let pat_under = self.pat_wild(e.span);
-                        let break_expr = self.expr_break(e.span, None);
+                        let break_expr = self.expr_break(e.span, ThinVec::new());
                         self.arm(hir_vec![pat_under], break_expr)
                     };
 
@@ -1418,12 +1401,11 @@ impl<'a> LoweringContext<'a> {
                                                hir::ExprMatch(sub_expr,
                                                               arms,
                                                               hir::MatchSource::WhileLetDesugar),
-                                               None);
+                                               ThinVec::new());
 
                     // `[opt_ident]: loop { ... }`
                     let loop_block = self.block_expr(match_expr);
-                    let loop_expr = hir::ExprLoop(loop_block,
-                                                  opt_ident.map(|ident| self.lower_ident(ident)));
+                    let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident));
                     // add attributes to the outer returned expr node
                     let attrs = e.attrs.clone();
                     return P(hir::Expr { id: e.id, node: loop_expr, span: e.span, attrs: attrs });
@@ -1461,7 +1443,7 @@ impl<'a> LoweringContext<'a> {
                             id: self.next_id(),
                             node: hir::ExprBlock(body_block),
                             span: body_span,
-                            attrs: None,
+                            attrs: ThinVec::new(),
                         });
                         let pat = self.lower_pat(pat);
                         let some_pat = self.pat_some(e.span, pat);
@@ -1471,7 +1453,7 @@ impl<'a> LoweringContext<'a> {
 
                     // `::std::option::Option::None => break`
                     let break_arm = {
-                        let break_expr = self.expr_break(e.span, None);
+                        let break_expr = self.expr_break(e.span, ThinVec::new());
                         let pat = self.pat_none(e.span);
                         self.arm(hir_vec![pat], break_expr)
                     };
@@ -1487,26 +1469,26 @@ impl<'a> LoweringContext<'a> {
 
                             self.path_global(e.span, strs)
                         };
-                        let iter = self.expr_ident(e.span, iter, None, iter_pat.id);
-                        let ref_mut_iter = self.expr_mut_addr_of(e.span, iter, None);
-                        let next_path = self.expr_path(next_path, None);
-                        let next_expr = self.expr_call(e.span,
-                                                       next_path,
-                                                       hir_vec![ref_mut_iter],
-                                                       None);
+                        let iter = self.expr_ident(e.span, iter, iter_pat.id);
+                        let ref_mut_iter = self.expr_mut_addr_of(e.span, iter);
+                        let next_path = self.expr_path(next_path, ThinVec::new());
+                        let next_expr = self.expr_call(e.span, next_path, hir_vec![ref_mut_iter]);
                         let arms = hir_vec![pat_arm, break_arm];
 
                         self.expr(e.span,
                                   hir::ExprMatch(next_expr, arms, hir::MatchSource::ForLoopDesugar),
-                                  None)
+                                  ThinVec::new())
                     };
 
                     // `[opt_ident]: loop { ... }`
                     let loop_block = self.block_expr(match_expr);
-                    let loop_expr = hir::ExprLoop(loop_block,
-                                                  opt_ident.map(|ident| self.lower_ident(ident)));
-                    let loop_expr =
-                        P(hir::Expr { id: e.id, node: loop_expr, span: e.span, attrs: None });
+                    let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident));
+                    let loop_expr = P(hir::Expr {
+                        id: e.id,
+                        node: loop_expr,
+                        span: e.span,
+                        attrs: ThinVec::new(),
+                    });
 
                     // `mut iter => { ... }`
                     let iter_arm = self.arm(hir_vec![iter_pat], loop_expr);
@@ -1519,23 +1501,22 @@ impl<'a> LoweringContext<'a> {
                             self.path_global(e.span, strs)
                         };
 
-                        let into_iter = self.expr_path(into_iter_path, None);
-                        self.expr_call(e.span, into_iter, hir_vec![head], None)
+                        let into_iter = self.expr_path(into_iter_path, ThinVec::new());
+                        self.expr_call(e.span, into_iter, hir_vec![head])
                     };
 
                     let match_expr = self.expr_match(e.span,
                                                      into_iter_expr,
                                                      hir_vec![iter_arm],
-                                                     hir::MatchSource::ForLoopDesugar,
-                                                     None);
+                                                     hir::MatchSource::ForLoopDesugar);
 
                     // `{ let _result = ...; _result }`
                     // underscore prevents an unused_variables lint if the head diverges
                     let result_ident = self.str_to_ident("_result");
                     let (let_stmt, let_stmt_binding) =
-                        self.stmt_let(e.span, false, result_ident, match_expr, None);
+                        self.stmt_let(e.span, false, result_ident, match_expr);
 
-                    let result = self.expr_ident(e.span, result_ident, None, let_stmt_binding);
+                    let result = self.expr_ident(e.span, result_ident, let_stmt_binding);
                     let block = self.block_all(e.span, hir_vec![let_stmt], Some(result));
                     // add the attributes to the outer returned expr node
                     return self.expr_block(block, e.attrs.clone());
@@ -1562,7 +1543,7 @@ impl<'a> LoweringContext<'a> {
                     let ok_arm = {
                         let val_ident = self.str_to_ident("val");
                         let val_pat = self.pat_ident(e.span, val_ident);
-                        let val_expr = self.expr_ident(e.span, val_ident, None, val_pat.id);
+                        let val_expr = self.expr_ident(e.span, val_ident, val_pat.id);
                         let ok_pat = self.pat_ok(e.span, val_pat);
 
                         self.arm(hir_vec![ok_pat], val_expr)
@@ -1575,26 +1556,26 @@ impl<'a> LoweringContext<'a> {
                         let from_expr = {
                             let path = self.std_path(&["convert", "From", "from"]);
                             let path = self.path_global(e.span, path);
-                            let from = self.expr_path(path, None);
-                            let err_expr = self.expr_ident(e.span, err_ident, None, err_local.id);
+                            let from = self.expr_path(path, ThinVec::new());
+                            let err_expr = self.expr_ident(e.span, err_ident, err_local.id);
 
-                            self.expr_call(e.span, from, hir_vec![err_expr], None)
+                            self.expr_call(e.span, from, hir_vec![err_expr])
                         };
                         let err_expr = {
                             let path = self.std_path(&["result", "Result", "Err"]);
                             let path = self.path_global(e.span, path);
-                            let err_ctor = self.expr_path(path, None);
-                            self.expr_call(e.span, err_ctor, hir_vec![from_expr], None)
+                            let err_ctor = self.expr_path(path, ThinVec::new());
+                            self.expr_call(e.span, err_ctor, hir_vec![from_expr])
                         };
                         let err_pat = self.pat_err(e.span, err_local);
                         let ret_expr = self.expr(e.span,
-                                                 hir::Expr_::ExprRet(Some(err_expr)), None);
-
+                                                 hir::Expr_::ExprRet(Some(err_expr)),
+                                                 ThinVec::new());
                         self.arm(hir_vec![err_pat], ret_expr)
                     };
 
                     return self.expr_match(e.span, sub_expr, hir_vec![err_arm, ok_arm],
-                                           hir::MatchSource::TryDesugar, None);
+                                           hir::MatchSource::TryDesugar);
                 }
 
                 ExprKind::Mac(_) => panic!("Shouldn't exist here"),
@@ -1606,21 +1587,29 @@ impl<'a> LoweringContext<'a> {
 
     fn lower_stmt(&mut self, s: &Stmt) -> hir::Stmt {
         match s.node {
-            StmtKind::Decl(ref d, id) => {
-                Spanned {
-                    node: hir::StmtDecl(self.lower_decl(d), id),
+            StmtKind::Local(ref l) => Spanned {
+                node: hir::StmtDecl(P(Spanned {
+                    node: hir::DeclLocal(self.lower_local(l)),
                     span: s.span,
-                }
-            }
-            StmtKind::Expr(ref e, id) => {
+                }), s.id),
+                span: s.span,
+            },
+            StmtKind::Item(ref it) => Spanned {
+                node: hir::StmtDecl(P(Spanned {
+                    node: hir::DeclItem(self.lower_item_id(it)),
+                    span: s.span,
+                }), s.id),
+                span: s.span,
+            },
+            StmtKind::Expr(ref e) => {
                 Spanned {
-                    node: hir::StmtExpr(self.lower_expr(e), id),
+                    node: hir::StmtExpr(self.lower_expr(e), s.id),
                     span: s.span,
                 }
             }
-            StmtKind::Semi(ref e, id) => {
+            StmtKind::Semi(ref e) => {
                 Spanned {
-                    node: hir::StmtSemi(self.lower_expr(e), id),
+                    node: hir::StmtSemi(self.lower_expr(e), s.id),
                     span: s.span,
                 }
             }
@@ -1709,23 +1698,18 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
-    fn expr_break(&mut self, span: Span, attrs: ThinAttributes) -> P<hir::Expr> {
+    fn expr_break(&mut self, span: Span, attrs: ThinVec<Attribute>) -> P<hir::Expr> {
         self.expr(span, hir::ExprBreak(None), attrs)
     }
 
-    fn expr_call(&mut self,
-                 span: Span,
-                 e: P<hir::Expr>,
-                 args: hir::HirVec<P<hir::Expr>>,
-                 attrs: ThinAttributes)
+    fn expr_call(&mut self, span: Span, e: P<hir::Expr>, args: hir::HirVec<P<hir::Expr>>)
                  -> P<hir::Expr> {
-        self.expr(span, hir::ExprCall(e, args), attrs)
+        self.expr(span, hir::ExprCall(e, args), ThinVec::new())
     }
 
-    fn expr_ident(&mut self, span: Span, id: Name, attrs: ThinAttributes, binding: NodeId)
-                  -> P<hir::Expr> {
+    fn expr_ident(&mut self, span: Span, id: Name, binding: NodeId) -> P<hir::Expr> {
         let expr_path = hir::ExprPath(None, self.path_ident(span, id));
-        let expr = self.expr(span, expr_path, attrs);
+        let expr = self.expr(span, expr_path, ThinVec::new());
 
         let def = self.resolver.definitions().map(|defs| {
             Def::Local(defs.local_def_id(binding), binding)
@@ -1735,12 +1719,11 @@ impl<'a> LoweringContext<'a> {
         expr
     }
 
-    fn expr_mut_addr_of(&mut self, span: Span, e: P<hir::Expr>, attrs: ThinAttributes)
-                        -> P<hir::Expr> {
-        self.expr(span, hir::ExprAddrOf(hir::MutMutable, e), attrs)
+    fn expr_mut_addr_of(&mut self, span: Span, e: P<hir::Expr>) -> P<hir::Expr> {
+        self.expr(span, hir::ExprAddrOf(hir::MutMutable, e), ThinVec::new())
     }
 
-    fn expr_path(&mut self, path: hir::Path, attrs: ThinAttributes) -> P<hir::Expr> {
+    fn expr_path(&mut self, path: hir::Path, attrs: ThinVec<Attribute>) -> P<hir::Expr> {
         let def = self.resolver.resolve_generated_global_path(&path, true);
         let expr = self.expr(path.span, hir::ExprPath(None, path), attrs);
         self.resolver.record_resolution(expr.id, def);
@@ -1751,19 +1734,17 @@ impl<'a> LoweringContext<'a> {
                   span: Span,
                   arg: P<hir::Expr>,
                   arms: hir::HirVec<hir::Arm>,
-                  source: hir::MatchSource,
-                  attrs: ThinAttributes)
+                  source: hir::MatchSource)
                   -> P<hir::Expr> {
-        self.expr(span, hir::ExprMatch(arg, arms, source), attrs)
+        self.expr(span, hir::ExprMatch(arg, arms, source), ThinVec::new())
     }
 
-    fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinAttributes) -> P<hir::Expr> {
+    fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> P<hir::Expr> {
         self.expr(b.span, hir::ExprBlock(b), attrs)
     }
 
-    fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec<P<hir::Expr>>, attrs: ThinAttributes)
-                  -> P<hir::Expr> {
-        self.expr(sp, hir::ExprTup(exprs), attrs)
+    fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec<P<hir::Expr>>) -> P<hir::Expr> {
+        self.expr(sp, hir::ExprTup(exprs), ThinVec::new())
     }
 
     fn expr_struct(&mut self,
@@ -1771,14 +1752,14 @@ impl<'a> LoweringContext<'a> {
                    path: hir::Path,
                    fields: hir::HirVec<hir::Field>,
                    e: Option<P<hir::Expr>>,
-                   attrs: ThinAttributes) -> P<hir::Expr> {
+                   attrs: ThinVec<Attribute>) -> P<hir::Expr> {
         let def = self.resolver.resolve_generated_global_path(&path, false);
         let expr = self.expr(sp, hir::ExprStruct(path, fields, e), attrs);
         self.resolver.record_resolution(expr.id, def);
         expr
     }
 
-    fn expr(&mut self, span: Span, node: hir::Expr_, attrs: ThinAttributes) -> P<hir::Expr> {
+    fn expr(&mut self, span: Span, node: hir::Expr_, attrs: ThinVec<Attribute>) -> P<hir::Expr> {
         P(hir::Expr {
             id: self.next_id(),
             node: node,
@@ -1787,12 +1768,7 @@ impl<'a> LoweringContext<'a> {
         })
     }
 
-    fn stmt_let(&mut self,
-                sp: Span,
-                mutbl: bool,
-                ident: Name,
-                ex: P<hir::Expr>,
-                attrs: ThinAttributes)
+    fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P<hir::Expr>)
                 -> (hir::Stmt, NodeId) {
         let pat = if mutbl {
             self.pat_ident_binding_mode(sp, ident, hir::BindByValue(hir::MutMutable))
@@ -1806,7 +1782,7 @@ impl<'a> LoweringContext<'a> {
             init: Some(ex),
             id: self.next_id(),
             span: sp,
-            attrs: attrs,
+            attrs: ThinVec::new(),
         });
         let decl = respan(sp, hir::DeclLocal(local));
         (respan(sp, hir::StmtDecl(P(decl), self.next_id())), pat_id)
@@ -1857,7 +1833,7 @@ impl<'a> LoweringContext<'a> {
         let pt = if subpats.is_empty() {
             hir::PatKind::Path(path)
         } else {
-            hir::PatKind::TupleStruct(path, Some(subpats))
+            hir::PatKind::TupleStruct(path, subpats, None)
         };
         let pat = self.pat(span, pt);
         self.resolver.record_resolution(pat.id, def);
@@ -1870,7 +1846,7 @@ impl<'a> LoweringContext<'a> {
 
     fn pat_ident_binding_mode(&mut self, span: Span, name: Name, bm: hir::BindingMode)
                               -> P<hir::Pat> {
-        let pat_ident = hir::PatKind::Ident(bm,
+        let pat_ident = hir::PatKind::Binding(bm,
                                             Spanned {
                                                 span: span,
                                                 node: name,
@@ -1966,7 +1942,7 @@ impl<'a> LoweringContext<'a> {
                          expr: P<hir::Expr>,
                          span: Span,
                          rule: hir::BlockCheckMode,
-                         attrs: ThinAttributes)
+                         attrs: ThinVec<Attribute>)
                          -> P<hir::Expr> {
         let id = self.next_id();
         let block = P(hir::Block {
index bac96c68e4cf82eab0e72aa5970a78df9d19dcda..50e8c6e7ab842e59adbec3e7336a1ec2a62492d7 100644 (file)
@@ -27,9 +27,8 @@ use hir::map::{self, Node};
 use syntax::abi;
 use hir::{Block, FnDecl};
 use syntax::ast::{Attribute, Name, NodeId};
-use syntax::attr::ThinAttributesExt;
 use hir as ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use hir::intravisit::FnKind;
 
 /// An FnLikeNode is a Node that is like a fn, in that it has a decl
@@ -257,11 +256,7 @@ impl<'a> FnLikeNode<'a> {
             }
             map::NodeExpr(e) => match e.node {
                 ast::ExprClosure(_, ref decl, ref block, _fn_decl_span) =>
-                    closure(ClosureParts::new(&decl,
-                                              &block,
-                                              e.id,
-                                              e.span,
-                                              e.attrs.as_attr_slice())),
+                    closure(ClosureParts::new(&decl, &block, e.id, e.span, &e.attrs)),
                 _ => bug!("expr FnLikeNode that is not fn-like"),
             },
             _ => bug!("other FnLikeNode that is not fn-like"),
index 99e5f32e263f2544d4a02ba2569bef18483869ff..693d7a2edfca5cf726d366d74b3e8255bd6bdb2b 100644 (file)
@@ -17,7 +17,7 @@ use hir::def_id::DefId;
 use middle::cstore::InlinedItem;
 use std::iter::repeat;
 use syntax::ast::{NodeId, CRATE_NODE_ID};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 /// A Visitor that walks over the HIR and collects Nodes into a HIR map
 pub struct NodeCollector<'ast> {
@@ -165,7 +165,7 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
     }
 
     fn visit_pat(&mut self, pat: &'ast Pat) {
-        let node = if let PatKind::Ident(..) = pat.node {
+        let node = if let PatKind::Binding(..) = pat.node {
             NodeLocal(pat)
         } else {
             NodePat(pat)
index e783d84dc1b4ad8f9f040e532449a43276e797aa..2b89695ab41cad2fdbb39f846547a314f1ece101 100644 (file)
@@ -25,15 +25,15 @@ pub struct DefCollector<'ast> {
     // If we are walking HIR (c.f., AST), we need to keep a reference to the
     // crate.
     hir_crate: Option<&'ast hir::Crate>,
-    pub definitions: Definitions,
+    definitions: &'ast mut Definitions,
     parent_def: Option<DefIndex>,
 }
 
 impl<'ast> DefCollector<'ast> {
-    pub fn root() -> DefCollector<'ast> {
+    pub fn root(definitions: &'ast mut Definitions) -> DefCollector<'ast> {
         let mut collector = DefCollector {
             hir_crate: None,
-            definitions: Definitions::new(),
+            definitions: definitions,
             parent_def: None,
         };
         let root = collector.create_def_with_parent(None, CRATE_NODE_ID, DefPathData::CrateRoot);
@@ -48,7 +48,7 @@ impl<'ast> DefCollector<'ast> {
     pub fn extend(parent_node: NodeId,
                   parent_def_path: DefPath,
                   parent_def_id: DefId,
-                  definitions: Definitions)
+                  definitions: &'ast mut Definitions)
                   -> DefCollector<'ast> {
         let mut collector = DefCollector {
             hir_crate: None,
@@ -98,7 +98,7 @@ impl<'ast> DefCollector<'ast> {
         self.parent_def = parent;
     }
 
-    fn visit_ast_const_integer(&mut self, expr: &'ast Expr) {
+    fn visit_ast_const_integer(&mut self, expr: &Expr) {
         // Find the node which will be used after lowering.
         if let ExprKind::Paren(ref inner) = expr.node {
             return self.visit_ast_const_integer(inner);
@@ -124,8 +124,8 @@ impl<'ast> DefCollector<'ast> {
     }
 }
 
-impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
-    fn visit_item(&mut self, i: &'ast Item) {
+impl<'ast> visit::Visitor for DefCollector<'ast> {
+    fn visit_item(&mut self, i: &Item) {
         debug!("visit_item: {:?}", i);
 
         // Pick the def data. This need not be unique, but the more
@@ -183,7 +183,7 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
         });
     }
 
-    fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) {
+    fn visit_foreign_item(&mut self, foreign_item: &ForeignItem) {
         let def = self.create_def(foreign_item.id, DefPathData::ValueNs(foreign_item.ident.name));
 
         self.with_parent(def, |this| {
@@ -191,7 +191,7 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
         });
     }
 
-    fn visit_generics(&mut self, generics: &'ast Generics) {
+    fn visit_generics(&mut self, generics: &Generics) {
         for ty_param in generics.ty_params.iter() {
             self.create_def(ty_param.id, DefPathData::TypeParam(ty_param.ident.name));
         }
@@ -199,11 +199,12 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
         visit::walk_generics(self, generics);
     }
 
-    fn visit_trait_item(&mut self, ti: &'ast TraitItem) {
+    fn visit_trait_item(&mut self, ti: &TraitItem) {
         let def_data = match ti.node {
             TraitItemKind::Method(..) | TraitItemKind::Const(..) =>
                 DefPathData::ValueNs(ti.ident.name),
             TraitItemKind::Type(..) => DefPathData::TypeNs(ti.ident.name),
+            TraitItemKind::Macro(..) => DefPathData::MacroDef(ti.ident.name),
         };
 
         let def = self.create_def(ti.id, def_data);
@@ -216,7 +217,7 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
         });
     }
 
-    fn visit_impl_item(&mut self, ii: &'ast ImplItem) {
+    fn visit_impl_item(&mut self, ii: &ImplItem) {
         let def_data = match ii.node {
             ImplItemKind::Method(..) | ImplItemKind::Const(..) =>
                 DefPathData::ValueNs(ii.ident.name),
@@ -234,7 +235,7 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
         });
     }
 
-    fn visit_pat(&mut self, pat: &'ast Pat) {
+    fn visit_pat(&mut self, pat: &Pat) {
         let parent_def = self.parent_def;
 
         if let PatKind::Ident(_, id, _) = pat.node {
@@ -246,7 +247,7 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
         self.parent_def = parent_def;
     }
 
-    fn visit_expr(&mut self, expr: &'ast Expr) {
+    fn visit_expr(&mut self, expr: &Expr) {
         let parent_def = self.parent_def;
 
         if let ExprKind::Repeat(_, ref count) = expr.node {
@@ -262,18 +263,18 @@ impl<'ast> visit::Visitor<'ast> for DefCollector<'ast> {
         self.parent_def = parent_def;
     }
 
-    fn visit_ty(&mut self, ty: &'ast Ty) {
+    fn visit_ty(&mut self, ty: &Ty) {
         if let TyKind::FixedLengthVec(_, ref length) = ty.node {
             self.visit_ast_const_integer(length);
         }
         visit::walk_ty(self, ty);
     }
 
-    fn visit_lifetime_def(&mut self, def: &'ast LifetimeDef) {
+    fn visit_lifetime_def(&mut self, def: &LifetimeDef) {
         self.create_def(def.lifetime.id, DefPathData::LifetimeDef(def.lifetime.name));
     }
 
-    fn visit_macro_def(&mut self, macro_def: &'ast MacroDef) {
+    fn visit_macro_def(&mut self, macro_def: &MacroDef) {
         self.create_def(macro_def.id, DefPathData::MacroDef(macro_def.ident.name));
     }
 }
@@ -396,7 +397,7 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
     fn visit_pat(&mut self, pat: &'ast hir::Pat) {
         let parent_def = self.parent_def;
 
-        if let hir::PatKind::Ident(_, name, _) = pat.node {
+        if let hir::PatKind::Binding(_, name, _) = pat.node {
             let def = self.create_def(pat.id, DefPathData::Binding(name.node));
             self.parent_def = Some(def);
         }
index d66df3e4e8fd274a70a11c0ad65d5e242e493509..3317585f820aa60b30647c97d7b33c1561a7b784 100644 (file)
@@ -10,8 +10,9 @@
 
 use middle::cstore::LOCAL_CRATE;
 use hir::def_id::{DefId, DefIndex};
+use hir::map::def_collector::DefCollector;
 use rustc_data_structures::fnv::FnvHashMap;
-use syntax::ast;
+use syntax::{ast, visit};
 use syntax::parse::token::InternedString;
 use util::nodemap::NodeMap;
 
@@ -189,6 +190,11 @@ impl Definitions {
         }
     }
 
+    pub fn collect(&mut self, krate: &ast::Crate) {
+        let mut def_collector = DefCollector::root(self);
+        visit::walk_crate(&mut def_collector, krate);
+    }
+
     /// Get the number of definitions.
     pub fn len(&self) -> usize {
         self.data.len()
index 2f310806a7420fa068b4d71ffeec5f661627d415..960e32ae99faf0810cdde1ff5fe9adf479c7f7dc 100644 (file)
@@ -23,9 +23,8 @@ use hir::def_id::{CRATE_DEF_INDEX, DefId, DefIndex};
 
 use syntax::abi::Abi;
 use syntax::ast::{self, Name, NodeId, DUMMY_NODE_ID, };
-use syntax::attr::ThinAttributesExt;
-use syntax::codemap::{Span, Spanned};
-use syntax::visit;
+use syntax::codemap::Spanned;
+use syntax_pos::Span;
 
 use hir::*;
 use hir::fold::Folder;
@@ -561,7 +560,7 @@ impl<'ast> Map<'ast> {
             NodeVariant(v) => v.node.name,
             NodeLifetime(lt) => lt.name,
             NodeTyParam(tp) => tp.name,
-            NodeLocal(&Pat { node: PatKind::Ident(_,l,_), .. }) => l.node,
+            NodeLocal(&Pat { node: PatKind::Binding(_,l,_), .. }) => l.node,
             NodeStructCtor(_) => self.name(self.get_parent(id)),
             _ => bug!("no name for {}", self.node_to_string(id))
         }
@@ -577,7 +576,7 @@ impl<'ast> Map<'ast> {
             Some(NodeTraitItem(ref ti)) => Some(&ti.attrs[..]),
             Some(NodeImplItem(ref ii)) => Some(&ii.attrs[..]),
             Some(NodeVariant(ref v)) => Some(&v.node.attrs[..]),
-            Some(NodeExpr(ref e)) => Some(e.attrs.as_attr_slice()),
+            Some(NodeExpr(ref e)) => Some(&*e.attrs),
             Some(NodeStmt(ref s)) => Some(s.node.attrs()),
             // unit/tuple structs take the attributes straight from
             // the struct definition.
@@ -780,12 +779,6 @@ impl<F: FoldOps> Folder for IdAndSpanUpdater<F> {
     }
 }
 
-pub fn collect_definitions<'ast>(krate: &'ast ast::Crate) -> Definitions {
-    let mut def_collector = DefCollector::root();
-    visit::walk_crate(&mut def_collector, krate);
-    def_collector.definitions
-}
-
 pub fn map_crate<'ast>(forest: &'ast mut Forest,
                        definitions: Definitions)
                        -> Map<'ast> {
@@ -842,13 +835,12 @@ pub fn map_decoded_item<'ast, F: FoldOps>(map: &Map<'ast>,
     let ii = map.forest.inlined_items.alloc(ii);
     let ii_parent_id = fld.new_id(DUMMY_NODE_ID);
 
-    let defs = mem::replace(&mut *map.definitions.borrow_mut(), Definitions::new());
+    let defs = &mut *map.definitions.borrow_mut();
     let mut def_collector = DefCollector::extend(ii_parent_id,
                                                  parent_def_path.clone(),
                                                  parent_def_id,
                                                  defs);
     def_collector.walk_item(ii, map.krate());
-    *map.definitions.borrow_mut() = def_collector.definitions;
 
     let mut collector = NodeCollector::extend(map.krate(),
                                               ii,
index 39a6ec9f3af270ed26329079b3a0e9c235ad2211..a139dd152f006f5bcc51d26c0d26a1cb33ea7a21 100644 (file)
@@ -36,13 +36,15 @@ use hir::def::Def;
 use hir::def_id::DefId;
 use util::nodemap::{NodeMap, FnvHashSet};
 
-use syntax::codemap::{self, mk_sp, respan, Span, Spanned, ExpnId};
+use syntax_pos::{mk_sp, Span, ExpnId};
+use syntax::codemap::{self, respan, Spanned};
 use syntax::abi::Abi;
-use syntax::ast::{Name, NodeId, DUMMY_NODE_ID, TokenTree, AsmDialect};
+use syntax::ast::{Name, NodeId, DUMMY_NODE_ID, AsmDialect};
 use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, MetaItem};
-use syntax::attr::{ThinAttributes, ThinAttributesExt};
 use syntax::parse::token::{keywords, InternedString};
 use syntax::ptr::P;
+use syntax::tokenstream::TokenTree;
+use syntax::util::ThinVec;
 
 use std::collections::BTreeMap;
 use std::fmt;
@@ -466,11 +468,11 @@ impl Pat {
         }
 
         match self.node {
-            PatKind::Ident(_, _, Some(ref p)) => p.walk_(it),
+            PatKind::Binding(_, _, Some(ref p)) => p.walk_(it),
             PatKind::Struct(_, ref fields, _) => {
                 fields.iter().all(|field| field.node.pat.walk_(it))
             }
-            PatKind::TupleStruct(_, Some(ref s)) | PatKind::Tup(ref s) => {
+            PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
                 s.iter().all(|p| p.walk_(it))
             }
             PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
@@ -484,8 +486,7 @@ impl Pat {
             PatKind::Wild |
             PatKind::Lit(_) |
             PatKind::Range(_, _) |
-            PatKind::Ident(_, _, _) |
-            PatKind::TupleStruct(..) |
+            PatKind::Binding(..) |
             PatKind::Path(..) |
             PatKind::QPath(_, _) => {
                 true
@@ -525,23 +526,17 @@ pub enum PatKind {
     /// Represents a wildcard pattern (`_`)
     Wild,
 
-    /// A `PatKind::Ident` may either be a new bound variable,
-    /// or a unit struct/variant pattern, or a const pattern (in the last two cases
-    /// the third field must be `None`).
-    ///
-    /// In the unit or const pattern case, the parser can't determine
-    /// which it is. The resolver determines this, and
-    /// records this pattern's `NodeId` in an auxiliary
-    /// set (of "PatIdents that refer to unit patterns or constants").
-    Ident(BindingMode, Spanned<Name>, Option<P<Pat>>),
+    /// A fresh binding `ref mut binding @ OPT_SUBPATTERN`.
+    Binding(BindingMode, Spanned<Name>, Option<P<Pat>>),
 
     /// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`.
     /// The `bool` is `true` in the presence of a `..`.
     Struct(Path, HirVec<Spanned<FieldPat>>, bool),
 
-    /// A tuple struct/variant pattern `Variant(x, y, z)`.
-    /// "None" means a `Variant(..)` pattern where we don't bind the fields to names.
-    TupleStruct(Path, Option<HirVec<P<Pat>>>),
+    /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
+    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    TupleStruct(Path, HirVec<P<Pat>>, Option<usize>),
 
     /// A path pattern.
     /// Such pattern can be resolved to a unit struct/variant or a constant.
@@ -553,8 +548,10 @@ pub enum PatKind {
     /// PatKind::Path, and the resolver will have to sort that out.
     QPath(QSelf, Path),
 
-    /// A tuple pattern `(a, b)`
-    Tup(HirVec<P<Pat>>),
+    /// A tuple pattern `(a, b)`.
+    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    Tuple(HirVec<P<Pat>>, Option<usize>),
     /// A `box` pattern
     Box(P<Pat>),
     /// A reference pattern, e.g. `&mut (a, b)`
@@ -737,7 +734,7 @@ impl Stmt_ {
         match *self {
             StmtDecl(ref d, _) => d.node.attrs(),
             StmtExpr(ref e, _) |
-            StmtSemi(ref e, _) => e.attrs.as_attr_slice(),
+            StmtSemi(ref e, _) => &e.attrs,
         }
     }
 
@@ -761,7 +758,7 @@ pub struct Local {
     pub init: Option<P<Expr>>,
     pub id: NodeId,
     pub span: Span,
-    pub attrs: ThinAttributes,
+    pub attrs: ThinVec<Attribute>,
 }
 
 pub type Decl = Spanned<Decl_>;
@@ -777,7 +774,7 @@ pub enum Decl_ {
 impl Decl_ {
     pub fn attrs(&self) -> &[Attribute] {
         match *self {
-            DeclLocal(ref l) => l.attrs.as_attr_slice(),
+            DeclLocal(ref l) => &l.attrs,
             DeclItem(_) => &[]
         }
     }
@@ -822,7 +819,7 @@ pub struct Expr {
     pub id: NodeId,
     pub node: Expr_,
     pub span: Span,
-    pub attrs: ThinAttributes,
+    pub attrs: ThinVec<Attribute>,
 }
 
 impl fmt::Debug for Expr {
@@ -873,11 +870,11 @@ pub enum Expr_ {
     /// A while loop, with an optional label
     ///
     /// `'label: while expr { block }`
-    ExprWhile(P<Expr>, P<Block>, Option<Name>),
+    ExprWhile(P<Expr>, P<Block>, Option<Spanned<Name>>),
     /// Conditionless loop (can be exited with break, continue, or return)
     ///
     /// `'label: loop { block }`
-    ExprLoop(P<Block>, Option<Name>),
+    ExprLoop(P<Block>, Option<Spanned<Name>>),
     /// A `match` block, with a source that indicates whether or not it is
     /// the result of a desugaring, and if so, which kind.
     ExprMatch(P<Expr>, HirVec<Arm>, MatchSource),
@@ -1142,8 +1139,8 @@ pub type ExplicitSelf = Spanned<SelfKind>;
 
 impl Arg {
     pub fn to_self(&self) -> Option<ExplicitSelf> {
-        if let PatKind::Ident(BindByValue(mutbl), name, _) = self.pat.node {
-            if name.node.unhygienize() == keywords::SelfValue.name() {
+        if let PatKind::Binding(BindByValue(mutbl), name, _) = self.pat.node {
+            if name.node == keywords::SelfValue.name() {
                 return match self.ty.node {
                     TyInfer => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
                     TyRptr(lt, MutTy{ref ty, mutbl}) if ty.node == TyInfer => {
@@ -1158,8 +1155,8 @@ impl Arg {
     }
 
     pub fn is_self(&self) -> bool {
-        if let PatKind::Ident(_, name, _) = self.pat.node {
-            name.node.unhygienize() == keywords::SelfValue.name()
+        if let PatKind::Binding(_, name, _) = self.pat.node {
+            name.node == keywords::SelfValue.name()
         } else {
             false
         }
@@ -1175,6 +1172,9 @@ pub struct FnDecl {
 }
 
 impl FnDecl {
+    pub fn get_self(&self) -> Option<ExplicitSelf> {
+        self.inputs.get(0).and_then(Arg::to_self)
+    }
     pub fn has_self(&self) -> bool {
         self.inputs.get(0).map(Arg::is_self).unwrap_or(false)
     }
index 15f2310607ffe9ea6bb681c96adea4a511076a4a..3bb9b6d260255065bee28fc7986777812297d13e 100644 (file)
 
 use hir::def::*;
 use hir::def_id::DefId;
+use hir::{self, PatKind};
 use ty::TyCtxt;
 use util::nodemap::FnvHashMap;
-
 use syntax::ast;
-use hir::{self, PatKind};
-use syntax::codemap::{respan, Span, Spanned, DUMMY_SP};
+use syntax::codemap::Spanned;
+use syntax_pos::{Span, DUMMY_SP};
 
-use std::cell::RefCell;
+use std::iter::{Enumerate, ExactSizeIterator};
 
 pub type PatIdMap = FnvHashMap<ast::Name, ast::NodeId>;
 
-// This is used because same-named variables in alternative patterns need to
-// use the NodeId of their namesake in the first pattern.
-pub fn pat_id_map(dm: &RefCell<DefMap>, pat: &hir::Pat) -> PatIdMap {
-    let mut map = FnvHashMap();
-    pat_bindings(dm, pat, |_bm, p_id, _s, path1| {
-        map.insert(path1.node, p_id);
-    });
-    map
+pub struct EnumerateAndAdjust<I> {
+    enumerate: Enumerate<I>,
+    gap_pos: usize,
+    gap_len: usize,
+}
+
+impl<I> Iterator for EnumerateAndAdjust<I> where I: Iterator {
+    type Item = (usize, <I as Iterator>::Item);
+
+    fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
+        self.enumerate.next().map(|(i, elem)| {
+            (if i < self.gap_pos { i } else { i + self.gap_len }, elem)
+        })
+    }
+}
+
+pub trait EnumerateAndAdjustIterator {
+    fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option<usize>)
+        -> EnumerateAndAdjust<Self> where Self: Sized;
+}
+
+impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
+    fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option<usize>)
+            -> EnumerateAndAdjust<Self> where Self: Sized {
+        let actual_len = self.len();
+        EnumerateAndAdjust {
+            enumerate: self.enumerate(),
+            gap_pos: if let Some(gap_pos) = gap_pos { gap_pos } else { expected_len },
+            gap_len: expected_len - actual_len,
+        }
+    }
 }
 
 pub fn pat_is_refutable(dm: &DefMap, pat: &hir::Pat) -> bool {
@@ -36,7 +59,6 @@ pub fn pat_is_refutable(dm: &DefMap, pat: &hir::Pat) -> bool {
         PatKind::Lit(_) | PatKind::Range(_, _) | PatKind::QPath(..) => true,
         PatKind::TupleStruct(..) |
         PatKind::Path(..) |
-        PatKind::Ident(_, _, None) |
         PatKind::Struct(..) => {
             match dm.get(&pat.id).map(|d| d.full_def()) {
                 Some(Def::Variant(..)) => true,
@@ -52,7 +74,6 @@ pub fn pat_is_variant_or_struct(dm: &DefMap, pat: &hir::Pat) -> bool {
     match pat.node {
         PatKind::TupleStruct(..) |
         PatKind::Path(..) |
-        PatKind::Ident(_, _, None) |
         PatKind::Struct(..) => {
             match dm.get(&pat.id).map(|d| d.full_def()) {
                 Some(Def::Variant(..)) | Some(Def::Struct(..)) | Some(Def::TyAlias(..)) => true,
@@ -65,7 +86,7 @@ pub fn pat_is_variant_or_struct(dm: &DefMap, pat: &hir::Pat) -> bool {
 
 pub fn pat_is_const(dm: &DefMap, pat: &hir::Pat) -> bool {
     match pat.node {
-        PatKind::Ident(_, _, None) | PatKind::Path(..) | PatKind::QPath(..) => {
+        PatKind::Path(..) | PatKind::QPath(..) => {
             match dm.get(&pat.id).map(|d| d.full_def()) {
                 Some(Def::Const(..)) | Some(Def::AssociatedConst(..)) => true,
                 _ => false
@@ -79,7 +100,7 @@ pub fn pat_is_const(dm: &DefMap, pat: &hir::Pat) -> bool {
 // returned instead of a panic.
 pub fn pat_is_resolved_const(dm: &DefMap, pat: &hir::Pat) -> bool {
     match pat.node {
-        PatKind::Ident(_, _, None) | PatKind::Path(..) | PatKind::QPath(..) => {
+        PatKind::Path(..) | PatKind::QPath(..) => {
             match dm.get(&pat.id)
                     .and_then(|d| if d.depth == 0 { Some(d.base_def) }
                                   else { None } ) {
@@ -91,35 +112,14 @@ pub fn pat_is_resolved_const(dm: &DefMap, pat: &hir::Pat) -> bool {
     }
 }
 
-pub fn pat_is_binding(dm: &DefMap, pat: &hir::Pat) -> bool {
-    match pat.node {
-        PatKind::Ident(..) => {
-            !pat_is_variant_or_struct(dm, pat) &&
-            !pat_is_const(dm, pat)
-        }
-        _ => false
-    }
-}
-
-pub fn pat_is_binding_or_wild(dm: &DefMap, pat: &hir::Pat) -> bool {
-    match pat.node {
-        PatKind::Ident(..) => pat_is_binding(dm, pat),
-        PatKind::Wild => true,
-        _ => false
-    }
-}
-
-/// Call `it` on every "binding" in a pattern, e.g., on `a` in
+/// Call `f` on every "binding" in a pattern, e.g., on `a` in
 /// `match foo() { Some(a) => (), None => () }`
-pub fn pat_bindings<I>(dm: &RefCell<DefMap>, pat: &hir::Pat, mut it: I) where
-    I: FnMut(hir::BindingMode, ast::NodeId, Span, &Spanned<ast::Name>),
+pub fn pat_bindings<F>(pat: &hir::Pat, mut f: F)
+    where F: FnMut(hir::BindingMode, ast::NodeId, Span, &Spanned<ast::Name>),
 {
     pat.walk(|p| {
-        match p.node {
-          PatKind::Ident(binding_mode, ref pth, _) if pat_is_binding(&dm.borrow(), p) => {
-            it(binding_mode, p.id, p.span, &respan(pth.span, pth.node));
-          }
-          _ => {}
+        if let PatKind::Binding(binding_mode, ref pth, _) = p.node {
+            f(binding_mode, p.id, p.span, pth);
         }
         true
     });
@@ -127,10 +127,10 @@ pub fn pat_bindings<I>(dm: &RefCell<DefMap>, pat: &hir::Pat, mut it: I) where
 
 /// Checks if the pattern contains any patterns that bind something to
 /// an ident, e.g. `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
-pub fn pat_contains_bindings(dm: &DefMap, pat: &hir::Pat) -> bool {
+pub fn pat_contains_bindings(pat: &hir::Pat) -> bool {
     let mut contains_bindings = false;
     pat.walk(|p| {
-        if pat_is_binding(dm, p) {
+        if let PatKind::Binding(..) = p.node {
             contains_bindings = true;
             false // there's at least one binding, can short circuit now.
         } else {
@@ -142,18 +142,15 @@ pub fn pat_contains_bindings(dm: &DefMap, pat: &hir::Pat) -> bool {
 
 /// Checks if the pattern contains any `ref` or `ref mut` bindings,
 /// and if yes whether its containing mutable ones or just immutables ones.
-pub fn pat_contains_ref_binding(dm: &RefCell<DefMap>, pat: &hir::Pat) -> Option<hir::Mutability> {
+pub fn pat_contains_ref_binding(pat: &hir::Pat) -> Option<hir::Mutability> {
     let mut result = None;
-    pat_bindings(dm, pat, |mode, _, _, _| {
-        match mode {
-            hir::BindingMode::BindByRef(m) => {
-                // Pick Mutable as maximum
-                match result {
-                    None | Some(hir::MutImmutable) => result = Some(m),
-                    _ => (),
-                }
+    pat_bindings(pat, |mode, _, _, _| {
+        if let hir::BindingMode::BindByRef(m) = mode {
+            // Pick Mutable as maximum
+            match result {
+                None | Some(hir::MutImmutable) => result = Some(m),
+                _ => (),
             }
-            hir::BindingMode::BindByValue(_) => { }
         }
     });
     result
@@ -161,9 +158,9 @@ pub fn pat_contains_ref_binding(dm: &RefCell<DefMap>, pat: &hir::Pat) -> Option<
 
 /// Checks if the patterns for this arm contain any `ref` or `ref mut`
 /// bindings, and if yes whether its containing mutable ones or just immutables ones.
-pub fn arm_contains_ref_binding(dm: &RefCell<DefMap>, arm: &hir::Arm) -> Option<hir::Mutability> {
+pub fn arm_contains_ref_binding(arm: &hir::Arm) -> Option<hir::Mutability> {
     arm.pats.iter()
-            .filter_map(|pat| pat_contains_ref_binding(dm, pat))
+            .filter_map(|pat| pat_contains_ref_binding(pat))
             .max_by_key(|m| match *m {
                 hir::MutMutable => 1,
                 hir::MutImmutable => 0,
@@ -172,14 +169,15 @@ pub fn arm_contains_ref_binding(dm: &RefCell<DefMap>, arm: &hir::Arm) -> Option<
 
 /// Checks if the pattern contains any patterns that bind something to
 /// an ident or wildcard, e.g. `foo`, or `Foo(_)`, `foo @ Bar(..)`,
-pub fn pat_contains_bindings_or_wild(dm: &DefMap, pat: &hir::Pat) -> bool {
+pub fn pat_contains_bindings_or_wild(pat: &hir::Pat) -> bool {
     let mut contains_bindings = false;
     pat.walk(|p| {
-        if pat_is_binding_or_wild(dm, p) {
-            contains_bindings = true;
-            false // there's at least one binding/wildcard, can short circuit now.
-        } else {
-            true
+        match p.node {
+            PatKind::Binding(..) | PatKind::Wild => {
+                contains_bindings = true;
+                false // there's at least one binding/wildcard, can short circuit now.
+            }
+            _ => true
         }
     });
     contains_bindings
@@ -187,7 +185,7 @@ pub fn pat_contains_bindings_or_wild(dm: &DefMap, pat: &hir::Pat) -> bool {
 
 pub fn simple_name<'a>(pat: &'a hir::Pat) -> Option<ast::Name> {
     match pat.node {
-        PatKind::Ident(hir::BindByValue(_), ref path1, None) => {
+        PatKind::Binding(hir::BindByValue(..), ref path1, None) => {
             Some(path1.node)
         }
         _ => {
@@ -207,7 +205,6 @@ pub fn necessary_variants(dm: &DefMap, pat: &hir::Pat) -> Vec<DefId> {
         match p.node {
             PatKind::TupleStruct(..) |
             PatKind::Path(..) |
-            PatKind::Ident(_, _, None) |
             PatKind::Struct(..) => {
                 match dm.get(&p.id) {
                     Some(&PathResolution { base_def: Def::Variant(_, id), .. }) => {
index 4455c7da3ba3f568c25ce954eae938954dd5ebd2..bf6188faa2fbdf160d7c60032e0bb8613c186042 100644 (file)
@@ -12,8 +12,7 @@ pub use self::AnnNode::*;
 
 use syntax::abi::Abi;
 use syntax::ast;
-use syntax::codemap::{self, CodeMap, BytePos, Spanned};
-use syntax::errors;
+use syntax::codemap::{CodeMap, Spanned};
 use syntax::parse::token::{self, keywords, BinOpToken};
 use syntax::parse::lexer::comments;
 use syntax::print::pp::{self, break_offset, word, space, hardbreak};
@@ -21,6 +20,8 @@ use syntax::print::pp::{Breaks, eof};
 use syntax::print::pp::Breaks::{Consistent, Inconsistent};
 use syntax::print::pprust::{self as ast_pp, PrintState};
 use syntax::ptr::P;
+use syntax_pos::{self, BytePos};
+use errors;
 
 use hir;
 use hir::{Crate, PatKind, RegionTyParamBound, SelfKind, TraitTyParamBound, TraitBoundModifier};
@@ -368,11 +369,11 @@ impl<'a> State<'a> {
         self.end() // close the head-box
     }
 
-    pub fn bclose_(&mut self, span: codemap::Span, indented: usize) -> io::Result<()> {
+    pub fn bclose_(&mut self, span: syntax_pos::Span, indented: usize) -> io::Result<()> {
         self.bclose_maybe_open(span, indented, true)
     }
     pub fn bclose_maybe_open(&mut self,
-                             span: codemap::Span,
+                             span: syntax_pos::Span,
                              indented: usize,
                              close_box: bool)
                              -> io::Result<()> {
@@ -384,7 +385,7 @@ impl<'a> State<'a> {
         }
         Ok(())
     }
-    pub fn bclose(&mut self, span: codemap::Span) -> io::Result<()> {
+    pub fn bclose(&mut self, span: syntax_pos::Span) -> io::Result<()> {
         self.bclose_(span, indent_unit)
     }
 
@@ -432,7 +433,7 @@ impl<'a> State<'a> {
                                   mut get_span: G)
                                   -> io::Result<()>
         where F: FnMut(&mut State, &T) -> io::Result<()>,
-              G: FnMut(&T) -> codemap::Span
+              G: FnMut(&T) -> syntax_pos::Span
     {
         self.rbox(0, b)?;
         let len = elts.len();
@@ -859,7 +860,7 @@ impl<'a> State<'a> {
                           enum_definition: &hir::EnumDef,
                           generics: &hir::Generics,
                           name: ast::Name,
-                          span: codemap::Span,
+                          span: syntax_pos::Span,
                           visibility: &hir::Visibility)
                           -> io::Result<()> {
         self.head(&visibility_qualified(visibility, "enum"))?;
@@ -872,7 +873,7 @@ impl<'a> State<'a> {
 
     pub fn print_variants(&mut self,
                           variants: &[hir::Variant],
-                          span: codemap::Span)
+                          span: syntax_pos::Span)
                           -> io::Result<()> {
         self.bopen()?;
         for v in variants {
@@ -902,7 +903,7 @@ impl<'a> State<'a> {
                         struct_def: &hir::VariantData,
                         generics: &hir::Generics,
                         name: ast::Name,
-                        span: codemap::Span,
+                        span: syntax_pos::Span,
                         print_finalizer: bool)
                         -> io::Result<()> {
         self.print_name(name)?;
@@ -911,8 +912,9 @@ impl<'a> State<'a> {
             if struct_def.is_tuple() {
                 self.popen()?;
                 self.commasep(Inconsistent, struct_def.fields(), |s, field| {
-                    s.print_visibility(&field.vis)?;
                     s.maybe_print_comment(field.span.lo)?;
+                    s.print_outer_attributes(&field.attrs)?;
+                    s.print_visibility(&field.vis)?;
                     s.print_type(&field.ty)
                 })?;
                 self.pclose()?;
@@ -1351,9 +1353,9 @@ impl<'a> State<'a> {
             hir::ExprIf(ref test, ref blk, ref elseopt) => {
                 self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e))?;
             }
-            hir::ExprWhile(ref test, ref blk, opt_name) => {
-                if let Some(name) = opt_name {
-                    self.print_name(name)?;
+            hir::ExprWhile(ref test, ref blk, opt_sp_name) => {
+                if let Some(sp_name) = opt_sp_name {
+                    self.print_name(sp_name.node)?;
                     self.word_space(":")?;
                 }
                 self.head("while")?;
@@ -1361,9 +1363,9 @@ impl<'a> State<'a> {
                 space(&mut self.s)?;
                 self.print_block(&blk)?;
             }
-            hir::ExprLoop(ref blk, opt_name) => {
-                if let Some(name) = opt_name {
-                    self.print_name(name)?;
+            hir::ExprLoop(ref blk, opt_sp_name) => {
+                if let Some(sp_name) = opt_sp_name {
+                    self.print_name(sp_name.node)?;
                     self.word_space(":")?;
                 }
                 self.head("loop")?;
@@ -1695,13 +1697,10 @@ impl<'a> State<'a> {
                 self.commasep(Inconsistent, &data.inputs, |s, ty| s.print_type(&ty))?;
                 word(&mut self.s, ")")?;
 
-                match data.output {
-                    None => {}
-                    Some(ref ty) => {
-                        self.space_if_not_bol()?;
-                        self.word_space("->")?;
-                        self.print_type(&ty)?;
-                    }
+                if let Some(ref ty) = data.output {
+                    self.space_if_not_bol()?;
+                    self.word_space("->")?;
+                    self.print_type(&ty)?;
                 }
             }
         }
@@ -1716,7 +1715,7 @@ impl<'a> State<'a> {
         // is that it doesn't matter
         match pat.node {
             PatKind::Wild => word(&mut self.s, "_")?,
-            PatKind::Ident(binding_mode, ref path1, ref sub) => {
+            PatKind::Binding(binding_mode, ref path1, ref sub) => {
                 match binding_mode {
                     hir::BindByRef(mutbl) => {
                         self.word_nbsp("ref")?;
@@ -1728,24 +1727,28 @@ impl<'a> State<'a> {
                     }
                 }
                 self.print_name(path1.node)?;
-                match *sub {
-                    Some(ref p) => {
-                        word(&mut self.s, "@")?;
-                        self.print_pat(&p)?;
-                    }
-                    None => (),
+                if let Some(ref p) = *sub {
+                    word(&mut self.s, "@")?;
+                    self.print_pat(&p)?;
                 }
             }
-            PatKind::TupleStruct(ref path, ref args_) => {
+            PatKind::TupleStruct(ref path, ref elts, ddpos) => {
                 self.print_path(path, true, 0)?;
-                match *args_ {
-                    None => word(&mut self.s, "(..)")?,
-                    Some(ref args) => {
-                        self.popen()?;
-                        self.commasep(Inconsistent, &args[..], |s, p| s.print_pat(&p))?;
-                        self.pclose()?;
+                self.popen()?;
+                if let Some(ddpos) = ddpos {
+                    self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?;
+                    if ddpos != 0 {
+                        self.word_space(",")?;
                     }
+                    word(&mut self.s, "..")?;
+                    if ddpos != elts.len() {
+                        word(&mut self.s, ",")?;
+                        self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?;
+                    }
+                } else {
+                    try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
                 }
+                try!(self.pclose());
             }
             PatKind::Path(ref path) => {
                 self.print_path(path, true, 0)?;
@@ -1778,11 +1781,23 @@ impl<'a> State<'a> {
                 space(&mut self.s)?;
                 word(&mut self.s, "}")?;
             }
-            PatKind::Tup(ref elts) => {
+            PatKind::Tuple(ref elts, ddpos) => {
                 self.popen()?;
-                self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?;
-                if elts.len() == 1 {
-                    word(&mut self.s, ",")?;
+                if let Some(ddpos) = ddpos {
+                    self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p))?;
+                    if ddpos != 0 {
+                        self.word_space(",")?;
+                    }
+                    word(&mut self.s, "..")?;
+                    if ddpos != elts.len() {
+                        word(&mut self.s, ",")?;
+                        self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p))?;
+                    }
+                } else {
+                    self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p))?;
+                    if elts.len() == 1 {
+                        word(&mut self.s, ",")?;
+                    }
                 }
                 self.pclose()?;
             }
@@ -2151,7 +2166,7 @@ impl<'a> State<'a> {
                 if let Some(eself) = input.to_self() {
                     self.print_explicit_self(&eself)?;
                 } else {
-                    let invalid = if let PatKind::Ident(_, name, _) = input.pat.node {
+                    let invalid = if let PatKind::Binding(_, name, _) = input.pat.node {
                         name.node == keywords::Invalid.name()
                     } else {
                         false
@@ -2220,32 +2235,28 @@ impl<'a> State<'a> {
     }
 
     pub fn maybe_print_trailing_comment(&mut self,
-                                        span: codemap::Span,
+                                        span: syntax_pos::Span,
                                         next_pos: Option<BytePos>)
                                         -> io::Result<()> {
         let cm = match self.cm {
             Some(cm) => cm,
             _ => return Ok(()),
         };
-        match self.next_comment() {
-            Some(ref cmnt) => {
-                if (*cmnt).style != comments::Trailing {
-                    return Ok(());
-                }
-                let span_line = cm.lookup_char_pos(span.hi);
-                let comment_line = cm.lookup_char_pos((*cmnt).pos);
-                let mut next = (*cmnt).pos + BytePos(1);
-                match next_pos {
-                    None => (),
-                    Some(p) => next = p,
-                }
-                if span.hi < (*cmnt).pos && (*cmnt).pos < next &&
-                   span_line.line == comment_line.line {
-                    self.print_comment(cmnt)?;
-                    self.cur_cmnt_and_lit.cur_cmnt += 1;
-                }
+        if let Some(ref cmnt) = self.next_comment() {
+            if (*cmnt).style != comments::Trailing {
+                return Ok(());
+            }
+            let span_line = cm.lookup_char_pos(span.hi);
+            let comment_line = cm.lookup_char_pos((*cmnt).pos);
+            let mut next = (*cmnt).pos + BytePos(1);
+            if let Some(p) = next_pos {
+                next = p;
+            }
+            if span.hi < (*cmnt).pos && (*cmnt).pos < next &&
+               span_line.line == comment_line.line {
+                self.print_comment(cmnt)?;
+                self.cur_cmnt_and_lit.cur_cmnt += 1;
             }
-            _ => (),
         }
         Ok(())
     }
index e2f27074dbfc9206a2fe466a0546ef53e9fa4b82..c9235d063cba0981826648b5a7616d2ffd2c0998 100644 (file)
@@ -49,7 +49,7 @@ use ty::relate::{RelateResult, TypeRelation};
 use traits::PredicateObligations;
 
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 #[derive(Clone)]
 pub struct CombineFields<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
@@ -337,8 +337,10 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx
 
     fn fold_region(&mut self, r: ty::Region) -> ty::Region {
         match r {
-            // Never make variables for regions bound within the type itself.
-            ty::ReLateBound(..) => { return r; }
+            // Never make variables for regions bound within the type itself,
+            // nor for erased regions.
+            ty::ReLateBound(..) |
+            ty::ReErased => { return r; }
 
             // Early-bound regions should really have been substituted away before
             // we get to this point.
index 8afee54c4bc683fb07ccb97942678ef70da17e8f..894044296cbd6ecaf1816a3585faf7edc7424f5e 100644 (file)
@@ -77,6 +77,7 @@ use hir::map as ast_map;
 use hir;
 use hir::print as pprust;
 
+use lint;
 use hir::def::Def;
 use hir::def_id::DefId;
 use infer::{self, TypeOrigin};
@@ -90,10 +91,10 @@ use std::cell::{Cell, RefCell};
 use std::char::from_u32;
 use std::fmt;
 use syntax::ast;
-use syntax::errors::{DiagnosticBuilder, check_old_skool};
-use syntax::codemap::{self, Pos, Span};
 use syntax::parse::token;
 use syntax::ptr::P;
+use syntax_pos::{self, Pos, Span};
+use errors::{DiagnosticBuilder, check_old_skool};
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn note_and_explain_region(self,
@@ -215,7 +216,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             //
             // We shouldn't really be having unification failures with ReVar
             // and ReLateBound though.
-            ty::ReSkolemized(..) | ty::ReVar(_) | ty::ReLateBound(..) => {
+            ty::ReSkolemized(..) |
+            ty::ReVar(_) |
+            ty::ReLateBound(..) |
+            ty::ReErased => {
                 (format!("lifetime {:?}", region), None)
             }
         };
@@ -1017,6 +1021,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         let (fn_decl, generics) = rebuilder.rebuild();
         self.give_expl_lifetime_param(err, &fn_decl, unsafety, constness, name, &generics, span);
     }
+
+    pub fn issue_32330_warnings(&self, span: Span, issue32330s: &[ty::Issue32330]) {
+        for issue32330 in issue32330s {
+            match *issue32330 {
+                ty::Issue32330::WontChange => { }
+                ty::Issue32330::WillChange { fn_def_id, region_name } => {
+                    self.tcx.sess.add_lint(
+                        lint::builtin::HR_LIFETIME_IN_ASSOC_TYPE,
+                        ast::CRATE_NODE_ID,
+                        span,
+                        format!("lifetime parameter `{0}` declared on fn `{1}` \
+                                 appears only in the return type, \
+                                 but here is required to be higher-ranked, \
+                                 which means that `{0}` must appear in both \
+                                 argument and return types",
+                                region_name,
+                                self.tcx.item_path_str(fn_def_id)));
+                }
+            }
+        }
+    }
 }
 
 struct RebuildPathInfo<'a> {
@@ -1129,7 +1154,7 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
                 ty::BrAnon(i) => {
                     anon_nums.insert(i);
                 }
-                ty::BrNamed(_, name) => {
+                ty::BrNamed(_, name, _) => {
                     region_names.insert(name);
                 }
                 _ => ()
@@ -1143,7 +1168,7 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
         for sr in self.same_regions {
             for br in &sr.regions {
                 match *br {
-                    ty::BrNamed(_, name) => {
+                    ty::BrNamed(_, name, _) => {
                         all_region_names.insert(name);
                     }
                     _ => ()
@@ -1332,17 +1357,7 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
                     ty_queue.push(&mut_ty.ty);
                 }
                 hir::TyPath(ref maybe_qself, ref path) => {
-                    let a_def = match self.tcx.def_map.borrow().get(&cur_ty.id) {
-                        None => {
-                            self.tcx
-                                .sess
-                                .fatal(&format!(
-                                        "unbound path {}",
-                                        pprust::path_to_string(path)))
-                        }
-                        Some(d) => d.full_def()
-                    };
-                    match a_def {
+                    match self.tcx.expect_def(cur_ty.id) {
                         Def::Enum(did) | Def::TyAlias(did) | Def::Struct(did) => {
                             let generics = self.tcx.lookup_item_type(did).generics;
 
@@ -1841,11 +1856,10 @@ fn lifetimes_in_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
         },
         None => None
     };
-    if method_id_opt.is_some() {
-        let method_id = method_id_opt.unwrap();
+    if let Some(method_id) = method_id_opt {
         let parent = tcx.map.get_parent(method_id);
-        match tcx.map.find(parent) {
-            Some(node) => match node {
+        if let Some(node) = tcx.map.find(parent) {
+            match node {
                 ast_map::NodeItem(item) => match item.node {
                     hir::ItemImpl(_, _, ref gen, _, _, _) => {
                         taken.extend_from_slice(&gen.lifetimes);
@@ -1853,8 +1867,7 @@ fn lifetimes_in_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                     _ => ()
                 },
                 _ => ()
-            },
-            None => ()
+            }
         }
     }
     return taken;
@@ -1920,6 +1933,6 @@ impl LifeGiver {
 
 fn name_to_dummy_lifetime(name: ast::Name) -> hir::Lifetime {
     hir::Lifetime { id: ast::DUMMY_NODE_ID,
-                    span: codemap::DUMMY_SP,
+                    span: syntax_pos::DUMMY_SP,
                     name: name }
 }
index 5ded6dc73646ea04f5920446cdfb9385693d8b82..880661a882af3c38e114390d6d39226b5eccd020 100644 (file)
@@ -23,7 +23,7 @@
 //! error messages or in any other form. Freshening is only really useful as an internal detail.
 //!
 //! __An important detail concerning regions.__ The freshener also replaces *all* regions with
-//! 'static. The reason behind this is that, in general, we do not take region relationships into
+//! 'erased. The reason behind this is that, in general, we do not take region relationships into
 //! account when making type-overloaded decisions. This is important because of the design of the
 //! region inferencer, which is not based on unification but rather on accumulating and then
 //! solving a set of constraints. In contrast, the type inferencer assigns a value to each type
@@ -96,9 +96,10 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
             ty::ReScope(_) |
             ty::ReVar(_) |
             ty::ReSkolemized(..) |
-            ty::ReEmpty => {
-                // replace all free regions with 'static
-                ty::ReStatic
+            ty::ReEmpty |
+            ty::ReErased => {
+                // replace all free regions with 'erased
+                ty::ReErased
             }
         }
     }
index 6814d50107f6848fb95faab14abd786690698963..03a09917c5343bd1fce8783e2c1a524565290531 100644 (file)
 //! Helper routines for higher-ranked things. See the `doc` module at
 //! the end of the file for details.
 
-use super::{CombinedSnapshot, InferCtxt, HigherRankedType, SkolemizationMap};
+use super::{CombinedSnapshot,
+            InferCtxt,
+            LateBoundRegion,
+            HigherRankedType,
+            SubregionOrigin,
+            SkolemizationMap};
 use super::combine::CombineFields;
+use super::region_inference::{TaintDirections};
 
 use ty::{self, TyCtxt, Binder, TypeFoldable};
 use ty::error::TypeError;
 use ty::relate::{Relate, RelateResult, TypeRelation};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use util::nodemap::{FnvHashMap, FnvHashSet};
 
+pub struct HrMatchResult<U> {
+    pub value: U,
+
+    /// Normally, when we do a higher-ranked match operation, we
+    /// expect all higher-ranked regions to be constrained as part of
+    /// the match operation. However, in the transition period for
+    /// #32330, it can happen that we sometimes have unconstrained
+    /// regions that get instantiated with fresh variables. In that
+    /// case, we collect the set of unconstrained bound regions here
+    /// and replace them with fresh variables.
+    pub unconstrained_regions: Vec<ty::BoundRegion>,
+}
+
 impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
     pub fn higher_ranked_sub<T>(&self, a: &Binder<T>, b: &Binder<T>)
                                 -> RelateResult<'tcx, Binder<T>>
@@ -39,11 +58,13 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         // Start a snapshot so we can examine "all bindings that were
         // created as part of this type comparison".
         return self.infcx.commit_if_ok(|snapshot| {
+            let span = self.trace.origin.span();
+
             // First, we instantiate each bound region in the subtype with a fresh
             // region variable.
             let (a_prime, _) =
                 self.infcx.replace_late_bound_regions_with_fresh_var(
-                    self.trace.origin.span(),
+                    span,
                     HigherRankedType,
                     a);
 
@@ -60,7 +81,11 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
 
             // Presuming type comparison succeeds, we need to check
             // that the skolemized regions do not "leak".
-            self.infcx.leak_check(!self.a_is_expected, &skol_map, snapshot)?;
+            self.infcx.leak_check(!self.a_is_expected, span, &skol_map, snapshot)?;
+
+            // We are finished with the skolemized regions now so pop
+            // them off.
+            self.infcx.pop_skolemized(skol_map, snapshot);
 
             debug!("higher_ranked_sub: OK result={:?}", result);
 
@@ -68,6 +93,134 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
         });
     }
 
+    /// The value consists of a pair `(t, u)` where `t` is the
+    /// *matcher* and `u` is a *value*. The idea is to find a
+    /// substitution `S` such that `S(t) == b`, and then return
+    /// `S(u)`. In other words, find values for the late-bound regions
+    /// in `a` that can make `t == b` and then replace the LBR in `u`
+    /// with those values.
+    ///
+    /// This routine is (as of this writing) used in trait matching,
+    /// particularly projection.
+    ///
+    /// NB. It should not happen that there are LBR appearing in `U`
+    /// that do not appear in `T`. If that happens, those regions are
+    /// unconstrained, and this routine replaces them with `'static`.
+    pub fn higher_ranked_match<T, U>(&self,
+                                     span: Span,
+                                     a_pair: &Binder<(T, U)>,
+                                     b_match: &T)
+                                     -> RelateResult<'tcx, HrMatchResult<U>>
+        where T: Relate<'tcx>,
+              U: TypeFoldable<'tcx>
+    {
+        debug!("higher_ranked_match(a={:?}, b={:?})",
+               a_pair, b_match);
+
+        // Start a snapshot so we can examine "all bindings that were
+        // created as part of this type comparison".
+        return self.infcx.commit_if_ok(|snapshot| {
+            // First, we instantiate each bound region in the matcher
+            // with a skolemized region.
+            let ((a_match, a_value), skol_map) =
+                self.infcx.skolemize_late_bound_regions(a_pair, snapshot);
+
+            debug!("higher_ranked_match: a_match={:?}", a_match);
+            debug!("higher_ranked_match: skol_map={:?}", skol_map);
+
+            // Equate types now that bound regions have been replaced.
+            try!(self.equate().relate(&a_match, &b_match));
+
+            // Map each skolemized region to a vector of other regions that it
+            // must be equated with. (Note that this vector may include other
+            // skolemized regions from `skol_map`.)
+            let skol_resolution_map: FnvHashMap<_, _> =
+                skol_map
+                .iter()
+                .map(|(&br, &skol)| {
+                    let tainted_regions =
+                        self.infcx.tainted_regions(snapshot,
+                                                   skol,
+                                                   TaintDirections::incoming()); // [1]
+
+                    // [1] this routine executes after the skolemized
+                    // regions have been *equated* with something
+                    // else, so examining the incoming edges ought to
+                    // be enough to collect all constraints
+
+                    (skol, (br, tainted_regions))
+                })
+                .collect();
+
+            // For each skolemized region, pick a representative -- which can
+            // be any region from the sets above, except for other members of
+            // `skol_map`. There should always be a representative if things
+            // are properly well-formed.
+            let mut unconstrained_regions = vec![];
+            let skol_representatives: FnvHashMap<_, _> =
+                skol_resolution_map
+                .iter()
+                .map(|(&skol, &(br, ref regions))| {
+                    let representative =
+                        regions.iter()
+                               .filter(|r| !skol_resolution_map.contains_key(r))
+                               .cloned()
+                               .next()
+                               .unwrap_or_else(|| { // [1]
+                                   unconstrained_regions.push(br);
+                                   self.infcx.next_region_var(
+                                       LateBoundRegion(span, br, HigherRankedType))
+                               });
+
+                    // [1] There should always be a representative,
+                    // unless the higher-ranked region did not appear
+                    // in the values being matched. We should reject
+                    // as ill-formed cases that can lead to this, but
+                    // right now we sometimes issue warnings (see
+                    // #32330).
+
+                    (skol, representative)
+                })
+                .collect();
+
+            // Equate all the members of each skolemization set with the
+            // representative.
+            for (skol, &(_br, ref regions)) in &skol_resolution_map {
+                let representative = &skol_representatives[skol];
+                debug!("higher_ranked_match: \
+                        skol={:?} representative={:?} regions={:?}",
+                       skol, representative, regions);
+                for region in regions.iter()
+                                     .filter(|&r| !skol_resolution_map.contains_key(r))
+                                     .filter(|&r| r != representative)
+                {
+                    let origin = SubregionOrigin::Subtype(self.trace.clone());
+                    self.infcx.region_vars.make_eqregion(origin,
+                                                         *representative,
+                                                         *region);
+                }
+            }
+
+            // Replace the skolemized regions appearing in value with
+            // their representatives
+            let a_value =
+                fold_regions_in(
+                    self.tcx(),
+                    &a_value,
+                    |r, _| skol_representatives.get(&r).cloned().unwrap_or(r));
+
+            debug!("higher_ranked_match: value={:?}", a_value);
+
+            // We are now done with these skolemized variables.
+            self.infcx.pop_skolemized(skol_map, snapshot);
+
+            Ok(HrMatchResult {
+                value: a_value,
+                unconstrained_regions: unconstrained_regions,
+            })
+        });
+    }
+
     pub fn higher_ranked_lub<T>(&self, a: &Binder<T>, b: &Binder<T>)
                                 -> RelateResult<'tcx, Binder<T>>
         where T: Relate<'tcx>
@@ -124,7 +277,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
                 return r0;
             }
 
-            let tainted = infcx.tainted_regions(snapshot, r0);
+            let tainted = infcx.tainted_regions(snapshot, r0, TaintDirections::both());
 
             // Variables created during LUB computation which are
             // *related* to regions that pre-date the LUB computation
@@ -219,7 +372,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
                 return r0;
             }
 
-            let tainted = infcx.tainted_regions(snapshot, r0);
+            let tainted = infcx.tainted_regions(snapshot, r0, TaintDirections::both());
 
             let mut a_r = None;
             let mut b_r = None;
@@ -341,8 +494,12 @@ fn fold_regions_in<'a, 'gcx, 'tcx, T, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
 }
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
-    fn tainted_regions(&self, snapshot: &CombinedSnapshot, r: ty::Region) -> Vec<ty::Region> {
-        self.region_vars.tainted(&snapshot.region_vars_snapshot, r)
+    fn tainted_regions(&self,
+                       snapshot: &CombinedSnapshot,
+                       r: ty::Region,
+                       directions: TaintDirections)
+                       -> FnvHashSet<ty::Region> {
+        self.region_vars.tainted(&snapshot.region_vars_snapshot, r, directions)
     }
 
     fn region_vars_confined_to_snapshot(&self,
@@ -422,22 +579,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         region_vars
     }
 
+    /// Replace all regions bound by `binder` with skolemized regions and
+    /// return a map indicating which bound-region was replaced with what
+    /// skolemized region. This is the first step of checking subtyping
+    /// when higher-ranked things are involved.
+    ///
+    /// **Important:** you must call this function from within a snapshot.
+    /// Moreover, before committing the snapshot, you must eventually call
+    /// either `plug_leaks` or `pop_skolemized` to remove the skolemized
+    /// regions. If you rollback the snapshot (or are using a probe), then
+    /// the pop occurs as part of the rollback, so an explicit call is not
+    /// needed (but is also permitted).
+    ///
+    /// See `README.md` for more details.
     pub fn skolemize_late_bound_regions<T>(&self,
                                            binder: &ty::Binder<T>,
                                            snapshot: &CombinedSnapshot)
                                            -> (T, SkolemizationMap)
         where T : TypeFoldable<'tcx>
     {
-        /*!
-         * Replace all regions bound by `binder` with skolemized regions and
-         * return a map indicating which bound-region was replaced with what
-         * skolemized region. This is the first step of checking subtyping
-         * when higher-ranked things are involved. See `README.md` for more
-         * details.
-         */
-
         let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| {
-            self.region_vars.new_skolemized(br, &snapshot.region_vars_snapshot)
+            self.region_vars.push_skolemized(br, &snapshot.region_vars_snapshot)
         });
 
         debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})",
@@ -448,32 +610,80 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         (result, map)
     }
 
+    /// Searches the region constriants created since `snapshot` was started
+    /// and checks to determine whether any of the skolemized regions created
+    /// in `skol_map` would "escape" -- meaning that they are related to
+    /// other regions in some way. If so, the higher-ranked subtyping doesn't
+    /// hold. See `README.md` for more details.
     pub fn leak_check(&self,
                       overly_polymorphic: bool,
+                      span: Span,
                       skol_map: &SkolemizationMap,
                       snapshot: &CombinedSnapshot)
                       -> RelateResult<'tcx, ()>
     {
-        /*!
-         * Searches the region constriants created since `snapshot` was started
-         * and checks to determine whether any of the skolemized regions created
-         * in `skol_map` would "escape" -- meaning that they are related to
-         * other regions in some way. If so, the higher-ranked subtyping doesn't
-         * hold. See `README.md` for more details.
-         */
-
         debug!("leak_check: skol_map={:?}",
                skol_map);
 
+        // ## Issue #32330 warnings
+        //
+        // When Issue #32330 is fixed, a certain number of late-bound
+        // regions (LBR) will become early-bound. We wish to issue
+        // warnings when the result of `leak_check` relies on such LBR, as
+        // that means that compilation will likely start to fail.
+        //
+        // Recall that when we do a "HR subtype" check, we replace all
+        // late-bound regions (LBR) in the subtype with fresh variables,
+        // and skolemize the late-bound regions in the supertype. If those
+        // skolemized regions from the supertype wind up being
+        // super-regions (directly or indirectly) of either
+        //
+        // - another skolemized region; or,
+        // - some region that pre-exists the HR subtype check
+        //   - e.g., a region variable that is not one of those created
+        //     to represent bound regions in the subtype
+        //
+        // then leak-check (and hence the subtype check) fails.
+        //
+        // What will change when we fix #32330 is that some of the LBR in the
+        // subtype may become early-bound. In that case, they would no longer be in
+        // the "permitted set" of variables that can be related to a skolemized
+        // type.
+        //
+        // So the foundation for this warning is to collect variables that we found
+        // to be related to a skolemized type. For each of them, we have a
+        // `BoundRegion` which carries a `Issue32330` flag. We check whether any of
+        // those flags indicate that this variable was created from a lifetime
+        // that will change from late- to early-bound. If so, we issue a warning
+        // indicating that the results of compilation may change.
+        //
+        // This is imperfect, since there are other kinds of code that will not
+        // compile once #32330 is fixed. However, it fixes the errors observed in
+        // practice on crater runs.
+        let mut warnings = vec![];
+
         let new_vars = self.region_vars_confined_to_snapshot(snapshot);
         for (&skol_br, &skol) in skol_map {
-            let tainted = self.tainted_regions(snapshot, skol);
-            for &tainted_region in &tainted {
+            // The inputs to a skolemized variable can only
+            // be itself or other new variables.
+            let incoming_taints = self.tainted_regions(snapshot,
+                                                       skol,
+                                                       TaintDirections::both());
+            for &tainted_region in &incoming_taints {
                 // Each skolemized should only be relatable to itself
                 // or new variables:
                 match tainted_region {
                     ty::ReVar(vid) => {
-                        if new_vars.iter().any(|&x| x == vid) { continue; }
+                        if new_vars.contains(&vid) {
+                            warnings.extend(
+                                match self.region_vars.var_origin(vid) {
+                                    LateBoundRegion(_,
+                                                    ty::BrNamed(_, _, wc),
+                                                    _) => Some(wc),
+                                    _ => None,
+                                });
+                            continue;
+                        }
                     }
                     _ => {
                         if tainted_region == skol { continue; }
@@ -496,6 +706,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 }
             }
         }
+
+        self.issue_32330_warnings(span, &warnings);
+
         Ok(())
     }
 
@@ -533,8 +746,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                          value: &T) -> T
         where T : TypeFoldable<'tcx>
     {
-        debug_assert!(self.leak_check(false, &skol_map, snapshot).is_ok());
-
         debug!("plug_leaks(skol_map={:?}, value={:?})",
                skol_map,
                value);
@@ -545,9 +756,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         // these taint sets are mutually disjoint.
         let inv_skol_map: FnvHashMap<ty::Region, ty::BoundRegion> =
             skol_map
-            .into_iter()
-            .flat_map(|(skol_br, skol)| {
-                self.tainted_regions(snapshot, skol)
+            .iter()
+            .flat_map(|(&skol_br, &skol)| {
+                self.tainted_regions(snapshot, skol, TaintDirections::both())
                     .into_iter()
                     .map(move |tainted_region| (tainted_region, skol_br))
             })
@@ -577,6 +788,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     // binders, so this assert is satisfied.
                     assert!(current_depth > 1);
 
+                    // since leak-check passed, this skolemized region
+                    // should only have incoming edges from variables
+                    // (which ought not to escape the snapshot, but we
+                    // don't check that) or itself
+                    assert!(
+                        match r {
+                            ty::ReVar(_) => true,
+                            ty::ReSkolemized(_, ref br1) => br == br1,
+                            _ => false,
+                        },
+                        "leak-check would have us replace {:?} with {:?}",
+                        r, br);
+
                     ty::ReLateBound(ty::DebruijnIndex::new(current_depth - 1), br.clone())
                 }
             }
@@ -585,6 +809,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         debug!("plug_leaks: result={:?}",
                result);
 
+        self.pop_skolemized(skol_map, snapshot);
+
+        debug!("plug_leaks: result={:?}", result);
+
         result
     }
+
+    /// Pops the skolemized regions found in `skol_map` from the region
+    /// inference context. Whenever you create skolemized regions via
+    /// `skolemize_late_bound_regions`, they must be popped before you
+    /// commit the enclosing snapshot (if you do not commit, e.g. within a
+    /// probe or as a result of an error, then this is not necessary, as
+    /// popping happens as part of the rollback).
+    ///
+    /// Note: popping also occurs implicitly as part of `leak_check`.
+    pub fn pop_skolemized(&self,
+                          skol_map: SkolemizationMap,
+                          snapshot: &CombinedSnapshot)
+    {
+        debug!("pop_skolemized({:?})", skol_map);
+        let skol_regions: FnvHashSet<_> = skol_map.values().cloned().collect();
+        self.region_vars.pop_skolemized(&skol_regions, &snapshot.region_vars_snapshot);
+    }
 }
index 41982ddc78b65e69662f37f6c0ad2aa5e5f06ea4..2ea2978b2940d858a482a408b98c424a4d2394b8 100644 (file)
@@ -39,12 +39,12 @@ use rustc_data_structures::unify::{self, UnificationTable};
 use std::cell::{Cell, RefCell, Ref, RefMut};
 use std::fmt;
 use syntax::ast;
-use syntax::codemap;
-use syntax::codemap::{Span, DUMMY_SP};
-use syntax::errors::DiagnosticBuilder;
+use errors::DiagnosticBuilder;
+use syntax_pos::{self, Span, DUMMY_SP};
 use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};
 
 use self::combine::CombineFields;
+use self::higher_ranked::HrMatchResult;
 use self::region_inference::{RegionVarBindings, RegionSnapshot};
 use self::unify_key::ToType;
 
@@ -63,6 +63,7 @@ pub mod sub;
 pub mod type_variable;
 pub mod unify_key;
 
+#[must_use]
 pub struct InferOk<'tcx, T> {
     pub value: T,
     pub obligations: PredicateObligations<'tcx>,
@@ -104,6 +105,12 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
     pub tables: InferTables<'a, 'gcx, 'tcx>,
 
+    // Cache for projections. This cache is snapshotted along with the
+    // infcx.
+    //
+    // Public so that `traits::project` can use it.
+    pub projection_cache: RefCell<traits::ProjectionCache<'tcx>>,
+
     // We instantiate UnificationTable with bounds<Ty> because the
     // types that might instantiate a general type variable have an
     // order, represented by its upper and lower bounds.
@@ -163,6 +170,11 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     // If the number of errors increases, that's also a sign (line
     // `tained_by_errors`) to avoid reporting certain kinds of errors.
     err_count_on_creation: usize,
+
+    // This flag is used for debugging, and is set to true if there are
+    // any obligations set during the current snapshot. In that case, the
+    // snapshot can't be rolled back.
+    pub obligations_in_snapshot: Cell<bool>,
 }
 
 /// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
@@ -472,11 +484,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
             parameter_environment: param_env,
             selection_cache: traits::SelectionCache::new(),
             evaluation_cache: traits::EvaluationCache::new(),
+            projection_cache: RefCell::new(traits::ProjectionCache::new()),
             reported_trait_errors: RefCell::new(FnvHashSet()),
             normalize: false,
             projection_mode: ProjectionMode::AnyFinal,
             tainted_by_errors_flag: Cell::new(false),
-            err_count_on_creation: self.sess.err_count()
+            err_count_on_creation: self.sess.err_count(),
+            obligations_in_snapshot: Cell::new(false),
         }
     }
 }
@@ -504,6 +518,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
         global_tcx.enter_local(arenas, |tcx| f(InferCtxt {
             tcx: tcx,
             tables: tables,
+            projection_cache: RefCell::new(traits::ProjectionCache::new()),
             type_variables: RefCell::new(type_variable::TypeVariableTable::new()),
             int_unification_table: RefCell::new(UnificationTable::new()),
             float_unification_table: RefCell::new(UnificationTable::new()),
@@ -515,7 +530,8 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
             normalize: normalize,
             projection_mode: projection_mode,
             tainted_by_errors_flag: Cell::new(false),
-            err_count_on_creation: tcx.sess.err_count()
+            err_count_on_creation: tcx.sess.err_count(),
+            obligations_in_snapshot: Cell::new(false),
         }))
     }
 }
@@ -531,17 +547,19 @@ impl<T> ExpectedFound<T> {
 }
 
 impl<'tcx, T> InferOk<'tcx, T> {
-    fn unit(self) -> InferOk<'tcx, ()> {
+    pub fn unit(self) -> InferOk<'tcx, ()> {
         InferOk { value: (), obligations: self.obligations }
     }
 }
 
 #[must_use = "once you start a snapshot, you should always consume it"]
 pub struct CombinedSnapshot {
+    projection_cache_snapshot: traits::ProjectionCacheSnapshot,
     type_snapshot: type_variable::Snapshot,
     int_snapshot: unify::Snapshot<ty::IntVid>,
     float_snapshot: unify::Snapshot<ty::FloatVid>,
     region_vars_snapshot: RegionSnapshot,
+    obligations_in_snapshot: bool,
 }
 
 /// Helper trait for shortening the lifetimes inside a
@@ -605,6 +623,24 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
             value.trans_normalize(&infcx)
         })
     }
+
+    pub fn normalize_associated_type_in_env<T>(
+        self, value: &T, env: &'a ty::ParameterEnvironment<'tcx>
+    ) -> T
+        where T: TransNormalize<'tcx>
+    {
+        debug!("normalize_associated_type_in_env(t={:?})", value);
+
+        let value = self.erase_regions(value);
+
+        if !value.has_projection_types() {
+            return value;
+        }
+
+        self.infer_ctxt(None, Some(env.clone()), ProjectionMode::Any).enter(|infcx| {
+            value.trans_normalize(&infcx)
+       })
+    }
 }
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
@@ -635,6 +671,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                             -> T::Lifted
         where T: TypeFoldable<'tcx> + ty::Lift<'gcx>
     {
+        debug!("drain_fulfillment_cx_or_panic()");
+
         let when = "resolving bounds after type-checking";
         let v = match self.drain_fulfillment_cx(fulfill_cx, result) {
             Ok(v) => v,
@@ -809,21 +847,36 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 
     fn start_snapshot(&self) -> CombinedSnapshot {
+        debug!("start_snapshot()");
+
+        let obligations_in_snapshot = self.obligations_in_snapshot.get();
+        self.obligations_in_snapshot.set(false);
+
         CombinedSnapshot {
+            projection_cache_snapshot: self.projection_cache.borrow_mut().snapshot(),
             type_snapshot: self.type_variables.borrow_mut().snapshot(),
             int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
             float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
             region_vars_snapshot: self.region_vars.start_snapshot(),
+            obligations_in_snapshot: obligations_in_snapshot,
         }
     }
 
     fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot) {
         debug!("rollback_to(cause={})", cause);
-        let CombinedSnapshot { type_snapshot,
+        let CombinedSnapshot { projection_cache_snapshot,
+                               type_snapshot,
                                int_snapshot,
                                float_snapshot,
-                               region_vars_snapshot } = snapshot;
+                               region_vars_snapshot,
+                               obligations_in_snapshot } = snapshot;
 
+        assert!(!self.obligations_in_snapshot.get());
+        self.obligations_in_snapshot.set(obligations_in_snapshot);
+
+        self.projection_cache
+            .borrow_mut()
+            .rollback_to(projection_cache_snapshot);
         self.type_variables
             .borrow_mut()
             .rollback_to(type_snapshot);
@@ -838,12 +891,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 
     fn commit_from(&self, snapshot: CombinedSnapshot) {
-        debug!("commit_from!");
-        let CombinedSnapshot { type_snapshot,
+        debug!("commit_from()");
+        let CombinedSnapshot { projection_cache_snapshot,
+                               type_snapshot,
                                int_snapshot,
                                float_snapshot,
-                               region_vars_snapshot } = snapshot;
+                               region_vars_snapshot,
+                               obligations_in_snapshot } = snapshot;
+
+        self.obligations_in_snapshot.set(obligations_in_snapshot);
 
+        self.projection_cache
+            .borrow_mut()
+            .commit(projection_cache_snapshot);
         self.type_variables
             .borrow_mut()
             .commit(type_snapshot);
@@ -901,17 +961,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         F: FnOnce() -> Result<T, E>
     {
         debug!("commit_regions_if_ok()");
-        let CombinedSnapshot { type_snapshot,
+        let CombinedSnapshot { projection_cache_snapshot,
+                               type_snapshot,
                                int_snapshot,
                                float_snapshot,
-                               region_vars_snapshot } = self.start_snapshot();
+                               region_vars_snapshot,
+                               obligations_in_snapshot } = self.start_snapshot();
 
         let r = self.commit_if_ok(|_| f());
 
         debug!("commit_regions_if_ok: rolling back everything but regions");
 
+        assert!(!self.obligations_in_snapshot.get());
+        self.obligations_in_snapshot.set(obligations_in_snapshot);
+
         // Roll back any non-region bindings - they should be resolved
         // inside `f`, with, e.g. `resolve_type_vars_if_possible`.
+        self.projection_cache
+            .borrow_mut()
+            .rollback_to(projection_cache_snapshot);
         self.type_variables
             .borrow_mut()
             .rollback_to(type_snapshot);
@@ -967,7 +1035,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                          -> UnitResult<'tcx>
     {
         self.probe(|_| {
-            let origin = TypeOrigin::Misc(codemap::DUMMY_SP);
+            let origin = TypeOrigin::Misc(syntax_pos::DUMMY_SP);
             let trace = TypeTrace::types(origin, true, a, b);
             self.sub(true, trace, &a, &b).map(|_| ())
         })
@@ -1053,7 +1121,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 self.skolemize_late_bound_regions(predicate, snapshot);
             let origin = TypeOrigin::EquatePredicate(span);
             let eqty_ok = self.eq_types(false, origin, a, b)?;
-            self.leak_check(false, &skol_map, snapshot).map(|_| eqty_ok.unit())
+            self.leak_check(false, span, &skol_map, snapshot)?;
+            self.pop_skolemized(skol_map, snapshot);
+            Ok(eqty_ok.unit())
         })
     }
 
@@ -1067,7 +1137,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 self.skolemize_late_bound_regions(predicate, snapshot);
             let origin = RelateRegionParamBound(span);
             self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
-            self.leak_check(false, &skol_map, snapshot)
+            self.leak_check(false, span, &skol_map, snapshot)?;
+            Ok(self.pop_skolemized(skol_map, snapshot))
         })
     }
 
@@ -1546,6 +1617,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             |br| self.next_region_var(LateBoundRegion(span, br, lbrct)))
     }
 
+    /// Given a higher-ranked projection predicate like:
+    ///
+    ///     for<'a> <T as Fn<&'a u32>>::Output = &'a u32
+    ///
+    /// and a target trait-ref like:
+    ///
+    ///     <T as Fn<&'x u32>>
+    ///
+    /// find a substitution `S` for the higher-ranked regions (here,
+    /// `['a => 'x]`) such that the predicate matches the trait-ref,
+    /// and then return the value (here, `&'a u32`) but with the
+    /// substitution applied (hence, `&'x u32`).
+    ///
+    /// See `higher_ranked_match` in `higher_ranked/mod.rs` for more
+    /// details.
+    pub fn match_poly_projection_predicate(&self,
+                                           origin: TypeOrigin,
+                                           match_a: ty::PolyProjectionPredicate<'tcx>,
+                                           match_b: ty::TraitRef<'tcx>)
+                                           -> InferResult<'tcx, HrMatchResult<Ty<'tcx>>>
+    {
+        let span = origin.span();
+        let match_trait_ref = match_a.skip_binder().projection_ty.trait_ref;
+        let trace = TypeTrace {
+            origin: origin,
+            values: TraitRefs(ExpectedFound::new(true, match_trait_ref, match_b))
+        };
+
+        let match_pair = match_a.map_bound(|p| (p.projection_ty.trait_ref, p.ty));
+        let combine = self.combine_fields(true, trace);
+        let result = combine.higher_ranked_match(span, &match_pair, &match_b)?;
+        Ok(InferOk { value: result, obligations: combine.obligations })
+    }
+
     /// See `verify_generic_bound` method in `region_inference`
     pub fn verify_generic_bound(&self,
                                 origin: SubregionOrigin<'tcx>,
@@ -1707,7 +1812,7 @@ impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> {
 
     pub fn dummy(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> TypeTrace<'tcx> {
         TypeTrace {
-            origin: TypeOrigin::Misc(codemap::DUMMY_SP),
+            origin: TypeOrigin::Misc(syntax_pos::DUMMY_SP),
             values: Types(ExpectedFound {
                 expected: tcx.types.err,
                 found: tcx.types.err,
@@ -1781,7 +1886,7 @@ impl RegionVariableOrigin {
             Coercion(a) => a,
             EarlyBoundRegion(a, _) => a,
             LateBoundRegion(a, _, _) => a,
-            BoundRegionInCoherence(_) => codemap::DUMMY_SP,
+            BoundRegionInCoherence(_) => syntax_pos::DUMMY_SP,
             UpvarRegion(_, a) => a
         }
     }
index c9037d6b12aa8ee7302a4e1cae6d7417a881e83b..905ad7c0faa236c45e18c6eb5d03cc64e3663441 100644 (file)
@@ -213,8 +213,12 @@ fn constraint_to_nodes(c: &Constraint) -> (Node, Node) {
     match *c {
         Constraint::ConstrainVarSubVar(rv_1, rv_2) =>
             (Node::RegionVid(rv_1), Node::RegionVid(rv_2)),
-        Constraint::ConstrainRegSubVar(r_1, rv_2) => (Node::Region(r_1), Node::RegionVid(rv_2)),
-        Constraint::ConstrainVarSubReg(rv_1, r_2) => (Node::RegionVid(rv_1), Node::Region(r_2)),
+        Constraint::ConstrainRegSubVar(r_1, rv_2) =>
+            (Node::Region(r_1), Node::RegionVid(rv_2)),
+        Constraint::ConstrainVarSubReg(rv_1, r_2) =>
+            (Node::RegionVid(rv_1), Node::Region(r_2)),
+        Constraint::ConstrainRegSubReg(r_1, r_2) =>
+            (Node::Region(r_1), Node::Region(r_2)),
     }
 }
 
index 5312d03052552817810dac087fd68b88602309c3..d3b4afa2cee7915caa98b010a4b792d8713ea5df 100644 (file)
@@ -11,7 +11,6 @@
 //! See README.md
 
 pub use self::Constraint::*;
-pub use self::Verify::*;
 pub use self::UndoLogEntry::*;
 pub use self::CombineMapType::*;
 pub use self::RegionResolutionError::*;
@@ -20,19 +19,19 @@ pub use self::VarValue::*;
 use super::{RegionVariableOrigin, SubregionOrigin, MiscVariable};
 use super::unify_key;
 
+use rustc_data_structures::fnv::{FnvHashMap, FnvHashSet};
 use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING};
 use rustc_data_structures::unify::{self, UnificationTable};
 use middle::free_region::FreeRegionMap;
 use ty::{self, Ty, TyCtxt};
 use ty::{BoundRegion, Region, RegionVid};
-use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound};
+use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased};
 use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh};
-use util::common::indenter;
-use util::nodemap::{FnvHashMap, FnvHashSet};
 
 use std::cell::{Cell, RefCell};
 use std::cmp::Ordering::{self, Less, Greater, Equal};
 use std::fmt;
+use std::mem;
 use std::u32;
 use syntax::ast;
 
@@ -47,25 +46,28 @@ pub enum Constraint {
     // Concrete region is subregion of region variable
     ConstrainRegSubVar(Region, RegionVid),
 
-    // Region variable is subregion of concrete region
-    //
-    // FIXME(#29436) -- should be remove in favor of a Verify
+    // Region variable is subregion of concrete region. This does not
+    // directly affect inference, but instead is checked after
+    // inference is complete.
     ConstrainVarSubReg(RegionVid, Region),
+
+    // A constraint where neither side is a variable. This does not
+    // directly affect inference, but instead is checked after
+    // inference is complete.
+    ConstrainRegSubReg(Region, Region),
 }
 
-// Something we have to verify after region inference is done, but
-// which does not directly influence the inference process
-pub enum Verify<'tcx> {
-    // VerifyRegSubReg(a, b): Verify that `a <= b`. Neither `a` nor
-    // `b` are inference variables.
-    VerifyRegSubReg(SubregionOrigin<'tcx>, Region, Region),
-
-    // VerifyGenericBound(T, _, R, RS): The parameter type `T` (or
-    // associated type) must outlive the region `R`. `T` is known to
-    // outlive `RS`. Therefore verify that `R <= RS[i]` for some
-    // `i`. Inference variables may be involved (but this verification
-    // step doesn't influence inference).
-    VerifyGenericBound(GenericKind<'tcx>, SubregionOrigin<'tcx>, Region, VerifyBound),
+// VerifyGenericBound(T, _, R, RS): The parameter type `T` (or
+// associated type) must outlive the region `R`. `T` is known to
+// outlive `RS`. Therefore verify that `R <= RS[i]` for some
+// `i`. Inference variables may be involved (but this verification
+// step doesn't influence inference).
+#[derive(Debug)]
+pub struct Verify<'tcx> {
+    kind: GenericKind<'tcx>,
+    origin: SubregionOrigin<'tcx>,
+    region: Region,
+    bound: VerifyBound,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq)]
@@ -108,13 +110,36 @@ pub struct TwoRegions {
 
 #[derive(Copy, Clone, PartialEq)]
 pub enum UndoLogEntry {
+    /// Pushed when we start a snapshot.
     OpenSnapshot,
+
+    /// Replaces an `OpenSnapshot` when a snapshot is committed, but
+    /// that snapshot is not the root. If the root snapshot is
+    /// unrolled, all nested snapshots must be committed.
     CommitedSnapshot,
+
+    /// We added `RegionVid`
     AddVar(RegionVid),
+
+    /// We added the given `constraint`
     AddConstraint(Constraint),
+
+    /// We added the given `verify`
     AddVerify(usize),
+
+    /// We added the given `given`
     AddGiven(ty::FreeRegion, ty::RegionVid),
+
+    /// We added a GLB/LUB "combinaton variable"
     AddCombination(CombineMapType, TwoRegions),
+
+    /// During skolemization, we sometimes purge entries from the undo
+    /// log in a kind of minisnapshot (unlike other snapshots, this
+    /// purging actually takes place *on success*). In that case, we
+    /// replace the corresponding entry with `Noop` so as to avoid the
+    /// need to do a bunch of swapping. (We can't use `swap_remove` as
+    /// the order of the vector is important.)
+    Purged,
 }
 
 #[derive(Copy, Clone, PartialEq)]
@@ -253,6 +278,112 @@ pub struct RegionSnapshot {
     skolemization_count: u32,
 }
 
+/// When working with skolemized regions, we often wish to find all of
+/// the regions that are either reachable from a skolemized region, or
+/// which can reach a skolemized region, or both. We call such regions
+/// *tained* regions.  This struct allows you to decide what set of
+/// tainted regions you want.
+#[derive(Debug)]
+pub struct TaintDirections {
+    incoming: bool,
+    outgoing: bool,
+}
+
+impl TaintDirections {
+    pub fn incoming() -> Self {
+        TaintDirections { incoming: true, outgoing: false }
+    }
+
+    pub fn outgoing() -> Self {
+        TaintDirections { incoming: false, outgoing: true }
+    }
+
+    pub fn both() -> Self {
+        TaintDirections { incoming: true, outgoing: true }
+    }
+}
+
+struct TaintSet {
+    directions: TaintDirections,
+    regions: FnvHashSet<ty::Region>
+}
+
+impl TaintSet {
+    fn new(directions: TaintDirections,
+           initial_region: ty::Region)
+           -> Self {
+        let mut regions = FnvHashSet();
+        regions.insert(initial_region);
+        TaintSet { directions: directions, regions: regions }
+    }
+
+    fn fixed_point(&mut self,
+                   undo_log: &[UndoLogEntry],
+                   verifys: &[Verify]) {
+        let mut prev_len = 0;
+        while prev_len < self.len() {
+            debug!("tainted: prev_len = {:?} new_len = {:?}",
+                   prev_len, self.len());
+
+            prev_len = self.len();
+
+            for undo_entry in undo_log {
+                match undo_entry {
+                    &AddConstraint(ConstrainVarSubVar(a, b)) => {
+                        self.add_edge(ReVar(a), ReVar(b));
+                    }
+                    &AddConstraint(ConstrainRegSubVar(a, b)) => {
+                        self.add_edge(a, ReVar(b));
+                    }
+                    &AddConstraint(ConstrainVarSubReg(a, b)) => {
+                        self.add_edge(ReVar(a), b);
+                    }
+                    &AddConstraint(ConstrainRegSubReg(a, b)) => {
+                        self.add_edge(a, b);
+                    }
+                    &AddGiven(a, b) => {
+                        self.add_edge(ReFree(a), ReVar(b));
+                    }
+                    &AddVerify(i) => {
+                        verifys[i].bound.for_each_region(&mut |b| {
+                            self.add_edge(verifys[i].region, b);
+                        });
+                    }
+                    &Purged |
+                    &AddCombination(..) |
+                    &AddVar(..) |
+                    &OpenSnapshot |
+                    &CommitedSnapshot => {}
+                }
+            }
+        }
+    }
+
+    fn into_set(self) -> FnvHashSet<ty::Region> {
+        self.regions
+    }
+
+    fn len(&self) -> usize {
+        self.regions.len()
+    }
+
+    fn add_edge(&mut self,
+                source: ty::Region,
+                target: ty::Region) {
+        if self.directions.incoming {
+            if self.regions.contains(&target) {
+                self.regions.insert(source);
+            }
+        }
+
+        if self.directions.outgoing {
+            if self.regions.contains(&source) {
+                self.regions.insert(target);
+            }
+        }
+    }
+}
+
 impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> RegionVarBindings<'a, 'gcx, 'tcx> {
         RegionVarBindings {
@@ -290,6 +421,10 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         debug!("RegionVarBindings: commit({})", snapshot.length);
         assert!(self.undo_log.borrow().len() > snapshot.length);
         assert!((*self.undo_log.borrow())[snapshot.length] == OpenSnapshot);
+        assert!(self.skolemization_count.get() == snapshot.skolemization_count,
+                "failed to pop skolemized regions: {} now vs {} at start",
+                self.skolemization_count.get(),
+                snapshot.skolemization_count);
 
         let mut undo_log = self.undo_log.borrow_mut();
         if snapshot.length == 0 {
@@ -297,7 +432,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         } else {
             (*undo_log)[snapshot.length] = CommitedSnapshot;
         }
-        self.skolemization_count.set(snapshot.skolemization_count);
         self.unification_table.borrow_mut().commit(snapshot.region_snapshot);
     }
 
@@ -307,33 +441,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         assert!(undo_log.len() > snapshot.length);
         assert!((*undo_log)[snapshot.length] == OpenSnapshot);
         while undo_log.len() > snapshot.length + 1 {
-            match undo_log.pop().unwrap() {
-                OpenSnapshot => {
-                    bug!("Failure to observe stack discipline");
-                }
-                CommitedSnapshot => {}
-                AddVar(vid) => {
-                    let mut var_origins = self.var_origins.borrow_mut();
-                    var_origins.pop().unwrap();
-                    assert_eq!(var_origins.len(), vid.index as usize);
-                }
-                AddConstraint(ref constraint) => {
-                    self.constraints.borrow_mut().remove(constraint);
-                }
-                AddVerify(index) => {
-                    self.verifys.borrow_mut().pop();
-                    assert_eq!(self.verifys.borrow().len(), index);
-                }
-                AddGiven(sub, sup) => {
-                    self.givens.borrow_mut().remove(&(sub, sup));
-                }
-                AddCombination(Glb, ref regions) => {
-                    self.glbs.borrow_mut().remove(regions);
-                }
-                AddCombination(Lub, ref regions) => {
-                    self.lubs.borrow_mut().remove(regions);
-                }
-            }
+            self.rollback_undo_entry(undo_log.pop().unwrap());
         }
         let c = undo_log.pop().unwrap();
         assert!(c == OpenSnapshot);
@@ -342,6 +450,38 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             .rollback_to(snapshot.region_snapshot);
     }
 
+    pub fn rollback_undo_entry(&self, undo_entry: UndoLogEntry) {
+        match undo_entry {
+            OpenSnapshot => {
+                panic!("Failure to observe stack discipline");
+            }
+            Purged | CommitedSnapshot => {
+                // nothing to do here
+            }
+            AddVar(vid) => {
+                let mut var_origins = self.var_origins.borrow_mut();
+                var_origins.pop().unwrap();
+                assert_eq!(var_origins.len(), vid.index as usize);
+            }
+            AddConstraint(ref constraint) => {
+                self.constraints.borrow_mut().remove(constraint);
+            }
+            AddVerify(index) => {
+                self.verifys.borrow_mut().pop();
+                assert_eq!(self.verifys.borrow().len(), index);
+            }
+            AddGiven(sub, sup) => {
+                self.givens.borrow_mut().remove(&(sub, sup));
+            }
+            AddCombination(Glb, ref regions) => {
+                self.glbs.borrow_mut().remove(regions);
+            }
+            AddCombination(Lub, ref regions) => {
+                self.lubs.borrow_mut().remove(regions);
+            }
+        }
+    }
+
     pub fn num_vars(&self) -> u32 {
         let len = self.var_origins.borrow().len();
         // enforce no overflow
@@ -366,22 +506,30 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         return vid;
     }
 
+    pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin {
+        self.var_origins.borrow()[vid.index as usize].clone()
+    }
+
     /// Creates a new skolemized region. Skolemized regions are fresh
     /// regions used when performing higher-ranked computations. They
     /// must be used in a very particular way and are never supposed
     /// to "escape" out into error messages or the code at large.
     ///
     /// The idea is to always create a snapshot. Skolemized regions
-    /// can be created in the context of this snapshot, but once the
-    /// snapshot is committed or rolled back, their numbers will be
-    /// recycled, so you must be finished with them. See the extensive
-    /// comments in `higher_ranked.rs` to see how it works (in
-    /// particular, the subtyping comparison).
+    /// can be created in the context of this snapshot, but before the
+    /// snapshot is committed or rolled back, they must be popped
+    /// (using `pop_skolemized_regions`), so that their numbers can be
+    /// recycled. Normally you don't have to think about this: you use
+    /// the APIs in `higher_ranked/mod.rs`, such as
+    /// `skolemize_late_bound_regions` and `plug_leaks`, which will
+    /// guide you on this path (ensure that the `SkolemizationMap` is
+    /// consumed and you are good).  There are also somewhat extensive
+    /// comments in `higher_ranked/README.md`.
     ///
     /// The `snapshot` argument to this function is not really used;
     /// it's just there to make it explicit which snapshot bounds the
-    /// skolemized region that results.
-    pub fn new_skolemized(&self, br: ty::BoundRegion, snapshot: &RegionSnapshot) -> Region {
+    /// skolemized region that results. It should always be the top-most snapshot.
+    pub fn push_skolemized(&self, br: ty::BoundRegion, snapshot: &RegionSnapshot) -> Region {
         assert!(self.in_snapshot());
         assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot);
 
@@ -390,6 +538,94 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)
     }
 
+    /// Removes all the edges to/from the skolemized regions that are
+    /// in `skols`. This is used after a higher-ranked operation
+    /// completes to remove all trace of the skolemized regions
+    /// created in that time.
+    pub fn pop_skolemized(&self,
+                          skols: &FnvHashSet<ty::Region>,
+                          snapshot: &RegionSnapshot) {
+        debug!("pop_skolemized_regions(skols={:?})", skols);
+
+        assert!(self.in_snapshot());
+        assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot);
+        assert!(self.skolemization_count.get() as usize >= skols.len(),
+                "popping more skolemized variables than actually exist, \
+                 sc now = {}, skols.len = {}",
+                self.skolemization_count.get(),
+                skols.len());
+
+        let last_to_pop = self.skolemization_count.get();
+        let first_to_pop = last_to_pop - (skols.len() as u32);
+
+        assert!(first_to_pop >= snapshot.skolemization_count,
+                "popping more regions than snapshot contains, \
+                 sc now = {}, sc then = {}, skols.len = {}",
+                self.skolemization_count.get(),
+                snapshot.skolemization_count,
+                skols.len());
+        debug_assert! {
+            skols.iter()
+                 .all(|k| match *k {
+                     ty::ReSkolemized(index, _) =>
+                         index.index >= first_to_pop &&
+                         index.index < last_to_pop,
+                     _ =>
+                         false
+                 }),
+            "invalid skolemization keys or keys out of range ({}..{}): {:?}",
+            snapshot.skolemization_count,
+            self.skolemization_count.get(),
+            skols
+        }
+
+        let mut undo_log = self.undo_log.borrow_mut();
+
+        let constraints_to_kill: Vec<usize> =
+            undo_log.iter()
+                    .enumerate()
+                    .rev()
+                    .filter(|&(_, undo_entry)| kill_constraint(skols, undo_entry))
+                    .map(|(index, _)| index)
+                    .collect();
+
+        for index in constraints_to_kill {
+            let undo_entry = mem::replace(&mut undo_log[index], Purged);
+            self.rollback_undo_entry(undo_entry);
+        }
+
+        self.skolemization_count.set(snapshot.skolemization_count);
+        return;
+
+        fn kill_constraint(skols: &FnvHashSet<ty::Region>,
+                           undo_entry: &UndoLogEntry)
+                           -> bool {
+            match undo_entry {
+                &AddConstraint(ConstrainVarSubVar(_, _)) =>
+                    false,
+                &AddConstraint(ConstrainRegSubVar(a, _)) =>
+                    skols.contains(&a),
+                &AddConstraint(ConstrainVarSubReg(_, b)) =>
+                    skols.contains(&b),
+                &AddConstraint(ConstrainRegSubReg(a, b)) =>
+                    skols.contains(&a) || skols.contains(&b),
+                &AddGiven(_, _) =>
+                    false,
+                &AddVerify(_) =>
+                    false,
+                &AddCombination(_, ref two_regions) =>
+                    skols.contains(&two_regions.a) ||
+                    skols.contains(&two_regions.b),
+                &AddVar(..) |
+                &OpenSnapshot |
+                &Purged |
+                &CommitedSnapshot =>
+                    false,
+            }
+        }
+
+    }
+
     pub fn new_bound(&self, debruijn: ty::DebruijnIndex) -> Region {
         // Creates a fresh bound variable for use in GLB computations.
         // See discussion of GLB computation in the large comment at
@@ -443,11 +679,9 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         debug!("RegionVarBindings: add_verify({:?})", verify);
 
         // skip no-op cases known to be satisfied
-        match verify {
-            VerifyGenericBound(_, _, _, VerifyBound::AllBounds(ref bs)) if bs.len() == 0 => {
-                return;
-            }
-            _ => {}
+        match verify.bound {
+            VerifyBound::AllBounds(ref bs) if bs.len() == 0 => { return; }
+            _ => { }
         }
 
         let mut verifys = self.verifys.borrow_mut();
@@ -515,7 +749,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                 self.add_constraint(ConstrainVarSubReg(sub_id, r), origin);
             }
             _ => {
-                self.add_verify(VerifyRegSubReg(origin, sub, sup));
+                self.add_constraint(ConstrainRegSubReg(sub, sup), origin);
             }
         }
     }
@@ -526,7 +760,12 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                                 kind: GenericKind<'tcx>,
                                 sub: Region,
                                 bound: VerifyBound) {
-        self.add_verify(VerifyGenericBound(kind, origin, sub, bound));
+        self.add_verify(Verify {
+            kind: kind,
+            origin: origin,
+            region: sub,
+            bound: bound
+        });
     }
 
     pub fn lub_regions(&self, origin: SubregionOrigin<'tcx>, a: Region, b: Region) -> Region {
@@ -603,11 +842,8 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         where F: FnMut(&RegionVarBindings<'a, 'gcx, 'tcx>, Region, Region)
     {
         let vars = TwoRegions { a: a, b: b };
-        match self.combine_map(t).borrow().get(&vars) {
-            Some(&c) => {
-                return ReVar(c);
-            }
-            None => {}
+        if let Some(&c) = self.combine_map(t).borrow().get(&vars) {
+            return ReVar(c);
         }
         let c = self.new_region_var(MiscVariable(origin.span()));
         self.combine_map(t).borrow_mut().insert(vars, c);
@@ -632,83 +868,30 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             .collect()
     }
 
-    /// Computes all regions that have been related to `r0` in any way since the mark `mark` was
-    /// made---`r0` itself will be the first entry. This is used when checking whether skolemized
-    /// regions are being improperly related to other regions.
-    pub fn tainted(&self, mark: &RegionSnapshot, r0: Region) -> Vec<Region> {
-        debug!("tainted(mark={:?}, r0={:?})", mark, r0);
-        let _indenter = indenter();
+    /// Computes all regions that have been related to `r0` since the
+    /// mark `mark` was made---`r0` itself will be the first
+    /// entry. The `directions` parameter controls what kind of
+    /// relations are considered. For example, one can say that only
+    /// "incoming" edges to `r0` are desired, in which case one will
+    /// get the set of regions `{r|r <= r0}`. This is used when
+    /// checking whether skolemized regions are being improperly
+    /// related to other regions.
+    pub fn tainted(&self,
+                   mark: &RegionSnapshot,
+                   r0: Region,
+                   directions: TaintDirections)
+                   -> FnvHashSet<ty::Region> {
+        debug!("tainted(mark={:?}, r0={:?}, directions={:?})",
+               mark, r0, directions);
 
         // `result_set` acts as a worklist: we explore all outgoing
         // edges and add any new regions we find to result_set.  This
         // is not a terribly efficient implementation.
-        let mut result_set = vec![r0];
-        let mut result_index = 0;
-        while result_index < result_set.len() {
-            // nb: can't use usize::range() here because result_set grows
-            let r = result_set[result_index];
-            debug!("result_index={}, r={:?}", result_index, r);
-
-            for undo_entry in self.undo_log.borrow()[mark.length..].iter() {
-                match undo_entry {
-                    &AddConstraint(ConstrainVarSubVar(a, b)) => {
-                        consider_adding_bidirectional_edges(&mut result_set, r, ReVar(a), ReVar(b));
-                    }
-                    &AddConstraint(ConstrainRegSubVar(a, b)) => {
-                        consider_adding_bidirectional_edges(&mut result_set, r, a, ReVar(b));
-                    }
-                    &AddConstraint(ConstrainVarSubReg(a, b)) => {
-                        consider_adding_bidirectional_edges(&mut result_set, r, ReVar(a), b);
-                    }
-                    &AddGiven(a, b) => {
-                        consider_adding_bidirectional_edges(&mut result_set,
-                                                            r,
-                                                            ReFree(a),
-                                                            ReVar(b));
-                    }
-                    &AddVerify(i) => {
-                        match (*self.verifys.borrow())[i] {
-                            VerifyRegSubReg(_, a, b) => {
-                                consider_adding_bidirectional_edges(&mut result_set, r, a, b);
-                            }
-                            VerifyGenericBound(_, _, a, ref bound) => {
-                                bound.for_each_region(&mut |b| {
-                                    consider_adding_bidirectional_edges(&mut result_set, r, a, b)
-                                });
-                            }
-                        }
-                    }
-                    &AddCombination(..) |
-                    &AddVar(..) |
-                    &OpenSnapshot |
-                    &CommitedSnapshot => {}
-                }
-            }
-
-            result_index += 1;
-        }
-
-        return result_set;
-
-        fn consider_adding_bidirectional_edges(result_set: &mut Vec<Region>,
-                                               r: Region,
-                                               r1: Region,
-                                               r2: Region) {
-            consider_adding_directed_edge(result_set, r, r1, r2);
-            consider_adding_directed_edge(result_set, r, r2, r1);
-        }
-
-        fn consider_adding_directed_edge(result_set: &mut Vec<Region>,
-                                         r: Region,
-                                         r1: Region,
-                                         r2: Region) {
-            if r == r1 {
-                // Clearly, this is potentially inefficient.
-                if !result_set.iter().any(|x| *x == r2) {
-                    result_set.push(r2);
-                }
-            }
-        }
+        let mut taint_set = TaintSet::new(directions, r0);
+        taint_set.fixed_point(&self.undo_log.borrow()[mark.length..],
+                              &self.verifys.borrow());
+        debug!("tainted: result={:?}", taint_set.regions);
+        return taint_set.into_set();
     }
 
     /// This function performs the actual region resolution.  It must be
@@ -732,8 +915,10 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             (ReLateBound(..), _) |
             (_, ReLateBound(..)) |
             (ReEarlyBound(..), _) |
-            (_, ReEarlyBound(..)) => {
-                bug!("cannot relate bound region: LUB({:?}, {:?})", a, b);
+            (_, ReEarlyBound(..)) |
+            (ReErased, _) |
+            (_, ReErased) => {
+                bug!("cannot relate region: LUB({:?}, {:?})", a, b);
             }
 
             (ReStatic, _) | (_, ReStatic) => {
@@ -805,10 +990,6 @@ pub enum VarValue {
     ErrorValue,
 }
 
-struct VarData {
-    value: VarValue,
-}
-
 struct RegionAndOrigin<'tcx> {
     region: Region,
     origin: SubregionOrigin<'tcx>,
@@ -834,18 +1015,14 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         let graph = self.construct_graph();
         self.expand_givens(&graph);
         self.expansion(free_regions, &mut var_data);
-        self.contraction(free_regions, &mut var_data);
-        let values = self.extract_values_and_collect_conflicts(free_regions,
-                                                               &var_data,
-                                                               &graph,
-                                                               errors);
-        self.collect_concrete_region_errors(free_regions, &values, errors);
-        values
+        self.collect_errors(free_regions, &mut var_data, errors);
+        self.collect_var_errors(free_regions, &var_data, &graph, errors);
+        var_data
     }
 
-    fn construct_var_data(&self) -> Vec<VarData> {
+    fn construct_var_data(&self) -> Vec<VarValue> {
         (0..self.num_vars() as usize)
-            .map(|_| VarData { value: Value(ty::ReEmpty) })
+            .map(|_| Value(ty::ReEmpty))
             .collect()
     }
 
@@ -882,30 +1059,28 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn expansion(&self, free_regions: &FreeRegionMap, var_data: &mut [VarData]) {
-        self.iterate_until_fixed_point("Expansion", |constraint| {
+    fn expansion(&self, free_regions: &FreeRegionMap, var_values: &mut [VarValue]) {
+        self.iterate_until_fixed_point("Expansion", |constraint, origin| {
             debug!("expansion: constraint={:?} origin={:?}",
-                   constraint,
-                   self.constraints
-                       .borrow()
-                       .get(constraint)
-                       .unwrap());
+                   constraint, origin);
             match *constraint {
                 ConstrainRegSubVar(a_region, b_vid) => {
-                    let b_data = &mut var_data[b_vid.index as usize];
+                    let b_data = &mut var_values[b_vid.index as usize];
                     self.expand_node(free_regions, a_region, b_vid, b_data)
                 }
                 ConstrainVarSubVar(a_vid, b_vid) => {
-                    match var_data[a_vid.index as usize].value {
+                    match var_values[a_vid.index as usize] {
                         ErrorValue => false,
                         Value(a_region) => {
-                            let b_node = &mut var_data[b_vid.index as usize];
+                            let b_node = &mut var_values[b_vid.index as usize];
                             self.expand_node(free_regions, a_region, b_vid, b_node)
                         }
                     }
                 }
+                ConstrainRegSubReg(..) |
                 ConstrainVarSubReg(..) => {
-                    // This is a contraction constraint.  Ignore it.
+                    // These constraints are checked after expansion
+                    // is done, in `collect_errors`.
                     false
                 }
             }
@@ -916,12 +1091,12 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                    free_regions: &FreeRegionMap,
                    a_region: Region,
                    b_vid: RegionVid,
-                   b_data: &mut VarData)
+                   b_data: &mut VarValue)
                    -> bool {
         debug!("expand_node({:?}, {:?} == {:?})",
                a_region,
                b_vid,
-               b_data.value);
+               b_data);
 
         // Check if this relationship is implied by a given.
         match a_region {
@@ -934,7 +1109,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             _ => {}
         }
 
-        match b_data.value {
+        match *b_data {
             Value(cur_region) => {
                 let lub = self.lub_concrete_regions(free_regions, a_region, cur_region);
                 if lub == cur_region {
@@ -946,7 +1121,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                        cur_region,
                        lub);
 
-                b_data.value = Value(lub);
+                *b_data = Value(lub);
                 return true;
             }
 
@@ -956,94 +1131,92 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         }
     }
 
-    // FIXME(#29436) -- this fn would just go away if we removed ConstrainVarSubReg
-    fn contraction(&self, free_regions: &FreeRegionMap, var_data: &mut [VarData]) {
-        self.iterate_until_fixed_point("Contraction", |constraint| {
-            debug!("contraction: constraint={:?} origin={:?}",
-                   constraint,
-                   self.constraints
-                       .borrow()
-                       .get(constraint)
-                       .unwrap());
+    /// After expansion is complete, go and check upper bounds (i.e.,
+    /// cases where the region cannot grow larger than a fixed point)
+    /// and check that they are satisfied.
+    fn collect_errors(&self,
+                      free_regions: &FreeRegionMap,
+                      var_data: &mut Vec<VarValue>,
+                      errors: &mut Vec<RegionResolutionError<'tcx>>) {
+        let constraints = self.constraints.borrow();
+        for (constraint, origin) in constraints.iter() {
+            debug!("collect_errors: constraint={:?} origin={:?}",
+                   constraint, origin);
             match *constraint {
                 ConstrainRegSubVar(..) |
                 ConstrainVarSubVar(..) => {
                     // Expansion will ensure that these constraints hold. Ignore.
                 }
+
+                ConstrainRegSubReg(sub, sup) => {
+                    if free_regions.is_subregion_of(self.tcx, sub, sup) {
+                        continue;
+                    }
+
+                    debug!("collect_errors: region error at {:?}: \
+                            cannot verify that {:?} <= {:?}",
+                           origin,
+                           sub,
+                           sup);
+
+                    errors.push(ConcreteFailure((*origin).clone(), sub, sup));
+                }
+
                 ConstrainVarSubReg(a_vid, b_region) => {
                     let a_data = &mut var_data[a_vid.index as usize];
                     debug!("contraction: {:?} == {:?}, {:?}",
                            a_vid,
-                           a_data.value,
+                           a_data,
                            b_region);
 
-                    let a_region = match a_data.value {
-                        ErrorValue => return false,
+                    let a_region = match *a_data {
+                        ErrorValue => continue,
                         Value(a_region) => a_region,
                     };
 
+                    // Do not report these errors immediately:
+                    // instead, set the variable value to error and
+                    // collect them later.
                     if !free_regions.is_subregion_of(self.tcx, a_region, b_region) {
-                        debug!("Setting {:?} to ErrorValue: {:?} not subregion of {:?}",
+                        debug!("collect_errors: region error at {:?}: \
+                                cannot verify that {:?}={:?} <= {:?}",
+                               origin,
                                a_vid,
                                a_region,
                                b_region);
-                        a_data.value = ErrorValue;
+                        *a_data = ErrorValue;
                     }
                 }
             }
+        }
 
-            false
-        })
-    }
-
-    fn collect_concrete_region_errors(&self,
-                                      free_regions: &FreeRegionMap,
-                                      values: &Vec<VarValue>,
-                                      errors: &mut Vec<RegionResolutionError<'tcx>>) {
-        let mut reg_reg_dups = FnvHashSet();
         for verify in self.verifys.borrow().iter() {
-            match *verify {
-                VerifyRegSubReg(ref origin, sub, sup) => {
-                    if free_regions.is_subregion_of(self.tcx, sub, sup) {
-                        continue;
-                    }
-
-                    if !reg_reg_dups.insert((sub, sup)) {
-                        continue;
-                    }
-
-                    debug!("region inference error at {:?}: {:?} <= {:?} is not true",
-                           origin,
-                           sub,
-                           sup);
-
-                    errors.push(ConcreteFailure((*origin).clone(), sub, sup));
-                }
+            debug!("collect_errors: verify={:?}", verify);
+            let sub = normalize(var_data, verify.region);
+            if verify.bound.is_met(self.tcx, free_regions, var_data, sub) {
+                continue;
+            }
 
-                VerifyGenericBound(ref kind, ref origin, sub, ref bound) => {
-                    let sub = normalize(values, sub);
-                    if bound.is_met(self.tcx, free_regions, values, sub) {
-                        continue;
-                    }
+            debug!("collect_errors: region error at {:?}: \
+                    cannot verify that {:?} <= {:?}",
+                   verify.origin,
+                   verify.region,
+                   verify.bound);
 
-                    debug!("region inference error at {:?}: verifying {:?} <= {:?}",
-                           origin,
-                           sub,
-                           bound);
-
-                    errors.push(GenericBoundFailure((*origin).clone(), kind.clone(), sub));
-                }
-            }
+            errors.push(GenericBoundFailure(verify.origin.clone(),
+                                            verify.kind.clone(),
+                                            sub));
         }
     }
 
-    fn extract_values_and_collect_conflicts(&self,
-                                            free_regions: &FreeRegionMap,
-                                            var_data: &[VarData],
-                                            graph: &RegionGraph,
-                                            errors: &mut Vec<RegionResolutionError<'tcx>>)
-                                            -> Vec<VarValue> {
-        debug!("extract_values_and_collect_conflicts()");
+    /// Go over the variables that were declared to be error variables
+    /// and create a `RegionResolutionError` for each of them.
+    fn collect_var_errors(&self,
+                          free_regions: &FreeRegionMap,
+                          var_data: &[VarValue],
+                          graph: &RegionGraph,
+                          errors: &mut Vec<RegionResolutionError<'tcx>>) {
+        debug!("collect_var_errors");
 
         // This is the best way that I have found to suppress
         // duplicate and related errors. Basically we keep a set of
@@ -1059,7 +1232,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
         let mut dup_vec = vec![u32::MAX; self.num_vars() as usize];
 
         for idx in 0..self.num_vars() as usize {
-            match var_data[idx].value {
+            match var_data[idx] {
                 Value(_) => {
                     /* Inference successful */
                 }
@@ -1096,8 +1269,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                 }
             }
         }
-
-        (0..self.num_vars() as usize).map(|idx| var_data[idx].value).collect()
     }
 
     fn construct_graph(&self) -> RegionGraph {
@@ -1132,6 +1303,10 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                 ConstrainVarSubReg(a_id, _) => {
                     graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint);
                 }
+                ConstrainRegSubReg(..) => {
+                    // this would be an edge from `dummy_source` to
+                    // `dummy_sink`; just ignore it.
+                }
             }
         }
 
@@ -1274,13 +1449,18 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
                             origin: this.constraints.borrow().get(&edge.data).unwrap().clone(),
                         });
                     }
+
+                    ConstrainRegSubReg(..) => {
+                        panic!("cannot reach reg-sub-reg edge in region inference \
+                                post-processing")
+                    }
                 }
             }
         }
     }
 
     fn iterate_until_fixed_point<F>(&self, tag: &str, mut body: F)
-        where F: FnMut(&Constraint) -> bool
+        where F: FnMut(&Constraint, &SubregionOrigin<'tcx>) -> bool
     {
         let mut iteration = 0;
         let mut changed = true;
@@ -1288,8 +1468,8 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
             changed = false;
             iteration += 1;
             debug!("---- {} Iteration {}{}", "#", tag, iteration);
-            for (constraint, _) in self.constraints.borrow().iter() {
-                let edge_changed = body(constraint);
+            for (constraint, origin) in self.constraints.borrow().iter() {
+                let edge_changed = body(constraint, origin);
                 if edge_changed {
                     debug!("Updated due to constraint {:?}", constraint);
                     changed = true;
@@ -1301,19 +1481,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> {
 
 }
 
-impl<'tcx> fmt::Debug for Verify<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            VerifyRegSubReg(_, ref a, ref b) => {
-                write!(f, "VerifyRegSubReg({:?}, {:?})", a, b)
-            }
-            VerifyGenericBound(_, ref p, ref a, ref bs) => {
-                write!(f, "VerifyGenericBound({:?}, {:?}, {:?})", p, a, bs)
-            }
-        }
-    }
-}
-
 fn normalize(values: &Vec<VarValue>, r: ty::Region) -> ty::Region {
     match r {
         ty::ReVar(rid) => lookup(values, rid),
index c2a8d04ac00da6a6ed40c98a515f7029565f2cf7..09ae16540c4f159666c843c18db912ddb0006ab1 100644 (file)
@@ -13,7 +13,7 @@ use self::TypeVariableValue::*;
 use self::UndoEntry::*;
 use hir::def_id::{DefId};
 use ty::{self, Ty};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use std::cmp::min;
 use std::marker::PhantomData;
@@ -178,7 +178,9 @@ impl<'tcx> TypeVariableTable<'tcx> {
             value: Bounded { relations: vec![], default: default },
             diverging: diverging
         });
-        ty::TyVid { index: index as u32 }
+        let v = ty::TyVid { index: index as u32 };
+        debug!("new_var() -> {:?}", v);
+        v
     }
 
     pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
@@ -219,6 +221,17 @@ impl<'tcx> TypeVariableTable<'tcx> {
     }
 
     pub fn rollback_to(&mut self, s: Snapshot) {
+        debug!("rollback_to{:?}", {
+            for action in self.values.actions_since_snapshot(&s.snapshot) {
+                match *action {
+                    sv::UndoLog::NewElem(index) => {
+                        debug!("inference variable _#{}t popped", index)
+                    }
+                    _ => { }
+                }
+            }
+        });
+
         self.values.rollback_to(s.snapshot);
         self.eq_relations.rollback_to(s.eq_snapshot);
     }
index e1fb701e641bf1da7e977c3793da271588d08e5c..48ea953cc1e8b05a137d32659ab11caa20bde3a8 100644 (file)
@@ -28,8 +28,8 @@
 #![feature(box_syntax)]
 #![feature(collections)]
 #![feature(const_fn)]
+#![feature(core_intrinsics)]
 #![feature(enumset)]
-#![feature(iter_arith)]
 #![feature(libc)]
 #![feature(nonzero)]
 #![feature(quote)]
@@ -54,8 +54,10 @@ extern crate rustc_data_structures;
 extern crate serialize;
 extern crate collections;
 extern crate rustc_const_math;
+extern crate rustc_errors as errors;
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
+#[macro_use] extern crate syntax_pos;
 #[macro_use] #[no_link] extern crate rustc_bitflags;
 
 extern crate serialize as rustc_serialize; // used by deriving
@@ -102,10 +104,12 @@ pub mod middle {
 }
 
 pub mod mir {
+    mod cache;
     pub mod repr;
     pub mod tcx;
     pub mod visit;
     pub mod transform;
+    pub mod traversal;
     pub mod mir_map;
 }
 
index d7971cd2cf040743dfec64b62ca6e6bf09a7974a..41086b5d1c990651cfdb774e595b3335ee5330a7 100644 (file)
@@ -204,6 +204,12 @@ declare_lint! {
     "object-unsafe non-principal fragments in object types were erroneously allowed"
 }
 
+declare_lint! {
+    pub LIFETIME_UNDERSCORE,
+    Warn,
+    "lifetimes or labels named `'_` were erroneously allowed"
+}
+
 /// Does nothing as a lint pass, but registers some `Lint`s
 /// which are used by other parts of the compiler.
 #[derive(Copy, Clone)]
@@ -242,7 +248,8 @@ impl LintPass for HardwiredLints {
             SUPER_OR_SELF_IN_GLOBAL_PATH,
             UNSIZED_IN_TUPLE,
             OBJECT_UNSAFE_FRAGMENT,
-            HR_LIFETIME_IN_ASSOC_TYPE
+            HR_LIFETIME_IN_ASSOC_TYPE,
+            LIFETIME_UNDERSCORE
         )
     }
 }
index 0801f8f4ac7ef8bc63e5d18325b6fc2fb89c3210..01e14ad71b39c8c0e7e1e384950b00cc1b046559 100644 (file)
@@ -40,11 +40,10 @@ use std::cmp;
 use std::default::Default as StdDefault;
 use std::mem;
 use syntax::attr::{self, AttrMetaMethods};
-use syntax::codemap::Span;
-use syntax::errors::DiagnosticBuilder;
 use syntax::parse::token::InternedString;
 use syntax::ast;
-use syntax::attr::ThinAttributesExt;
+use syntax_pos::Span;
+use errors::DiagnosticBuilder;
 use hir;
 use hir::intravisit as hir_visit;
 use hir::intravisit::{IdVisitor, IdVisitingOperation};
@@ -767,7 +766,7 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
     }
 
     fn visit_expr(&mut self, e: &hir::Expr) {
-        self.with_lint_attrs(e.attrs.as_attr_slice(), |cx| {
+        self.with_lint_attrs(&e.attrs, |cx| {
             run_lints!(cx, check_expr, late_passes, e);
             hir_visit::walk_expr(cx, e);
         })
@@ -832,7 +831,7 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
     }
 
     fn visit_local(&mut self, l: &hir::Local) {
-        self.with_lint_attrs(l.attrs.as_attr_slice(), |cx| {
+        self.with_lint_attrs(&l.attrs, |cx| {
             run_lints!(cx, check_local, late_passes, l);
             hir_visit::walk_local(cx, l);
         })
@@ -905,7 +904,7 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
     }
 }
 
-impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> {
+impl<'a> ast_visit::Visitor for EarlyContext<'a> {
     fn visit_item(&mut self, it: &ast::Item) {
         self.with_lint_attrs(&it.attrs, |cx| {
             run_lints!(cx, check_item, early_passes, it);
@@ -928,7 +927,7 @@ impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> {
     }
 
     fn visit_expr(&mut self, e: &ast::Expr) {
-        self.with_lint_attrs(e.attrs.as_attr_slice(), |cx| {
+        self.with_lint_attrs(&e.attrs, |cx| {
             run_lints!(cx, check_expr, early_passes, e);
             ast_visit::walk_expr(cx, e);
         })
@@ -939,8 +938,8 @@ impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> {
         ast_visit::walk_stmt(self, s);
     }
 
-    fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, decl: &'v ast::FnDecl,
-                body: &'v ast::Block, span: Span, id: ast::NodeId) {
+    fn visit_fn(&mut self, fk: ast_visit::FnKind, decl: &ast::FnDecl,
+                body: &ast::Block, span: Span, id: ast::NodeId) {
         run_lints!(self, check_fn, early_passes, fk, decl, body, span, id);
         ast_visit::walk_fn(self, fk, decl, body, span);
         run_lints!(self, check_fn_post, early_passes, fk, decl, body, span, id);
@@ -988,7 +987,7 @@ impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> {
     }
 
     fn visit_local(&mut self, l: &ast::Local) {
-        self.with_lint_attrs(l.attrs.as_attr_slice(), |cx| {
+        self.with_lint_attrs(&l.attrs, |cx| {
             run_lints!(cx, check_local, early_passes, l);
             ast_visit::walk_local(cx, l);
         })
@@ -1005,11 +1004,6 @@ impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> {
         ast_visit::walk_arm(self, a);
     }
 
-    fn visit_decl(&mut self, d: &ast::Decl) {
-        run_lints!(self, check_decl, early_passes, d);
-        ast_visit::walk_decl(self, d);
-    }
-
     fn visit_expr_post(&mut self, e: &ast::Expr) {
         run_lints!(self, check_expr_post, early_passes, e);
     }
@@ -1043,11 +1037,6 @@ impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> {
         run_lints!(self, check_lifetime_def, early_passes, lt);
     }
 
-    fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf) {
-        run_lints!(self, check_explicit_self, early_passes, es);
-        ast_visit::walk_explicit_self(self, es);
-    }
-
     fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId) {
         run_lints!(self, check_path, early_passes, p, id);
         ast_visit::walk_path(self, p);
@@ -1066,13 +1055,10 @@ impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> {
 // Output any lints that were previously added to the session.
 impl<'a, 'tcx> IdVisitingOperation for LateContext<'a, 'tcx> {
     fn visit_id(&mut self, id: ast::NodeId) {
-        match self.sess().lints.borrow_mut().remove(&id) {
-            None => {}
-            Some(lints) => {
-                debug!("LateContext::visit_id: id={:?} lints={:?}", id, lints);
-                for (lint_id, span, msg) in lints {
-                    self.span_lint(lint_id.lint, span, &msg[..])
-                }
+        if let Some(lints) = self.sess().lints.borrow_mut().remove(&id) {
+            debug!("LateContext::visit_id: id={:?} lints={:?}", id, lints);
+            for (lint_id, span, msg) in lints {
+                self.span_lint(lint_id.lint, span, &msg[..])
             }
         }
     }
index 28994e1a7c48d50d82f6c807784a0b3bee4c5989..92aa446c265f9f791d3911d063357a0a1a0809ea 100644 (file)
@@ -33,7 +33,7 @@ pub use self::LintSource::*;
 
 use std::hash;
 use std::ascii::AsciiExt;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use hir::intravisit::FnKind;
 use syntax::visit as ast_visit;
 use syntax::ast;
@@ -167,7 +167,6 @@ pub trait LateLintPass: LintPass {
     fn check_variant_post(&mut self, _: &LateContext, _: &hir::Variant, _: &hir::Generics) { }
     fn check_lifetime(&mut self, _: &LateContext, _: &hir::Lifetime) { }
     fn check_lifetime_def(&mut self, _: &LateContext, _: &hir::LifetimeDef) { }
-    fn check_explicit_self(&mut self, _: &LateContext, _: &hir::ExplicitSelf) { }
     fn check_path(&mut self, _: &LateContext, _: &hir::Path, _: ast::NodeId) { }
     fn check_path_list_item(&mut self, _: &LateContext, _: &hir::PathListItem) { }
     fn check_attribute(&mut self, _: &LateContext, _: &ast::Attribute) { }
@@ -196,7 +195,6 @@ pub trait EarlyLintPass: LintPass {
     fn check_stmt(&mut self, _: &EarlyContext, _: &ast::Stmt) { }
     fn check_arm(&mut self, _: &EarlyContext, _: &ast::Arm) { }
     fn check_pat(&mut self, _: &EarlyContext, _: &ast::Pat) { }
-    fn check_decl(&mut self, _: &EarlyContext, _: &ast::Decl) { }
     fn check_expr(&mut self, _: &EarlyContext, _: &ast::Expr) { }
     fn check_expr_post(&mut self, _: &EarlyContext, _: &ast::Expr) { }
     fn check_ty(&mut self, _: &EarlyContext, _: &ast::Ty) { }
@@ -218,7 +216,6 @@ pub trait EarlyLintPass: LintPass {
     fn check_variant_post(&mut self, _: &EarlyContext, _: &ast::Variant, _: &ast::Generics) { }
     fn check_lifetime(&mut self, _: &EarlyContext, _: &ast::Lifetime) { }
     fn check_lifetime_def(&mut self, _: &EarlyContext, _: &ast::LifetimeDef) { }
-    fn check_explicit_self(&mut self, _: &EarlyContext, _: &ast::ExplicitSelf) { }
     fn check_path(&mut self, _: &EarlyContext, _: &ast::Path, _: ast::NodeId) { }
     fn check_path_list_item(&mut self, _: &EarlyContext, _: &ast::PathListItem) { }
     fn check_attribute(&mut self, _: &EarlyContext, _: &ast::Attribute) { }
index 8f97a89e6547e321cdccf347598a4219dae82dc4..e856eb84ff2c3a93f33259acb9504d9dcb793fe7 100644 (file)
@@ -17,7 +17,7 @@
 use hir::def::Def;
 use ty::{Ty, TyCtxt};
 
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use hir as ast;
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
@@ -65,13 +65,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     /// to it.
     pub fn ast_ty_to_prim_ty(self, ast_ty: &ast::Ty) -> Option<Ty<'tcx>> {
         if let ast::TyPath(None, ref path) = ast_ty.node {
-            let def = match self.def_map.borrow().get(&ast_ty.id) {
-                None => {
-                    span_bug!(ast_ty.span, "unbound path {:?}", path)
-                }
-                Some(d) => d.full_def()
-            };
-            if let Def::PrimTy(nty) = def {
+            if let Def::PrimTy(nty) = self.expect_def(ast_ty.id) {
                 Some(self.prim_ty_to_ty(&path.segments, nty))
             } else {
                 None
index 3621cb267d91f9dfb079fc15d6fbebecf8532c1c..3482971cd1999c0507abc2b959d498534a950766 100644 (file)
@@ -12,14 +12,12 @@ use syntax::parse::token::InternedString;
 use syntax::ast;
 use std::rc::Rc;
 use hir::def_id::DefId;
-use std::hash;
-use std::mem::transmute;
 use rustc_const_math::*;
 use self::ConstVal::*;
 
-#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
 pub enum ConstVal {
-    Float(f64),
+    Float(ConstFloat),
     Integral(ConstInt),
     Str(InternedString),
     ByteStr(Rc<Vec<u8>>),
@@ -36,55 +34,10 @@ pub enum ConstVal {
     Dummy,
 }
 
-impl hash::Hash for ConstVal {
-    fn hash<H: hash::Hasher>(&self, state: &mut H) {
-        match *self {
-            Float(a) => unsafe { transmute::<_,u64>(a) }.hash(state),
-            Integral(a) => a.hash(state),
-            Str(ref a) => a.hash(state),
-            ByteStr(ref a) => a.hash(state),
-            Bool(a) => a.hash(state),
-            Struct(a) => a.hash(state),
-            Tuple(a) => a.hash(state),
-            Function(a) => a.hash(state),
-            Array(a, n) => { a.hash(state); n.hash(state) },
-            Repeat(a, n) => { a.hash(state); n.hash(state) },
-            Char(c) => c.hash(state),
-            Dummy => ().hash(state),
-        }
-    }
-}
-
-/// Note that equality for `ConstVal` means that the it is the same
-/// constant, not that the rust values are equal. In particular, `NaN
-/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
-/// are considering unequal).
-impl PartialEq for ConstVal {
-    fn eq(&self, other: &ConstVal) -> bool {
-        match (self, other) {
-            (&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)},
-            (&Integral(a), &Integral(b)) => a == b,
-            (&Str(ref a), &Str(ref b)) => a == b,
-            (&ByteStr(ref a), &ByteStr(ref b)) => a == b,
-            (&Bool(a), &Bool(b)) => a == b,
-            (&Struct(a), &Struct(b)) => a == b,
-            (&Tuple(a), &Tuple(b)) => a == b,
-            (&Function(a), &Function(b)) => a == b,
-            (&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn),
-            (&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn),
-            (&Char(a), &Char(b)) => a == b,
-            (&Dummy, &Dummy) => true, // FIXME: should this be false?
-            _ => false,
-        }
-    }
-}
-
-impl Eq for ConstVal { }
-
 impl ConstVal {
     pub fn description(&self) -> &'static str {
         match *self {
-            Float(_) => "float",
+            Float(f) => f.description(),
             Integral(i) => i.description(),
             Str(_) => "string literal",
             ByteStr(_) => "byte string literal",
index e5a8c1d1b4e6fe3542aee6b7446651c327c5bfcc..fd9463b13c055143eb208e4238b719a30ea4514f 100644 (file)
@@ -34,15 +34,14 @@ use mir::mir_map::MirMap;
 use session::Session;
 use session::config::PanicStrategy;
 use session::search_paths::PathKind;
-use util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap};
-use std::cell::RefCell;
+use util::nodemap::{FnvHashMap, NodeSet, DefIdMap};
 use std::rc::Rc;
 use std::path::PathBuf;
 use syntax::ast;
 use syntax::attr;
-use syntax::codemap::Span;
 use syntax::ptr::P;
 use syntax::parse::token::InternedString;
+use syntax_pos::Span;
 use rustc_back::target::Target;
 use hir;
 use hir::intravisit::{IdVisitor, IdVisitingOperation, Visitor};
@@ -164,12 +163,12 @@ pub trait CrateStore<'tcx> {
                      -> ty::TypeScheme<'tcx>;
     fn visible_parent_map<'a>(&'a self) -> ::std::cell::RefMut<'a, DefIdMap<DefId>>;
     fn item_name(&self, def: DefId) -> ast::Name;
+    fn opt_item_name(&self, def: DefId) -> Option<ast::Name>;
     fn item_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)
                            -> ty::GenericPredicates<'tcx>;
     fn item_super_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)
                                  -> ty::GenericPredicates<'tcx>;
     fn item_attrs(&self, def_id: DefId) -> Vec<ast::Attribute>;
-    fn item_symbol(&self, def: DefId) -> String;
     fn trait_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)-> ty::TraitDef<'tcx>;
     fn adt_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::AdtDefMaster<'tcx>;
     fn method_arg_names(&self, did: DefId) -> Vec<String>;
@@ -205,6 +204,7 @@ pub trait CrateStore<'tcx> {
     fn is_impl(&self, did: DefId) -> bool;
     fn is_default_impl(&self, impl_did: DefId) -> bool;
     fn is_extern_item<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, did: DefId) -> bool;
+    fn is_foreign_item(&self, did: DefId) -> bool;
     fn is_static_method(&self, did: DefId) -> bool;
     fn is_statically_included_foreign_item(&self, id: ast::NodeId) -> bool;
     fn is_typedef(&self, did: DefId) -> bool;
@@ -274,7 +274,6 @@ pub trait CrateStore<'tcx> {
     fn extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option<ast::CrateNum>;
     fn encode_metadata<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
                            reexports: &def::ExportMap,
-                           item_symbols: &RefCell<NodeMap<String>>,
                            link_meta: &LinkMeta,
                            reachable: &NodeSet,
                            mir_map: &MirMap<'tcx>,
@@ -347,12 +346,12 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
         bug!("visible_parent_map")
     }
     fn item_name(&self, def: DefId) -> ast::Name { bug!("item_name") }
+    fn opt_item_name(&self, def: DefId) -> Option<ast::Name> { bug!("opt_item_name") }
     fn item_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)
                            -> ty::GenericPredicates<'tcx> { bug!("item_predicates") }
     fn item_super_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)
                                  -> ty::GenericPredicates<'tcx> { bug!("item_super_predicates") }
     fn item_attrs(&self, def_id: DefId) -> Vec<ast::Attribute> { bug!("item_attrs") }
-    fn item_symbol(&self, def: DefId) -> String { bug!("item_symbol") }
     fn trait_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)-> ty::TraitDef<'tcx>
         { bug!("trait_def") }
     fn adt_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::AdtDefMaster<'tcx>
@@ -399,6 +398,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     fn is_default_impl(&self, impl_did: DefId) -> bool { bug!("is_default_impl") }
     fn is_extern_item<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, did: DefId) -> bool
         { bug!("is_extern_item") }
+    fn is_foreign_item(&self, did: DefId) -> bool { bug!("is_foreign_item") }
     fn is_static_method(&self, did: DefId) -> bool { bug!("is_static_method") }
     fn is_statically_included_foreign_item(&self, id: ast::NodeId) -> bool { false }
     fn is_typedef(&self, did: DefId) -> bool { bug!("is_typedef") }
@@ -481,7 +481,6 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     fn extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option<ast::CrateNum> { None }
     fn encode_metadata<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
                            reexports: &def::ExportMap,
-                           item_symbols: &RefCell<NodeMap<String>>,
                            link_meta: &LinkMeta,
                            reachable: &NodeSet,
                            mir_map: &MirMap<'tcx>,
index 41b27a48b29f8b789775eb60b7e09d1ed4a2dd6a..fc1294c86c44f518e1b9a7faa5caba80c5e3da20 100644 (file)
@@ -168,9 +168,8 @@ fn build_nodeid_to_index(decl: Option<&hir::FnDecl>,
     // into cfg itself?  i.e. introduce a fn-based flow-graph in
     // addition to the current block-based flow-graph, rather than
     // have to put traversals like this here?
-    match decl {
-        None => {}
-        Some(decl) => add_entries_from_fn_decl(&mut index, decl, cfg.entry)
+    if let Some(decl) = decl {
+        add_entries_from_fn_decl(&mut index, decl, cfg.entry);
     }
 
     cfg.graph.each_node(|node_idx, node| {
@@ -660,8 +659,8 @@ fn set_bit(words: &mut [usize], bit: usize) -> bool {
 }
 
 fn bit_str(bit: usize) -> String {
-    let byte = bit >> 8;
-    let lobits = 1 << (bit & 0xFF);
+    let byte = bit >> 3;
+    let lobits = 1 << (bit & 0b111);
     format!("[{}:{}-{:02x}]", bit, byte, lobits)
 }
 
index cc6b83fccf92cdd5e8813a9d5323dc8ddd7c7dc6..2b59e603cc897f7b33f80846304942b0eda1106c 100644 (file)
@@ -26,6 +26,7 @@ use lint;
 use std::collections::HashSet;
 use syntax::{ast, codemap};
 use syntax::attr;
+use syntax_pos;
 
 // Any local node that may call something in its body block should be
 // explored. For example, if it's a live NodeItem that is a
@@ -84,36 +85,35 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
         }
     }
 
-    fn lookup_and_handle_definition(&mut self, id: &ast::NodeId) {
+    fn lookup_and_handle_definition(&mut self, id: ast::NodeId) {
         use ty::TypeVariants::{TyEnum, TyStruct};
 
         // If `bar` is a trait item, make sure to mark Foo as alive in `Foo::bar`
-        self.tcx.tables.borrow().item_substs.get(id)
+        self.tcx.tables.borrow().item_substs.get(&id)
             .and_then(|substs| substs.substs.self_ty())
             .map(|ty| match ty.sty {
                 TyEnum(tyid, _) | TyStruct(tyid, _) => self.check_def_id(tyid.did),
                 _ => (),
             });
 
-        self.tcx.def_map.borrow().get(id).map(|def| {
-            match def.full_def() {
-                Def::Const(_) | Def::AssociatedConst(..) => {
-                    self.check_def_id(def.def_id());
-                }
-                _ if self.ignore_non_const_paths => (),
-                Def::PrimTy(_) => (),
-                Def::SelfTy(..) => (),
-                Def::Variant(enum_id, variant_id) => {
-                    self.check_def_id(enum_id);
-                    if !self.ignore_variant_stack.contains(&variant_id) {
-                        self.check_def_id(variant_id);
-                    }
-                }
-                _ => {
-                    self.check_def_id(def.def_id());
+        let def = self.tcx.expect_def(id);
+        match def {
+            Def::Const(_) | Def::AssociatedConst(..) => {
+                self.check_def_id(def.def_id());
+            }
+            _ if self.ignore_non_const_paths => (),
+            Def::PrimTy(_) => (),
+            Def::SelfTy(..) => (),
+            Def::Variant(enum_id, variant_id) => {
+                self.check_def_id(enum_id);
+                if !self.ignore_variant_stack.contains(&variant_id) {
+                    self.check_def_id(variant_id);
                 }
             }
-        });
+            _ => {
+                self.check_def_id(def.def_id());
+            }
+        }
     }
 
     fn lookup_and_handle_method(&mut self, id: ast::NodeId) {
@@ -138,10 +138,10 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
 
     fn handle_field_pattern_match(&mut self, lhs: &hir::Pat,
                                   pats: &[codemap::Spanned<hir::FieldPat>]) {
-        let def = self.tcx.def_map.borrow().get(&lhs.id).unwrap().full_def();
-        let pat_ty = self.tcx.node_id_to_type(lhs.id);
-        let variant = match pat_ty.sty {
-            ty::TyStruct(adt, _) | ty::TyEnum(adt, _) => adt.variant_of_def(def),
+        let variant = match self.tcx.node_id_to_type(lhs.id).sty {
+            ty::TyStruct(adt, _) | ty::TyEnum(adt, _) => {
+                adt.variant_of_def(self.tcx.expect_def(lhs.id))
+            }
             _ => span_bug!(lhs.span, "non-ADT in struct pattern")
         };
         for pat in pats {
@@ -161,12 +161,9 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
             }
             scanned.insert(id);
 
-            match self.tcx.map.find(id) {
-                Some(ref node) => {
-                    self.live_symbols.insert(id);
-                    self.visit_node(node);
-                }
-                None => (),
+            if let Some(ref node) = self.tcx.map.find(id) {
+                self.live_symbols.insert(id);
+                self.visit_node(node);
             }
         }
     }
@@ -219,7 +216,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
 impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> {
 
     fn visit_variant_data(&mut self, def: &hir::VariantData, _: ast::Name,
-                        _: &hir::Generics, _: ast::NodeId, _: codemap::Span) {
+                        _: &hir::Generics, _: ast::NodeId, _: syntax_pos::Span) {
         let has_extern_repr = self.struct_has_extern_repr;
         let inherited_pub_visibility = self.inherited_pub_visibility;
         let live_fields = def.fields().iter().filter(|f| {
@@ -272,7 +269,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> {
             }
             _ if pat_util::pat_is_const(&def_map.borrow(), pat) => {
                 // it might be the only use of a const
-                self.lookup_and_handle_definition(&pat.id)
+                self.lookup_and_handle_definition(pat.id)
             }
             _ => ()
         }
@@ -283,12 +280,12 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> {
     }
 
     fn visit_path(&mut self, path: &hir::Path, id: ast::NodeId) {
-        self.lookup_and_handle_definition(&id);
+        self.lookup_and_handle_definition(id);
         intravisit::walk_path(self, path);
     }
 
     fn visit_path_list_item(&mut self, path: &hir::Path, item: &hir::PathListItem) {
-        self.lookup_and_handle_definition(&item.node.id());
+        self.lookup_and_handle_definition(item.node.id());
         intravisit::walk_path_list_item(self, path, item);
     }
 }
@@ -373,9 +370,8 @@ fn create_and_seed_worklist<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
 
     // Seed entry point
-    match *tcx.sess.entry_fn.borrow() {
-        Some((id, _)) => worklist.push(id),
-        None => ()
+    if let Some((id, _)) = *tcx.sess.entry_fn.borrow() {
+        worklist.push(id);
     }
 
     // Seed implemented trait items
@@ -465,16 +461,14 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
         // method of a private type is used, but the type itself is never
         // called directly.
         let impl_items = self.tcx.impl_items.borrow();
-        match self.tcx.inherent_impls.borrow().get(&self.tcx.map.local_def_id(id)) {
-            None => (),
-            Some(impl_list) => {
-                for impl_did in impl_list.iter() {
-                    for item_did in impl_items.get(impl_did).unwrap().iter() {
-                        if let Some(item_node_id) =
-                                self.tcx.map.as_local_node_id(item_did.def_id()) {
-                            if self.live_symbols.contains(&item_node_id) {
-                                return true;
-                            }
+        if let Some(impl_list) =
+                self.tcx.inherent_impls.borrow().get(&self.tcx.map.local_def_id(id)) {
+            for impl_did in impl_list.iter() {
+                for item_did in impl_items.get(impl_did).unwrap().iter() {
+                    if let Some(item_node_id) =
+                            self.tcx.map.as_local_node_id(item_did.def_id()) {
+                        if self.live_symbols.contains(&item_node_id) {
+                            return true;
                         }
                     }
                 }
@@ -485,7 +479,7 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
 
     fn warn_dead_code(&mut self,
                       id: ast::NodeId,
-                      span: codemap::Span,
+                      span: syntax_pos::Span,
                       name: ast::Name,
                       node_type: &str) {
         let name = name.as_str();
index 0b398fd0d47c510c61502a6dd1b4afa1955f00f7..cf6905ecf439ab24400902c25129f22647894715 100644 (file)
@@ -105,9 +105,8 @@ fn calculate_type(sess: &session::Session,
         // If the global prefer_dynamic switch is turned off, first attempt
         // static linkage (this can fail).
         config::CrateTypeExecutable if !sess.opts.cg.prefer_dynamic => {
-            match attempt_static(sess) {
-                Some(v) => return v,
-                None => {}
+            if let Some(v) = attempt_static(sess) {
+                return v;
             }
         }
 
@@ -119,9 +118,8 @@ fn calculate_type(sess: &session::Session,
         // to be found, we generate some nice pretty errors.
         config::CrateTypeStaticlib |
         config::CrateTypeCdylib => {
-            match attempt_static(sess) {
-                Some(v) => return v,
-                None => {}
+            if let Some(v) = attempt_static(sess) {
+                return v;
             }
             for cnum in sess.cstore.crates() {
                 let src = sess.cstore.used_crate_source(cnum);
@@ -136,9 +134,8 @@ fn calculate_type(sess: &session::Session,
         // to try to eagerly statically link all dependencies. This is normally
         // done for end-product dylibs, not intermediate products.
         config::CrateTypeDylib if !sess.opts.cg.prefer_dynamic => {
-            match attempt_static(sess) {
-                Some(v) => return v,
-                None => {}
+            if let Some(v) = attempt_static(sess) {
+                return v;
             }
         }
 
index b62368c2a98781e33b2ad5d9dfef2fe887dfb299..6fe98119c706088f2d68b2a645237e7d7abafe5e 100644 (file)
@@ -18,7 +18,7 @@ use ty::{self, Ty, TyCtxt};
 use ty::MethodCall;
 
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use hir;
 use hir::intravisit;
 use hir::intravisit::{FnKind, Visitor};
@@ -172,7 +172,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
                 self.require_unsafe(expr.span, "use of inline assembly");
             }
             hir::ExprPath(..) => {
-                if let Def::Static(_, true) = self.tcx.resolve_expr(expr) {
+                if let Def::Static(_, true) = self.tcx.expect_def(expr.id) {
                     self.require_unsafe(expr.span, "use of mutable static");
                 }
             }
index 602889375e970250cb6756d1f751d1769e829416..23a261400ed076690f3cddaaf93d789e916947eb 100644 (file)
@@ -15,8 +15,8 @@ use hir::def_id::{CRATE_DEF_INDEX};
 use session::{config, Session};
 use syntax::ast::NodeId;
 use syntax::attr;
-use syntax::codemap::Span;
 use syntax::entry::EntryPointType;
+use syntax_pos::Span;
 use hir::{Item, ItemFn};
 use hir::intravisit::Visitor;
 
index 4cee8c5d89ae62e9f8b8a0f946e7a1e9d9c45a98..b73ecbdf0353d4507187f66791b4b3a2982fa8e0 100644 (file)
@@ -30,7 +30,7 @@ use hir::{self, PatKind};
 
 use syntax::ast;
 use syntax::ptr::P;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 ///////////////////////////////////////////////////////////////////////////
 // The Delegate trait
@@ -271,10 +271,19 @@ enum PassArgs {
 
 impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
     pub fn new(delegate: &'a mut (Delegate<'tcx>+'a),
-               infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self
+               infcx: &'a InferCtxt<'a, 'gcx, 'tcx>)
+               -> Self
+    {
+        ExprUseVisitor::with_options(delegate, infcx, mc::MemCategorizationOptions::default())
+    }
+
+    pub fn with_options(delegate: &'a mut (Delegate<'tcx>+'a),
+                        infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+                        options: mc::MemCategorizationOptions)
+               -> Self
     {
         ExprUseVisitor {
-            mc: mc::MemCategorizationContext::new(infcx),
+            mc: mc::MemCategorizationContext::with_options(infcx, options),
             delegate: delegate
         }
     }
@@ -612,8 +621,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
         match local.init {
             None => {
                 let delegate = &mut self.delegate;
-                pat_util::pat_bindings(&self.mc.infcx.tcx.def_map, &local.pat,
-                                       |_, id, span, _| {
+                pat_util::pat_bindings(&local.pat, |_, id, span, _| {
                     delegate.decl_without_init(id, span);
                 })
             }
@@ -736,26 +744,23 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
 
         for i in 0..autoderefs {
             let deref_id = ty::MethodCall::autoderef(expr.id, i as u32);
-            match self.mc.infcx.node_method_ty(deref_id) {
-                None => {}
-                Some(method_ty) => {
-                    let cmt = return_if_err!(self.mc.cat_expr_autoderefd(expr, i));
-
-                    // the method call infrastructure should have
-                    // replaced all late-bound regions with variables:
-                    let self_ty = method_ty.fn_sig().input(0);
-                    let self_ty = self.tcx().no_late_bound_regions(&self_ty).unwrap();
-
-                    let (m, r) = match self_ty.sty {
-                        ty::TyRef(r, ref m) => (m.mutbl, r),
-                        _ => span_bug!(expr.span,
-                                "bad overloaded deref type {:?}",
-                                method_ty)
-                    };
-                    let bk = ty::BorrowKind::from_mutbl(m);
-                    self.delegate.borrow(expr.id, expr.span, cmt,
-                                         *r, bk, AutoRef);
-                }
+            if let Some(method_ty) = self.mc.infcx.node_method_ty(deref_id) {
+                let cmt = return_if_err!(self.mc.cat_expr_autoderefd(expr, i));
+
+                // the method call infrastructure should have
+                // replaced all late-bound regions with variables:
+                let self_ty = method_ty.fn_sig().input(0);
+                let self_ty = self.tcx().no_late_bound_regions(&self_ty).unwrap();
+
+                let (m, r) = match self_ty.sty {
+                    ty::TyRef(r, ref m) => (m.mutbl, r),
+                    _ => span_bug!(expr.span,
+                                   "bad overloaded deref type {:?}",
+                                   method_ty)
+                };
+                let bk = ty::BorrowKind::from_mutbl(m);
+                self.delegate.borrow(expr.id, expr.span, cmt,
+                                     *r, bk, AutoRef);
             }
         }
     }
@@ -932,23 +937,16 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
         debug!("determine_pat_move_mode cmt_discr={:?} pat={:?}", cmt_discr,
                pat);
         return_if_err!(self.mc.cat_pattern(cmt_discr, pat, |_mc, cmt_pat, pat| {
-            let def_map = &self.tcx().def_map;
-            if pat_util::pat_is_binding(&def_map.borrow(), pat) {
-                match pat.node {
-                    PatKind::Ident(hir::BindByRef(_), _, _) =>
-                        mode.lub(BorrowingMatch),
-                    PatKind::Ident(hir::BindByValue(_), _, _) => {
-                        match copy_or_move(self.mc.infcx, &cmt_pat, PatBindingMove) {
-                            Copy => mode.lub(CopyingMatch),
-                            Move(_) => mode.lub(MovingMatch),
-                        }
-                    }
-                    _ => {
-                        span_bug!(
-                            pat.span,
-                            "binding pattern not an identifier");
+            match pat.node {
+                PatKind::Binding(hir::BindByRef(..), _, _) =>
+                    mode.lub(BorrowingMatch),
+                PatKind::Binding(hir::BindByValue(..), _, _) => {
+                    match copy_or_move(self.mc.infcx, &cmt_pat, PatBindingMove) {
+                        Copy => mode.lub(CopyingMatch),
+                        Move(..) => mode.lub(MovingMatch),
                     }
                 }
+                _ => {}
             }
         }));
     }
@@ -963,88 +961,45 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
         debug!("walk_pat cmt_discr={:?} pat={:?}", cmt_discr,
                pat);
 
+        let tcx = &self.tcx();
         let mc = &self.mc;
         let infcx = self.mc.infcx;
-        let def_map = &self.tcx().def_map;
         let delegate = &mut self.delegate;
         return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |mc, cmt_pat, pat| {
-            if pat_util::pat_is_binding(&def_map.borrow(), pat) {
-                debug!("binding cmt_pat={:?} pat={:?} match_mode={:?}",
-                       cmt_pat,
-                       pat,
-                       match_mode);
-
-                // pat_ty: the type of the binding being produced.
-                let pat_ty = return_if_err!(infcx.node_ty(pat.id));
-
-                // Each match binding is effectively an assignment to the
-                // binding being produced.
-                let def = def_map.borrow().get(&pat.id).unwrap().full_def();
-                match mc.cat_def(pat.id, pat.span, pat_ty, def) {
-                    Ok(binding_cmt) => {
+            match pat.node {
+                PatKind::Binding(bmode, _, _) => {
+                    debug!("binding cmt_pat={:?} pat={:?} match_mode={:?}",
+                           cmt_pat,
+                           pat,
+                           match_mode);
+
+                    // pat_ty: the type of the binding being produced.
+                    let pat_ty = return_if_err!(infcx.node_ty(pat.id));
+
+                    // Each match binding is effectively an assignment to the
+                    // binding being produced.
+                    if let Ok(binding_cmt) = mc.cat_def(pat.id, pat.span, pat_ty,
+                                                        tcx.expect_def(pat.id)) {
                         delegate.mutate(pat.id, pat.span, binding_cmt, MutateMode::Init);
                     }
-                    Err(_) => { }
-                }
 
-                // It is also a borrow or copy/move of the value being matched.
-                match pat.node {
-                    PatKind::Ident(hir::BindByRef(m), _, _) => {
-                        if let ty::TyRef(&r, _) = pat_ty.sty {
-                            let bk = ty::BorrowKind::from_mutbl(m);
-                            delegate.borrow(pat.id, pat.span, cmt_pat,
-                                            r, bk, RefBinding);
+                    // It is also a borrow or copy/move of the value being matched.
+                    match bmode {
+                        hir::BindByRef(m) => {
+                            if let ty::TyRef(&r, _) = pat_ty.sty {
+                                let bk = ty::BorrowKind::from_mutbl(m);
+                                delegate.borrow(pat.id, pat.span, cmt_pat,
+                                                r, bk, RefBinding);
+                            }
+                        }
+                        hir::BindByValue(..) => {
+                            let mode = copy_or_move(infcx, &cmt_pat, PatBindingMove);
+                            debug!("walk_pat binding consuming pat");
+                            delegate.consume_pat(pat, cmt_pat, mode);
                         }
                     }
-                    PatKind::Ident(hir::BindByValue(_), _, _) => {
-                        let mode = copy_or_move(infcx, &cmt_pat, PatBindingMove);
-                        debug!("walk_pat binding consuming pat");
-                        delegate.consume_pat(pat, cmt_pat, mode);
-                    }
-                    _ => {
-                        span_bug!(
-                            pat.span,
-                            "binding pattern not an identifier");
-                    }
-                }
-            } else {
-                match pat.node {
-                    PatKind::Vec(_, Some(ref slice_pat), _) => {
-                        // The `slice_pat` here creates a slice into
-                        // the original vector.  This is effectively a
-                        // borrow of the elements of the vector being
-                        // matched.
-
-                        let (slice_cmt, slice_mutbl, slice_r) =
-                            return_if_err!(mc.cat_slice_pattern(cmt_pat, &slice_pat));
-
-                        // Note: We declare here that the borrow
-                        // occurs upon entering the `[...]`
-                        // pattern. This implies that something like
-                        // `[a; b]` where `a` is a move is illegal,
-                        // because the borrow is already in effect.
-                        // In fact such a move would be safe-ish, but
-                        // it effectively *requires* that we use the
-                        // nulling out semantics to indicate when a
-                        // value has been moved, which we are trying
-                        // to move away from.  Otherwise, how can we
-                        // indicate that the first element in the
-                        // vector has been moved?  Eventually, we
-                        // could perhaps modify this rule to permit
-                        // `[..a, b]` where `b` is a move, because in
-                        // that case we can adjust the length of the
-                        // original vec accordingly, but we'd have to
-                        // make trans do the right thing, and it would
-                        // only work for `Box<[T]>`s. It seems simpler
-                        // to just require that people call
-                        // `vec.pop()` or `vec.unshift()`.
-                        let slice_bk = ty::BorrowKind::from_mutbl(slice_mutbl);
-                        delegate.borrow(pat.id, pat.span,
-                                        slice_cmt, slice_r,
-                                        slice_bk, RefBinding);
-                    }
-                    _ => { }
                 }
+                _ => {}
             }
         }));
 
@@ -1053,19 +1008,11 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
         // to the above loop's visit of than the bindings that form
         // the leaves of the pattern tree structure.
         return_if_err!(mc.cat_pattern(cmt_discr, pat, |mc, cmt_pat, pat| {
-            let def_map = def_map.borrow();
-            let tcx = infcx.tcx;
-
             match pat.node {
-                PatKind::TupleStruct(..) | PatKind::Path(..) | PatKind::QPath(..) |
-                PatKind::Ident(_, _, None) | PatKind::Struct(..) => {
-                    match def_map.get(&pat.id).map(|d| d.full_def()) {
-                        None => {
-                            // no definition found: pat is not a
-                            // struct or enum pattern.
-                        }
-
-                        Some(Def::Variant(enum_did, variant_did)) => {
+                PatKind::Struct(..) | PatKind::TupleStruct(..) |
+                PatKind::Path(..) | PatKind::QPath(..) => {
+                    match tcx.expect_def(pat.id) {
+                        Def::Variant(enum_did, variant_did) => {
                             let downcast_cmt =
                                 if tcx.lookup_adt_def(enum_did).is_univariant() {
                                     cmt_pat
@@ -1081,7 +1028,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                             delegate.matched_pat(pat, downcast_cmt, match_mode);
                         }
 
-                        Some(Def::Struct(..)) | Some(Def::TyAlias(..)) => {
+                        Def::Struct(..) | Def::TyAlias(..) => {
                             // A struct (in either the value or type
                             // namespace; we encounter the former on
                             // e.g. patterns for unit structs).
@@ -1093,15 +1040,13 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                             delegate.matched_pat(pat, cmt_pat, match_mode);
                         }
 
-                        Some(Def::Const(..)) |
-                        Some(Def::AssociatedConst(..)) |
-                        Some(Def::Local(..)) => {
+                        Def::Const(..) | Def::AssociatedConst(..) => {
                             // This is a leaf (i.e. identifier binding
                             // or constant value to match); thus no
                             // `matched_pat` call.
                         }
 
-                        Some(def) => {
+                        def => {
                             // An enum type should never be in a pattern.
                             // Remaining cases are e.g. Def::Fn, to
                             // which identifiers within patterns
@@ -1121,16 +1066,10 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                     }
                 }
 
-                PatKind::Ident(_, _, Some(_)) => {
-                    // Do nothing; this is a binding (not an enum
-                    // variant or struct), and the cat_pattern call
-                    // will visit the substructure recursively.
-                }
-
-                PatKind::Wild | PatKind::Tup(..) | PatKind::Box(..) |
+                PatKind::Wild | PatKind::Tuple(..) | PatKind::Box(..) |
                 PatKind::Ref(..) | PatKind::Lit(..) | PatKind::Range(..) |
-                PatKind::Vec(..) => {
-                    // Similarly, each of these cases does not
+                PatKind::Vec(..) | PatKind::Binding(..) => {
+                    // Each of these cases does not
                     // correspond to an enum variant or struct, so we
                     // do not do any `matched_pat` calls for these
                     // cases either.
index 07e69d85ff41bb811f451d7f96d69bbea1206767..d753381d71e252eb5e9ef15f5a76414a0d581d5e 100644 (file)
@@ -18,7 +18,7 @@ use ty::layout::{LayoutError, Pointer, SizeSkeleton};
 
 use syntax::abi::Abi::RustIntrinsic;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use hir::intravisit::{self, Visitor, FnKind};
 use hir;
 
@@ -156,7 +156,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ItemVisitor<'a, 'tcx> {
 impl<'a, 'gcx, 'tcx, 'v> Visitor<'v> for ExprVisitor<'a, 'gcx, 'tcx> {
     fn visit_expr(&mut self, expr: &hir::Expr) {
         if let hir::ExprPath(..) = expr.node {
-            match self.infcx.tcx.resolve_expr(expr) {
+            match self.infcx.tcx.expect_def(expr.id) {
                 Def::Fn(did) if self.def_id_is_transmute(did) => {
                     let typ = self.infcx.tcx.node_id_to_type(expr.id);
                     match typ.sty {
index 853477ac97c161035c1deb06094170bdcda56aec..960305e10488d2fbd6f64fdfb529e9b5444ef2e6 100644 (file)
@@ -37,7 +37,7 @@ use hir;
 
 // The actual lang items defined come at the end of this file in one handy table.
 // So you probably just want to nip down to the end.
-macro_rules! lets_do_this {
+macro_rules! language_item_table {
     (
         $( $variant:ident, $name:expr, $method:ident; )*
     ) => {
@@ -269,7 +269,7 @@ pub fn collect_language_items(session: &Session,
     }
 }
 
-lets_do_this! {
+language_item_table! {
 //  Variant name,                    Name,                      Method name;
     CharImplItem,                    "char",                    char_impl;
     StrImplItem,                     "str",                     str_impl;
index be8caeb436a3499c0edcab852afe08477af7323e..ea3765c76f89b2ec425fb845286e69338d7e685d 100644 (file)
@@ -123,9 +123,10 @@ use std::io::prelude::*;
 use std::io;
 use std::rc::Rc;
 use syntax::ast::{self, NodeId};
-use syntax::codemap::{BytePos, original_sp, Span};
+use syntax::codemap::original_sp;
 use syntax::parse::token::keywords;
 use syntax::ptr::P;
+use syntax_pos::{BytePos, Span};
 
 use hir::Expr;
 use hir;
@@ -380,9 +381,7 @@ fn visit_fn(ir: &mut IrMaps,
     debug!("creating fn_maps: {:?}", &fn_maps as *const IrMaps);
 
     for arg in &decl.inputs {
-        pat_util::pat_bindings(&ir.tcx.def_map,
-                               &arg.pat,
-                               |_bm, arg_id, _x, path1| {
+        pat_util::pat_bindings(&arg.pat, |_bm, arg_id, _x, path1| {
             debug!("adding argument {}", arg_id);
             let name = path1.node;
             fn_maps.add_variable(Arg(arg_id, name));
@@ -415,7 +414,7 @@ fn visit_fn(ir: &mut IrMaps,
 }
 
 fn visit_local(ir: &mut IrMaps, local: &hir::Local) {
-    pat_util::pat_bindings(&ir.tcx.def_map, &local.pat, |_, p_id, sp, path1| {
+    pat_util::pat_bindings(&local.pat, |_, p_id, sp, path1| {
         debug!("adding local variable {}", p_id);
         let name = path1.node;
         ir.add_live_node_for_node(p_id, VarDefNode(sp));
@@ -429,7 +428,7 @@ fn visit_local(ir: &mut IrMaps, local: &hir::Local) {
 
 fn visit_arm(ir: &mut IrMaps, arm: &hir::Arm) {
     for pat in &arm.pats {
-        pat_util::pat_bindings(&ir.tcx.def_map, &pat, |bm, p_id, sp, path1| {
+        pat_util::pat_bindings(&pat, |bm, p_id, sp, path1| {
             debug!("adding local variable {} from match with bm {:?}",
                    p_id, bm);
             let name = path1.node;
@@ -447,7 +446,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
     match expr.node {
       // live nodes required for uses or definitions of variables:
       hir::ExprPath(..) => {
-        let def = ir.tcx.def_map.borrow().get(&expr.id).unwrap().full_def();
+        let def = ir.tcx.expect_def(expr.id);
         debug!("expr {}: path that leads to {:?}", expr.id, def);
         if let Def::Local(..) = def {
             ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
@@ -589,7 +588,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
     fn pat_bindings<F>(&mut self, pat: &hir::Pat, mut f: F) where
         F: FnMut(&mut Liveness<'a, 'tcx>, LiveNode, Variable, Span, NodeId),
     {
-        pat_util::pat_bindings(&self.ir.tcx.def_map, pat, |_bm, p_id, sp, _n| {
+        pat_util::pat_bindings(pat, |_bm, p_id, sp, _n| {
             let ln = self.live_node(p_id, sp);
             let var = self.variable(p_id, sp);
             f(self, ln, var, sp, p_id);
@@ -599,11 +598,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
     fn arm_pats_bindings<F>(&mut self, pat: Option<&hir::Pat>, f: F) where
         F: FnMut(&mut Liveness<'a, 'tcx>, LiveNode, Variable, Span, NodeId),
     {
-        match pat {
-            Some(pat) => {
-                self.pat_bindings(pat, f);
-            }
-            None => {}
+        if let Some(pat) = pat {
+            self.pat_bindings(pat, f);
         }
     }
 
@@ -697,8 +693,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             Some(_) => {
                 // Refers to a labeled loop. Use the results of resolve
                 // to find with one
-                match self.ir.tcx.def_map.borrow().get(&id).map(|d| d.full_def()) {
-                    Some(Def::Label(loop_id)) => loop_id,
+                match self.ir.tcx.expect_def(id) {
+                    Def::Label(loop_id) => loop_id,
                     _ => span_bug!(sp, "label on break/loop \
                                         doesn't refer to a loop")
                 }
@@ -1271,7 +1267,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
     fn access_path(&mut self, expr: &Expr, succ: LiveNode, acc: u32)
                    -> LiveNode {
-        match self.ir.tcx.def_map.borrow().get(&expr.id).unwrap().full_def() {
+        match self.ir.tcx.expect_def(expr.id) {
           Def::Local(_, nid) => {
             let ln = self.live_node(expr.id, expr.span);
             if acc != 0 {
@@ -1536,9 +1532,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
     fn check_lvalue(&mut self, expr: &Expr) {
         match expr.node {
             hir::ExprPath(..) => {
-                if let Def::Local(_, nid) = self.ir.tcx.def_map.borrow().get(&expr.id)
-                                                                      .unwrap()
-                                                                      .full_def() {
+                if let Def::Local(_, nid) = self.ir.tcx.expect_def(expr.id) {
                     // Assignment to an immutable variable or argument: only legal
                     // if there is no later assignment. If this local is actually
                     // mutable, then check for a reassignment to flag the mutability
@@ -1567,9 +1561,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
     fn warn_about_unused_args(&self, decl: &hir::FnDecl, entry_ln: LiveNode) {
         for arg in &decl.inputs {
-            pat_util::pat_bindings(&self.ir.tcx.def_map,
-                                   &arg.pat,
-                                   |_bm, p_id, sp, path1| {
+            pat_util::pat_bindings(&arg.pat, |_bm, p_id, sp, path1| {
                 let var = self.variable(p_id, sp);
                 // Ignore unused self.
                 let name = path1.node;
index 3999b02425de6737d6e84ef9d2d653b0001fefe6..9e64192b1f8f40d34fd91e2bec66e85d9fe97053 100644 (file)
@@ -80,9 +80,10 @@ use ty::adjustment;
 use ty::{self, Ty, TyCtxt};
 
 use hir::{MutImmutable, MutMutable, PatKind};
+use hir::pat_util::EnumerateAndAdjustIterator;
 use hir;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use std::fmt;
 use std::rc::Rc;
@@ -227,10 +228,10 @@ fn deref_kind(t: Ty, context: DerefKindContext) -> McResult<deref_kind> {
             Ok(deref_interior(InteriorField(PositionalField(0))))
         }
 
-        ty::TyArray(_, _) | ty::TySlice(_) | ty::TyStr => {
+        ty::TyArray(_, _) | ty::TySlice(_) => {
             // no deref of indexed content without supplying InteriorOffsetKind
             if let Some(context) = context {
-                Ok(deref_interior(InteriorElement(context, element_kind(t))))
+                Ok(deref_interior(InteriorElement(context, ElementKind::VecElement)))
             } else {
                 Err(())
             }
@@ -258,6 +259,18 @@ impl ast_node for hir::Pat {
 #[derive(Copy, Clone)]
 pub struct MemCategorizationContext<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     pub infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    options: MemCategorizationOptions,
+}
+
+#[derive(Copy, Clone, Default)]
+pub struct MemCategorizationOptions {
+    // If true, then when analyzing a closure upvar, if the closure
+    // has a missing kind, we treat it like a Fn closure. When false,
+    // we ICE if the closure has a missing kind. Should be false
+    // except during closure kind inference. It is used by the
+    // mem-categorization code to be able to have stricter assertions
+    // (which are always true except during upvar inference).
+    pub during_closure_kind_inference: bool,
 }
 
 pub type McResult<T> = Result<T, ()>;
@@ -305,7 +318,7 @@ impl MutabilityCategory {
     fn from_local(tcx: TyCtxt, id: ast::NodeId) -> MutabilityCategory {
         let ret = match tcx.map.get(id) {
             ast_map::NodeLocal(p) => match p.node {
-                PatKind::Ident(bind_mode, _, _) => {
+                PatKind::Binding(bind_mode, _, _) => {
                     if bind_mode == hir::BindByValue(hir::MutMutable) {
                         McDeclared
                     } else {
@@ -361,7 +374,16 @@ impl MutabilityCategory {
 impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
     pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>)
                -> MemCategorizationContext<'a, 'gcx, 'tcx> {
-        MemCategorizationContext { infcx: infcx }
+        MemCategorizationContext::with_options(infcx, MemCategorizationOptions::default())
+    }
+
+    pub fn with_options(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+                        options: MemCategorizationOptions)
+                        -> MemCategorizationContext<'a, 'gcx, 'tcx> {
+        MemCategorizationContext {
+            infcx: infcx,
+            options: options,
+        }
     }
 
     fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
@@ -397,7 +419,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
         // *being borrowed* is.  But ideally we would put in a more
         // fundamental fix to this conflated use of the node id.
         let ret_ty = match pat.node {
-            PatKind::Ident(hir::BindByRef(_), _, _) => {
+            PatKind::Binding(hir::BindByRef(_), _, _) => {
                 // a bind-by-ref means that the base_ty will be the type of the ident itself,
                 // but what we want here is the type of the underlying value being borrowed.
                 // So peel off one-level, turning the &T into T.
@@ -516,8 +538,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
           }
 
           hir::ExprPath(..) => {
-            let def = self.tcx().def_map.borrow().get(&expr.id).unwrap().full_def();
-            self.cat_def(expr.id, expr.span, expr_ty, def)
+            self.cat_def(expr.id, expr.span, expr_ty, self.tcx().expect_def(expr.id))
           }
 
           hir::ExprType(ref e, _) => {
@@ -584,10 +605,20 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
                               self.cat_upvar(id, span, var_id, fn_node_id, kind)
                           }
                           None => {
-                              span_bug!(
-                                  span,
-                                  "No closure kind for {:?}",
-                                  closure_id);
+                              if !self.options.during_closure_kind_inference {
+                                  span_bug!(
+                                      span,
+                                      "No closure kind for {:?}",
+                                      closure_id);
+                              }
+
+                              // during closure kind inference, we
+                              // don't know the closure kind yet, but
+                              // it's ok because we detect that we are
+                              // accessing an upvar and handle that
+                              // case specially anyhow. Use Fn
+                              // arbitrarily.
+                              self.cat_upvar(id, span, var_id, fn_node_id, ty::ClosureKind::Fn)
                           }
                       }
                   }
@@ -980,18 +1011,19 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
         let method_call = ty::MethodCall::expr(elt.id());
         let method_ty = self.infcx.node_method_ty(method_call);
 
-        let element_ty = match method_ty {
+        let (element_ty, element_kind) = match method_ty {
             Some(method_ty) => {
                 let ref_ty = self.overloaded_method_return_ty(method_ty);
                 base_cmt = self.cat_rvalue_node(elt.id(), elt.span(), ref_ty);
 
                 // FIXME(#20649) -- why are we using the `self_ty` as the element type...?
                 let self_ty = method_ty.fn_sig().input(0);
-                self.tcx().no_late_bound_regions(&self_ty).unwrap()
+                (self.tcx().no_late_bound_regions(&self_ty).unwrap(),
+                 ElementKind::OtherElement)
             }
             None => {
                 match base_cmt.ty.builtin_index() {
-                    Some(ty) => ty,
+                    Some(ty) => (ty, ElementKind::VecElement),
                     None => {
                         return Err(());
                     }
@@ -999,102 +1031,11 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
             }
         };
 
-        let m = base_cmt.mutbl.inherit();
-        let ret = interior(elt, base_cmt.clone(), base_cmt.ty,
-                           m, context, element_ty);
+        let interior_elem = InteriorElement(context, element_kind);
+        let ret =
+            self.cat_imm_interior(elt, base_cmt.clone(), element_ty, interior_elem);
         debug!("cat_index ret {:?}", ret);
         return Ok(ret);
-
-        fn interior<'tcx, N: ast_node>(elt: &N,
-                                       of_cmt: cmt<'tcx>,
-                                       vec_ty: Ty<'tcx>,
-                                       mutbl: MutabilityCategory,
-                                       context: InteriorOffsetKind,
-                                       element_ty: Ty<'tcx>) -> cmt<'tcx>
-        {
-            let interior_elem = InteriorElement(context, element_kind(vec_ty));
-            Rc::new(cmt_ {
-                id:elt.id(),
-                span:elt.span(),
-                cat:Categorization::Interior(of_cmt, interior_elem),
-                mutbl:mutbl,
-                ty:element_ty,
-                note: NoteNone
-            })
-        }
-    }
-
-    // Takes either a vec or a reference to a vec and returns the cmt for the
-    // underlying vec.
-    fn deref_vec<N:ast_node>(&self,
-                             elt: &N,
-                             base_cmt: cmt<'tcx>,
-                             context: InteriorOffsetKind)
-                             -> McResult<cmt<'tcx>>
-    {
-        let ret = match deref_kind(base_cmt.ty, Some(context))? {
-            deref_ptr(ptr) => {
-                // for unique ptrs, we inherit mutability from the
-                // owning reference.
-                let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr);
-
-                // the deref is explicit in the resulting cmt
-                Rc::new(cmt_ {
-                    id:elt.id(),
-                    span:elt.span(),
-                    cat:Categorization::Deref(base_cmt.clone(), 0, ptr),
-                    mutbl:m,
-                    ty: match base_cmt.ty.builtin_deref(false, ty::NoPreference) {
-                        Some(mt) => mt.ty,
-                        None => bug!("Found non-derefable type")
-                    },
-                    note: NoteNone
-                })
-            }
-
-            deref_interior(_) => {
-                base_cmt
-            }
-        };
-        debug!("deref_vec ret {:?}", ret);
-        Ok(ret)
-    }
-
-    /// Given a pattern P like: `[_, ..Q, _]`, where `vec_cmt` is the cmt for `P`, `slice_pat` is
-    /// the pattern `Q`, returns:
-    ///
-    /// * a cmt for `Q`
-    /// * the mutability and region of the slice `Q`
-    ///
-    /// These last two bits of info happen to be things that borrowck needs.
-    pub fn cat_slice_pattern(&self,
-                             vec_cmt: cmt<'tcx>,
-                             slice_pat: &hir::Pat)
-                             -> McResult<(cmt<'tcx>, hir::Mutability, ty::Region)> {
-        let slice_ty = self.node_ty(slice_pat.id)?;
-        let (slice_mutbl, slice_r) = vec_slice_info(slice_pat, slice_ty);
-        let context = InteriorOffsetKind::Pattern;
-        let cmt_vec = self.deref_vec(slice_pat, vec_cmt, context)?;
-        let cmt_slice = self.cat_index(slice_pat, cmt_vec, context)?;
-        return Ok((cmt_slice, slice_mutbl, slice_r));
-
-        /// In a pattern like [a, b, ..c], normally `c` has slice type, but if you have [a, b,
-        /// ..ref c], then the type of `ref c` will be `&&[]`, so to extract the slice details we
-        /// have to recurse through rptrs.
-        fn vec_slice_info(pat: &hir::Pat, slice_ty: Ty)
-                          -> (hir::Mutability, ty::Region) {
-            match slice_ty.sty {
-                ty::TyRef(r, ref mt) => match mt.ty.sty {
-                    ty::TySlice(_) => (mt.mutbl, *r),
-                    _ => vec_slice_info(pat, mt.ty),
-                },
-
-                _ => {
-                    span_bug!(pat.span,
-                              "type of slice pattern is not a slice");
-                }
-            }
-        }
     }
 
     pub fn cat_imm_interior<N:ast_node>(&self,
@@ -1195,18 +1136,10 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
 
         (*op)(self, cmt.clone(), pat);
 
-        let opt_def = if let Some(path_res) = self.tcx().def_map.borrow().get(&pat.id) {
-            if path_res.depth != 0 || path_res.base_def == Def::Err {
-                // Since patterns can be associated constants
-                // which are resolved during typeck, we might have
-                // some unresolved patterns reaching this stage
-                // without aborting
-                return Err(());
-            }
-            Some(path_res.full_def())
-        } else {
-            None
-        };
+        let opt_def = self.tcx().expect_def_or_none(pat.id);
+        if opt_def == Some(Def::Err) {
+            return Err(());
+        }
 
         // Note: This goes up here (rather than within the PatKind::TupleStruct arm
         // alone) because struct patterns can refer to struct types or
@@ -1225,14 +1158,13 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
             // _
           }
 
-          PatKind::TupleStruct(_, None) => {
-            // variant(..)
-          }
-          PatKind::TupleStruct(_, Some(ref subpats)) => {
+          PatKind::TupleStruct(_, ref subpats, ddpos) => {
             match opt_def {
-                Some(Def::Variant(..)) => {
+                Some(Def::Variant(enum_def, def_id)) => {
                     // variant(x, y, z)
-                    for (i, subpat) in subpats.iter().enumerate() {
+                    let expected_len = self.tcx().lookup_adt_def(enum_def)
+                                                 .variant_with_id(def_id).fields.len();
+                    for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
                         let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
 
                         let subcmt =
@@ -1244,7 +1176,16 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
                     }
                 }
                 Some(Def::Struct(..)) => {
-                    for (i, subpat) in subpats.iter().enumerate() {
+                    let expected_len = match self.pat_ty(&pat)?.sty {
+                        ty::TyStruct(adt_def, _) => {
+                            adt_def.struct_variant().fields.len()
+                        }
+                        ref ty => {
+                            span_bug!(pat.span, "tuple struct pattern unexpected type {:?}", ty);
+                        }
+                    };
+
+                    for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
                         let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
                         let cmt_field =
                             self.cat_imm_interior(
@@ -1267,11 +1208,11 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
             }
           }
 
-          PatKind::Path(..) | PatKind::QPath(..) | PatKind::Ident(_, _, None) => {
+          PatKind::Path(..) | PatKind::QPath(..) | PatKind::Binding(_, _, None) => {
               // Lone constant, or unit variant or identifier: ignore
           }
 
-          PatKind::Ident(_, _, Some(ref subpat)) => {
+          PatKind::Binding(_, _, Some(ref subpat)) => {
               self.cat_pattern_(cmt, &subpat, op)?;
           }
 
@@ -1284,9 +1225,13 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
             }
           }
 
-          PatKind::Tup(ref subpats) => {
+          PatKind::Tuple(ref subpats, ddpos) => {
             // (p1, ..., pN)
-            for (i, subpat) in subpats.iter().enumerate() {
+            let expected_len = match self.pat_ty(&pat)?.sty {
+                ty::TyTuple(ref tys) => tys.len(),
+                ref ty => span_bug!(pat.span, "tuple pattern unexpected type {:?}", ty),
+            };
+            for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
                 let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
                 let subcmt =
                     self.cat_imm_interior(
@@ -1306,15 +1251,12 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
 
           PatKind::Vec(ref before, ref slice, ref after) => {
               let context = InteriorOffsetKind::Pattern;
-              let vec_cmt = self.deref_vec(pat, cmt, context)?;
-              let elt_cmt = self.cat_index(pat, vec_cmt, context)?;
+              let elt_cmt = self.cat_index(pat, cmt, context)?;
               for before_pat in before {
                   self.cat_pattern_(elt_cmt.clone(), &before_pat, op)?;
               }
               if let Some(ref slice_pat) = *slice {
-                  let slice_ty = self.pat_ty(&slice_pat)?;
-                  let slice_cmt = self.cat_rvalue_node(pat.id(), pat.span(), slice_ty);
-                  self.cat_pattern_(slice_cmt, &slice_pat, op)?;
+                  self.cat_pattern_(elt_cmt.clone(), &slice_pat, op)?;
               }
               for after_pat in after {
                   self.cat_pattern_(elt_cmt.clone(), &after_pat, op)?;
@@ -1607,18 +1549,6 @@ impl fmt::Debug for InteriorKind {
     }
 }
 
-fn element_kind(t: Ty) -> ElementKind {
-    match t.sty {
-        ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
-        ty::TyBox(ty) => match ty.sty {
-            ty::TySlice(_) => VecElement,
-            _ => OtherElement
-        },
-        ty::TyArray(..) | ty::TySlice(_) => VecElement,
-        _ => OtherElement
-    }
-}
-
 impl fmt::Debug for Upvar {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "{:?}/{:?}", self.id, self.kind)
index 55d75ace081517281366339dc5088f55daee4e3f..6ea0fa20c572689c00b6804c74c1818e077dd04c 100644 (file)
@@ -92,13 +92,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ReachableContext<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &hir::Expr) {
         match expr.node {
             hir::ExprPath(..) => {
-                let def = match self.tcx.def_map.borrow().get(&expr.id) {
-                    Some(d) => d.full_def(),
-                    None => {
-                        span_bug!(expr.span, "def ID not in def map?!")
-                    }
-                };
-
+                let def = self.tcx.expect_def(expr.id);
                 let def_id = def.def_id();
                 if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) {
                     if self.def_id_represents_local_inlined_item(def_id) {
index 56b4036b7d7859107fc2993ff14dcb1fe0ce7a5b..6f0ad087dc5898ad72b259f267274654ac5c467a 100644 (file)
@@ -27,8 +27,9 @@ use std::cell::RefCell;
 use std::collections::hash_map::Entry;
 use std::fmt;
 use std::mem;
-use syntax::codemap::{self, Span};
+use syntax::codemap;
 use syntax::ast::{self, NodeId};
+use syntax_pos::Span;
 
 use hir;
 use hir::intravisit::{self, Visitor, FnKind};
@@ -752,13 +753,9 @@ fn resolve_arm(visitor: &mut RegionResolutionVisitor, arm: &hir::Arm) {
 fn resolve_pat(visitor: &mut RegionResolutionVisitor, pat: &hir::Pat) {
     visitor.new_node_extent(pat.id);
 
-    // If this is a binding (or maybe a binding, I'm too lazy to check
-    // the def map) then record the lifetime of that binding.
-    match pat.node {
-        PatKind::Ident(..) => {
-            record_var_lifetime(visitor, pat.id, pat.span);
-        }
-        _ => { }
+    // If this is a binding then record the lifetime of that binding.
+    if let PatKind::Binding(..) = pat.node {
+        record_var_lifetime(visitor, pat.id, pat.span);
     }
 
     intravisit::walk_pat(visitor, pat);
@@ -958,7 +955,7 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &hir::Local) {
     ///        | box P&
     fn is_binding_pat(pat: &hir::Pat) -> bool {
         match pat.node {
-            PatKind::Ident(hir::BindByRef(_), _, _) => true,
+            PatKind::Binding(hir::BindByRef(_), _, _) => true,
 
             PatKind::Struct(_, ref field_pats, _) => {
                 field_pats.iter().any(|fp| is_binding_pat(&fp.node.pat))
@@ -970,8 +967,8 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &hir::Local) {
                 pats3.iter().any(|p| is_binding_pat(&p))
             }
 
-            PatKind::TupleStruct(_, Some(ref subpats)) |
-            PatKind::Tup(ref subpats) => {
+            PatKind::TupleStruct(_, ref subpats, _) |
+            PatKind::Tuple(ref subpats, _) => {
                 subpats.iter().any(|p| is_binding_pat(&p))
             }
 
index 2200d72c883ffa2bb4e259cce7d1b2c90ba0b50b..7f6614a959c894634e2d3898ed0a89c083863f97 100644 (file)
@@ -22,16 +22,18 @@ use dep_graph::DepNode;
 use hir::map::Map;
 use session::Session;
 use hir::def::{Def, DefMap};
+use hir::def_id::DefId;
 use middle::region;
 use ty::subst;
 use ty;
 use std::fmt;
 use std::mem::replace;
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::parse::token::keywords;
+use syntax_pos::Span;
 use util::nodemap::NodeMap;
 
+use rustc_data_structures::fnv::FnvHashSet;
 use hir;
 use hir::print::lifetime_to_string;
 use hir::intravisit::{self, Visitor, FnKind};
@@ -50,11 +52,21 @@ pub enum DefRegion {
 
 // Maps the id of each lifetime reference to the lifetime decl
 // that it corresponds to.
-pub type NamedRegionMap = NodeMap<DefRegion>;
+pub struct NamedRegionMap {
+    // maps from every use of a named (not anonymous) lifetime to a
+    // `DefRegion` describing how that region is bound
+    pub defs: NodeMap<DefRegion>,
+
+    // the set of lifetime def ids that are late-bound; late-bound ids
+    // are named regions appearing in fn arguments that do not appear
+    // in where-clauses
+    pub late_bound: NodeMap<ty::Issue32330>,
+}
 
-struct LifetimeContext<'a> {
+struct LifetimeContext<'a, 'tcx: 'a> {
     sess: &'a Session,
-    named_region_map: &'a mut NamedRegionMap,
+    hir_map: &'a Map<'tcx>,
+    map: &'a mut NamedRegionMap,
     scope: Scope<'a>,
     def_map: &'a DefMap,
     // Deep breath. Our representation for poly trait refs contains a single
@@ -101,21 +113,25 @@ pub fn krate(sess: &Session,
              -> Result<NamedRegionMap, usize> {
     let _task = hir_map.dep_graph.in_task(DepNode::ResolveLifetimes);
     let krate = hir_map.krate();
-    let mut named_region_map = NodeMap();
+    let mut map = NamedRegionMap {
+        defs: NodeMap(),
+        late_bound: NodeMap(),
+    };
     sess.track_errors(|| {
         krate.visit_all_items(&mut LifetimeContext {
             sess: sess,
-            named_region_map: &mut named_region_map,
+            hir_map: hir_map,
+            map: &mut map,
             scope: &ROOT_SCOPE,
             def_map: def_map,
             trait_ref_hack: false,
             labels_in_fn: vec![],
         });
     })?;
-    Ok(named_region_map)
+    Ok(map)
 }
 
-impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
+impl<'a, 'tcx, 'v> Visitor<'v> for LifetimeContext<'a, 'tcx> {
     fn visit_item(&mut self, item: &hir::Item) {
         assert!(self.labels_in_fn.is_empty());
 
@@ -164,8 +180,12 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
         // Items always introduce a new root scope
         self.with(RootScope, |_, this| {
             match item.node {
-                hir::ForeignItemFn(_, ref generics) => {
-                    this.visit_early_late(subst::FnSpace, generics, |this| {
+                hir::ForeignItemFn(ref decl, ref generics) => {
+                    this.visit_early_late(item.id,
+                                          subst::FnSpace,
+                                          decl,
+                                          generics,
+                                          |this| {
                         intravisit::walk_foreign_item(this, item);
                     })
                 }
@@ -179,24 +199,27 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
         replace(&mut self.labels_in_fn, saved);
     }
 
-    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
+    fn visit_fn(&mut self, fk: FnKind<'v>, decl: &'v hir::FnDecl,
                 b: &'v hir::Block, s: Span, fn_id: ast::NodeId) {
         match fk {
             FnKind::ItemFn(_, generics, _, _, _, _, _) => {
-                self.visit_early_late(subst::FnSpace, generics, |this| {
-                    this.add_scope_and_walk_fn(fk, fd, b, s, fn_id)
+                self.visit_early_late(fn_id, subst::FnSpace, decl, generics, |this| {
+                    this.add_scope_and_walk_fn(fk, decl, b, s, fn_id)
                 })
             }
             FnKind::Method(_, sig, _, _) => {
-                self.visit_early_late(subst::FnSpace, &sig.generics, |this| {
-                    this.add_scope_and_walk_fn(fk, fd, b, s, fn_id)
-                })
+                self.visit_early_late(
+                    fn_id,
+                    subst::FnSpace,
+                    decl,
+                    &sig.generics,
+                    |this| this.add_scope_and_walk_fn(fk, decl, b, s, fn_id));
             }
             FnKind::Closure(_) => {
                 // Closures have their own set of labels, save labels just
                 // like for foreign items above.
                 let saved = replace(&mut self.labels_in_fn, vec![]);
-                let result = self.add_scope_and_walk_fn(fk, fd, b, s, fn_id);
+                let result = self.add_scope_and_walk_fn(fk, decl, b, s, fn_id);
                 replace(&mut self.labels_in_fn, saved);
                 result
             }
@@ -240,7 +263,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
 
         if let hir::MethodTraitItem(ref sig, None) = trait_item.node {
             self.visit_early_late(
-                subst::FnSpace, &sig.generics,
+                trait_item.id, subst::FnSpace,
+                &sig.decl, &sig.generics,
                 |this| intravisit::walk_trait_item(this, trait_item))
         } else {
             intravisit::walk_trait_item(self, trait_item);
@@ -260,9 +284,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
     fn visit_generics(&mut self, generics: &hir::Generics) {
         for ty_param in generics.ty_params.iter() {
             walk_list!(self, visit_ty_param_bound, &ty_param.bounds);
-            match ty_param.default {
-                Some(ref ty) => self.visit_ty(&ty),
-                None => {}
+            if let Some(ref ty) = ty_param.default {
+                self.visit_ty(&ty);
             }
         }
         for predicate in &generics.where_clause.predicates {
@@ -380,8 +403,7 @@ fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, sha
 
 // Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning
 // if one of the label shadows a lifetime or another label.
-fn extract_labels<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v hir::Block) {
-
+fn extract_labels(ctxt: &mut LifetimeContext, b: &hir::Block) {
     struct GatherLabels<'a> {
         sess: &'a Session,
         scope: Scope<'a>,
@@ -404,23 +426,23 @@ fn extract_labels<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v hir::Block) {
             if let hir::ExprClosure(..) = ex.node {
                 return
             }
-            if let Some(label) = expression_label(ex) {
+            if let Some((label, label_span)) = expression_label(ex) {
                 for &(prior, prior_span) in &self.labels_in_fn[..] {
                     // FIXME (#24278): non-hygienic comparison
                     if label == prior {
                         signal_shadowing_problem(self.sess,
                                                  label,
                                                  original_label(prior_span),
-                                                 shadower_label(ex.span));
+                                                 shadower_label(label_span));
                     }
                 }
 
                 check_if_label_shadows_lifetime(self.sess,
                                                 self.scope,
                                                 label,
-                                                ex.span);
+                                                label_span);
 
-                self.labels_in_fn.push((label, ex.span));
+                self.labels_in_fn.push((label, label_span));
             }
             intravisit::walk_expr(self, ex)
         }
@@ -430,10 +452,10 @@ fn extract_labels<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v hir::Block) {
         }
     }
 
-    fn expression_label(ex: &hir::Expr) -> Option<ast::Name> {
+    fn expression_label(ex: &hir::Expr) -> Option<(ast::Name, Span)> {
         match ex.node {
             hir::ExprWhile(_, _, Some(label)) |
-            hir::ExprLoop(_, Some(label)) => Some(label.unhygienize()),
+            hir::ExprLoop(_, Some(label)) => Some((label.node, label.span)),
             _ => None,
         }
     }
@@ -467,7 +489,7 @@ fn extract_labels<'v, 'a>(ctxt: &mut LifetimeContext<'a>, b: &'v hir::Block) {
     }
 }
 
-impl<'a> LifetimeContext<'a> {
+impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
     fn add_scope_and_walk_fn<'b>(&mut self,
                                  fk: FnKind,
                                  fd: &hir::FnDecl,
@@ -500,10 +522,11 @@ impl<'a> LifetimeContext<'a> {
     fn with<F>(&mut self, wrap_scope: ScopeChain, f: F) where
         F: FnOnce(Scope, &mut LifetimeContext),
     {
-        let LifetimeContext {sess, ref mut named_region_map, ..} = *self;
+        let LifetimeContext {sess, hir_map, ref mut map, ..} = *self;
         let mut this = LifetimeContext {
             sess: sess,
-            named_region_map: *named_region_map,
+            hir_map: hir_map,
+            map: *map,
             scope: &wrap_scope,
             def_map: self.def_map,
             trait_ref_hack: self.trait_ref_hack,
@@ -533,20 +556,27 @@ impl<'a> LifetimeContext<'a> {
     /// bound lifetimes are resolved by name and associated with a binder id (`binder_id`), so the
     /// ordering is not important there.
     fn visit_early_late<F>(&mut self,
+                           fn_id: ast::NodeId,
                            early_space: subst::ParamSpace,
+                           decl: &hir::FnDecl,
                            generics: &hir::Generics,
                            walk: F) where
         F: FnOnce(&mut LifetimeContext),
     {
-        let referenced_idents = early_bound_lifetime_names(generics);
-
-        debug!("visit_early_late: referenced_idents={:?}",
-               referenced_idents);
-
-        let (early, late): (Vec<_>, _) = generics.lifetimes.iter().cloned().partition(
-            |l| referenced_idents.iter().any(|&i| i == l.lifetime.name));
-
-        self.with(EarlyScope(early_space, &early, self.scope), move |old_scope, this| {
+        let fn_def_id = self.hir_map.local_def_id(fn_id);
+        insert_late_bound_lifetimes(self.map,
+                                    fn_def_id,
+                                    decl,
+                                    generics);
+
+        let (late, early): (Vec<_>, _) =
+            generics.lifetimes
+                    .iter()
+                    .cloned()
+                    .partition(|l| self.map.late_bound.contains_key(&l.lifetime.id));
+
+        let this = self;
+        this.with(EarlyScope(early_space, &early, this.scope), move |old_scope, this| {
             this.with(LateScope(&late, this.scope), move |_, this| {
                 this.check_lifetime_defs(old_scope, &generics.lifetimes);
                 walk(this);
@@ -755,11 +785,12 @@ impl<'a> LifetimeContext<'a> {
                        probably a bug in syntax::fold");
         }
 
-        debug!("lifetime_ref={:?} id={:?} resolved to {:?}",
-                lifetime_to_string(lifetime_ref),
-                lifetime_ref.id,
-                def);
-        self.named_region_map.insert(lifetime_ref.id, def);
+        debug!("lifetime_ref={:?} id={:?} resolved to {:?} span={:?}",
+               lifetime_to_string(lifetime_ref),
+               lifetime_ref.id,
+               def,
+               self.sess.codemap().span_to_string(lifetime_ref.span));
+        self.map.defs.insert(lifetime_ref.id, def);
     }
 }
 
@@ -776,95 +807,132 @@ fn search_lifetimes<'a>(lifetimes: &'a [hir::LifetimeDef],
 
 ///////////////////////////////////////////////////////////////////////////
 
-pub fn early_bound_lifetimes<'a>(generics: &'a hir::Generics) -> Vec<hir::LifetimeDef> {
-    let referenced_idents = early_bound_lifetime_names(generics);
-    if referenced_idents.is_empty() {
-        return Vec::new();
+/// Detects late-bound lifetimes and inserts them into
+/// `map.late_bound`.
+///
+/// A region declared on a fn is **late-bound** if:
+/// - it is constrained by an argument type;
+/// - it does not appear in a where-clause.
+///
+/// "Constrained" basically means that it appears in any type but
+/// not amongst the inputs to a projection.  In other words, `<&'a
+/// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`.
+fn insert_late_bound_lifetimes(map: &mut NamedRegionMap,
+                               fn_def_id: DefId,
+                               decl: &hir::FnDecl,
+                               generics: &hir::Generics) {
+    debug!("insert_late_bound_lifetimes(decl={:?}, generics={:?})", decl, generics);
+
+    let mut constrained_by_input = ConstrainedCollector { regions: FnvHashSet() };
+    for arg in &decl.inputs {
+        constrained_by_input.visit_ty(&arg.ty);
     }
 
-    generics.lifetimes.iter()
-        .filter(|l| referenced_idents.iter().any(|&i| i == l.lifetime.name))
-        .cloned()
-        .collect()
-}
-
-/// Given a set of generic declarations, returns a list of names containing all early bound
-/// lifetime names for those generics. (In fact, this list may also contain other names.)
-fn early_bound_lifetime_names(generics: &hir::Generics) -> Vec<ast::Name> {
-    // Create two lists, dividing the lifetimes into early/late bound.
-    // Initially, all of them are considered late, but we will move
-    // things from late into early as we go if we find references to
-    // them.
-    let mut early_bound = Vec::new();
-    let mut late_bound = generics.lifetimes.iter()
-                                           .map(|l| l.lifetime.name)
-                                           .collect();
-
-    // Any lifetime that appears in a type bound is early.
-    {
-        let mut collector =
-            FreeLifetimeCollector { early_bound: &mut early_bound,
-                                    late_bound: &mut late_bound };
-        for ty_param in generics.ty_params.iter() {
-            walk_list!(&mut collector, visit_ty_param_bound, &ty_param.bounds);
+    let mut appears_in_output = AllCollector { regions: FnvHashSet() };
+    intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output);
+
+    debug!("insert_late_bound_lifetimes: constrained_by_input={:?}",
+           constrained_by_input.regions);
+
+    // Walk the lifetimes that appear in where clauses.
+    //
+    // Subtle point: because we disallow nested bindings, we can just
+    // ignore binders here and scrape up all names we see.
+    let mut appears_in_where_clause = AllCollector { regions: FnvHashSet() };
+    for ty_param in generics.ty_params.iter() {
+        walk_list!(&mut appears_in_where_clause,
+                   visit_ty_param_bound,
+                   &ty_param.bounds);
+    }
+    walk_list!(&mut appears_in_where_clause,
+               visit_where_predicate,
+               &generics.where_clause.predicates);
+    for lifetime_def in &generics.lifetimes {
+        if !lifetime_def.bounds.is_empty() {
+            // `'a: 'b` means both `'a` and `'b` are referenced
+            appears_in_where_clause.visit_lifetime_def(lifetime_def);
         }
-        for predicate in &generics.where_clause.predicates {
-            match predicate {
-                &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate{ref bounds,
-                                                                              ref bounded_ty,
-                                                                              ..}) => {
-                    collector.visit_ty(&bounded_ty);
-                    walk_list!(&mut collector, visit_ty_param_bound, bounds);
+    }
+
+    debug!("insert_late_bound_lifetimes: appears_in_where_clause={:?}",
+           appears_in_where_clause.regions);
+
+    // Late bound regions are those that:
+    // - appear in the inputs
+    // - do not appear in the where-clauses
+    for lifetime in &generics.lifetimes {
+        let name = lifetime.lifetime.name;
+
+        // appears in the where clauses? early-bound.
+        if appears_in_where_clause.regions.contains(&name) { continue; }
+
+        // does not appear in the inputs, but appears in the return
+        // type? eventually this will be early-bound, but for now we
+        // just mark it so we can issue warnings.
+        let constrained_by_input = constrained_by_input.regions.contains(&name);
+        let appears_in_output = appears_in_output.regions.contains(&name);
+        let will_change = !constrained_by_input && appears_in_output;
+        let issue_32330 = if will_change {
+            ty::Issue32330::WillChange {
+                fn_def_id: fn_def_id,
+                region_name: name,
+            }
+        } else {
+            ty::Issue32330::WontChange
+        };
+
+        debug!("insert_late_bound_lifetimes: \
+                lifetime {:?} with id {:?} is late-bound ({:?}",
+               lifetime.lifetime.name, lifetime.lifetime.id, issue_32330);
+
+        let prev = map.late_bound.insert(lifetime.lifetime.id, issue_32330);
+        assert!(prev.is_none(), "visited lifetime {:?} twice", lifetime.lifetime.id);
+    }
+
+    return;
+
+    struct ConstrainedCollector {
+        regions: FnvHashSet<ast::Name>,
+    }
+
+    impl<'v> Visitor<'v> for ConstrainedCollector {
+        fn visit_ty(&mut self, ty: &'v hir::Ty) {
+            match ty.node {
+                hir::TyPath(Some(_), _) => {
+                    // ignore lifetimes appearing in associated type
+                    // projections, as they are not *constrained*
+                    // (defined above)
                 }
-                &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate{ref lifetime,
-                                                                                ref bounds,
-                                                                                ..}) => {
-                    collector.visit_lifetime(lifetime);
 
-                    for bound in bounds {
-                        collector.visit_lifetime(bound);
+                hir::TyPath(None, ref path) => {
+                    // consider only the lifetimes on the final
+                    // segment; I am not sure it's even currently
+                    // valid to have them elsewhere, but even if it
+                    // is, those would be potentially inputs to
+                    // projections
+                    if let Some(last_segment) = path.segments.last() {
+                        self.visit_path_segment(path.span, last_segment);
                     }
                 }
-                &hir::WherePredicate::EqPredicate(_) => bug!("unimplemented")
-            }
-        }
-    }
 
-    // Any lifetime that either has a bound or is referenced by a
-    // bound is early.
-    for lifetime_def in &generics.lifetimes {
-        if !lifetime_def.bounds.is_empty() {
-            shuffle(&mut early_bound, &mut late_bound,
-                    lifetime_def.lifetime.name);
-            for bound in &lifetime_def.bounds {
-                shuffle(&mut early_bound, &mut late_bound,
-                        bound.name);
+                _ => {
+                    intravisit::walk_ty(self, ty);
+                }
             }
         }
-    }
-    return early_bound;
 
-    struct FreeLifetimeCollector<'a> {
-        early_bound: &'a mut Vec<ast::Name>,
-        late_bound: &'a mut Vec<ast::Name>,
+        fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
+            self.regions.insert(lifetime_ref.name);
+        }
     }
 
-    impl<'a, 'v> Visitor<'v> for FreeLifetimeCollector<'a> {
-        fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
-            shuffle(self.early_bound, self.late_bound,
-                    lifetime_ref.name);
-        }
+    struct AllCollector {
+        regions: FnvHashSet<ast::Name>,
     }
 
-    fn shuffle(early_bound: &mut Vec<ast::Name>,
-               late_bound: &mut Vec<ast::Name>,
-               name: ast::Name) {
-        match late_bound.iter().position(|n| *n == name) {
-            Some(index) => {
-                late_bound.swap_remove(index);
-                early_bound.push(name);
-            }
-            None => { }
+    impl<'v> Visitor<'v> for AllCollector {
+        fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
+            self.regions.insert(lifetime_ref.name);
         }
     }
 }
index c2db6de03700e254c0a93d35b5093e3509030e2f..36268a9de960fcdab041ef8e44b4698332ab8170 100644 (file)
@@ -23,19 +23,21 @@ use hir::def_id::{CRATE_DEF_INDEX, DefId};
 use ty::{self, TyCtxt};
 use middle::privacy::AccessLevels;
 use syntax::parse::token::InternedString;
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
 use syntax::ast;
 use syntax::ast::{NodeId, Attribute};
-use syntax::feature_gate::{GateIssue, emit_feature_err};
+use syntax::feature_gate::{GateIssue, emit_feature_err, find_lang_feature_accepted_version};
 use syntax::attr::{self, Stability, Deprecation, AttrMetaMethods};
 use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap};
 
 use hir;
 use hir::{Item, Generics, StructField, Variant, PatKind};
 use hir::intravisit::{self, Visitor};
+use hir::pat_util::EnumerateAndAdjustIterator;
 
 use std::mem::replace;
 use std::cmp::Ordering;
+use std::ops::Deref;
 
 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Hash)]
 pub enum StabilityLevel {
@@ -321,7 +323,7 @@ impl<'a, 'tcx> Index<'tcx> {
 /// features and possibly prints errors. Returns a list of all
 /// features used.
 pub fn check_unstable_api_usage<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
-                                          -> FnvHashMap<InternedString, StabilityLevel> {
+                                          -> FnvHashMap<InternedString, attr::StabilityLevel> {
     let _task = tcx.dep_graph.in_task(DepNode::StabilityCheck);
     let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features;
 
@@ -342,7 +344,7 @@ pub fn check_unstable_api_usage<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
 struct Checker<'a, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     active_features: FnvHashSet<InternedString>,
-    used_features: FnvHashMap<InternedString, StabilityLevel>,
+    used_features: FnvHashMap<InternedString, attr::StabilityLevel>,
     // Within a block where feature gate checking can be skipped.
     in_skip_block: u32,
 }
@@ -366,7 +368,8 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
 
         match *stab {
             Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => {
-                self.used_features.insert(feature.clone(), Unstable);
+                self.used_features.insert(feature.clone(),
+                                          attr::Unstable { reason: reason.clone(), issue: issue });
 
                 if !self.active_features.contains(feature) {
                     let msg = match *reason {
@@ -379,7 +382,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                 }
             }
             Some(&Stability { ref level, ref feature, .. }) => {
-                self.used_features.insert(feature.clone(), StabilityLevel::from_attr_level(level));
+                self.used_features.insert(feature.clone(), level.clone());
 
                 // Stable APIs are always ok to call and deprecated APIs are
                 // handled by a lint.
@@ -491,7 +494,7 @@ pub fn check_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         // individually as it's possible to have a stable trait with unstable
         // items.
         hir::ItemImpl(_, _, _, Some(ref t), _, ref impl_items) => {
-            let trait_did = tcx.def_map.borrow().get(&t.ref_id).unwrap().def_id();
+            let trait_did = tcx.expect_def(t.ref_id).def_id();
             let trait_items = tcx.trait_items(trait_did);
 
             for impl_item in impl_items {
@@ -577,7 +580,8 @@ pub fn check_path<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                             cb: &mut FnMut(DefId, Span,
                                            &Option<&Stability>,
                                            &Option<Deprecation>)) {
-    match tcx.def_map.borrow().get(&id).map(|d| d.full_def()) {
+    // Paths in import prefixes may have no resolution.
+    match tcx.expect_def_or_none(id) {
         Some(Def::PrimTy(..)) => {}
         Some(Def::SelfTy(..)) => {}
         Some(def) => {
@@ -592,12 +596,11 @@ pub fn check_path_list_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                       cb: &mut FnMut(DefId, Span,
                                                      &Option<&Stability>,
                                                      &Option<Deprecation>)) {
-    match tcx.def_map.borrow().get(&item.node.id()).map(|d| d.full_def()) {
-        Some(Def::PrimTy(..)) => {}
-        Some(def) => {
+    match tcx.expect_def(item.node.id()) {
+        Def::PrimTy(..) => {}
+        def => {
             maybe_do_stability_check(tcx, def.def_id(), item.span, cb);
         }
-        None => {}
     }
 }
 
@@ -614,10 +617,9 @@ pub fn check_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pat: &hir::Pat,
     };
     match pat.node {
         // Foo(a, b, c)
-        // A Variant(..) pattern `PatKind::TupleStruct(_, None)` doesn't have to be recursed into.
-        PatKind::TupleStruct(_, Some(ref pat_fields)) => {
-            for (field, struct_field) in pat_fields.iter().zip(&v.fields) {
-                maybe_do_stability_check(tcx, struct_field.did, field.span, cb)
+        PatKind::TupleStruct(_, ref pat_fields, ddpos) => {
+            for (i, field) in pat_fields.iter().enumerate_and_adjust(v.fields.len(), ddpos) {
+                maybe_do_stability_check(tcx, v.fields[i].did, field.span, cb)
             }
         }
         // Foo { a, b, c }
@@ -716,28 +718,32 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
 /// libraries, identify activated features that don't exist and error about them.
 pub fn check_unused_or_stable_features(sess: &Session,
                                        lib_features_used: &FnvHashMap<InternedString,
-                                                                      StabilityLevel>) {
+                                                                      attr::StabilityLevel>) {
     let ref declared_lib_features = sess.features.borrow().declared_lib_features;
     let mut remaining_lib_features: FnvHashMap<InternedString, Span>
         = declared_lib_features.clone().into_iter().collect();
 
-    let stable_msg = "this feature is stable. attribute no longer needed";
+    fn format_stable_since_msg(version: &str) -> String {
+        format!("this feature has been stable since {}. Attribute no longer needed", version)
+    }
 
-    for &span in &sess.features.borrow().declared_stable_lang_features {
+    for &(ref stable_lang_feature, span) in &sess.features.borrow().declared_stable_lang_features {
+        let version = find_lang_feature_accepted_version(stable_lang_feature.deref())
+            .expect("unexpectedly couldn't find version feature was stabilized");
         sess.add_lint(lint::builtin::STABLE_FEATURES,
                       ast::CRATE_NODE_ID,
                       span,
-                      stable_msg.to_string());
+                      format_stable_since_msg(version));
     }
 
     for (used_lib_feature, level) in lib_features_used {
         match remaining_lib_features.remove(used_lib_feature) {
             Some(span) => {
-                if *level == Stable {
+                if let &attr::StabilityLevel::Stable { since: ref version } = level {
                     sess.add_lint(lint::builtin::STABLE_FEATURES,
                                   ast::CRATE_NODE_ID,
                                   span,
-                                  stable_msg.to_string());
+                                  format_stable_since_msg(version.deref()));
                 }
             }
             None => ( /* used but undeclared, handled during the previous ast visit */ )
index 325887684914bdf9174183bfe970f40ab0764fcf..6fb1b16705fe47fce509e180202bc25cf0a75cdd 100644 (file)
@@ -15,8 +15,8 @@ use session::Session;
 use middle::lang_items;
 
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::parse::token::InternedString;
+use syntax_pos::Span;
 use hir::intravisit::Visitor;
 use hir::intravisit;
 use hir;
@@ -123,9 +123,8 @@ impl<'a> Context<'a> {
 
 impl<'a, 'v> Visitor<'v> for Context<'a> {
     fn visit_foreign_item(&mut self, i: &hir::ForeignItem) {
-        match lang_items::extract(&i.attrs) {
-            None => {}
-            Some(lang_item) => self.register(&lang_item, i.span),
+        if let Some(lang_item) = lang_items::extract(&i.attrs) {
+            self.register(&lang_item, i.span);
         }
         intravisit::walk_foreign_item(self, i)
     }
diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs
new file mode 100644 (file)
index 0000000..1be7d00
--- /dev/null
@@ -0,0 +1,69 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::cell::{Ref, RefCell};
+use rustc_data_structures::indexed_vec::IndexVec;
+
+use mir::repr::{Mir, BasicBlock};
+
+use rustc_serialize as serialize;
+
+#[derive(Clone, Debug)]
+pub struct Cache {
+    predecessors: RefCell<Option<IndexVec<BasicBlock, Vec<BasicBlock>>>>
+}
+
+
+impl serialize::Encodable for Cache {
+    fn encode<S: serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        serialize::Encodable::encode(&(), s)
+    }
+}
+
+impl serialize::Decodable for Cache {
+    fn decode<D: serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
+        serialize::Decodable::decode(d).map(|_v: ()| Self::new())
+    }
+}
+
+
+impl Cache {
+    pub fn new() -> Self {
+        Cache {
+            predecessors: RefCell::new(None)
+        }
+    }
+
+    pub fn invalidate(&self) {
+        // FIXME: consider being more fine-grained
+        *self.predecessors.borrow_mut() = None;
+    }
+
+    pub fn predecessors(&self, mir: &Mir) -> Ref<IndexVec<BasicBlock, Vec<BasicBlock>>> {
+        if self.predecessors.borrow().is_none() {
+            *self.predecessors.borrow_mut() = Some(calculate_predecessors(mir));
+        }
+
+        Ref::map(self.predecessors.borrow(), |p| p.as_ref().unwrap())
+    }
+}
+
+fn calculate_predecessors(mir: &Mir) -> IndexVec<BasicBlock, Vec<BasicBlock>> {
+    let mut result = IndexVec::from_elem(vec![], mir.basic_blocks());
+    for (bb, data) in mir.basic_blocks().iter_enumerated() {
+        if let Some(ref term) = data.terminator {
+            for &tgt in term.successors().iter() {
+                result[tgt].push(bb);
+            }
+        }
+    }
+
+    result
+}
index 458cb28144adbced7573fad926b5568d1c70154e..62d3421770c2f2cefe0a28ce6115ac5e006e21d1 100644 (file)
 
 use graphviz::IntoCow;
 use middle::const_val::ConstVal;
-use rustc_const_math::{ConstUsize, ConstInt};
+use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators};
+use rustc_data_structures::control_flow_graph::{GraphPredecessors, GraphSuccessors};
+use rustc_data_structures::control_flow_graph::ControlFlowGraph;
 use hir::def_id::DefId;
 use ty::subst::Substs;
 use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
@@ -19,43 +23,71 @@ use rustc_back::slice;
 use hir::InlineAsm;
 use std::ascii;
 use std::borrow::{Cow};
+use std::cell::Ref;
 use std::fmt::{self, Debug, Formatter, Write};
 use std::{iter, u32};
 use std::ops::{Index, IndexMut};
+use std::vec::IntoIter;
 use syntax::ast::{self, Name};
-use syntax::codemap::Span;
+use syntax_pos::Span;
+
+use super::cache::Cache;
+
+macro_rules! newtype_index {
+    ($name:ident, $debug_name:expr) => (
+        #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
+         RustcEncodable, RustcDecodable)]
+        pub struct $name(u32);
+
+        impl Idx for $name {
+            fn new(value: usize) -> Self {
+                assert!(value < (u32::MAX) as usize);
+                $name(value as u32)
+            }
+            fn index(self) -> usize {
+                self.0 as usize
+            }
+        }
+
+        impl Debug for $name {
+            fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
+                write!(fmt, "{}{}", $debug_name, self.0)
+            }
+        }
+    )
+}
 
 /// Lowered representation of a single function.
-#[derive(Clone, RustcEncodable, RustcDecodable)]
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Mir<'tcx> {
     /// List of basic blocks. References to basic block use a newtyped index type `BasicBlock`
     /// that indexes into this vector.
-    pub basic_blocks: Vec<BasicBlockData<'tcx>>,
+    basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
 
-    /// List of lexical scopes; these are referenced by statements and
-    /// used (eventually) for debuginfo. Indexed by a `ScopeId`.
-    pub scopes: Vec<ScopeData>,
+    /// List of visibility (lexical) scopes; these are referenced by statements
+    /// and used (eventually) for debuginfo. Indexed by a `VisibilityScope`.
+    pub visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
 
     /// Rvalues promoted from this function, such as borrows of constants.
     /// Each of them is the Mir of a constant with the fn's type parameters
     /// in scope, but no vars or args and a separate set of temps.
-    pub promoted: Vec<Mir<'tcx>>,
+    pub promoted: IndexVec<Promoted, Mir<'tcx>>,
 
     /// Return type of the function.
     pub return_ty: FnOutput<'tcx>,
 
     /// Variables: these are stack slots corresponding to user variables. They may be
     /// assigned many times.
-    pub var_decls: Vec<VarDecl<'tcx>>,
+    pub var_decls: IndexVec<Var, VarDecl<'tcx>>,
 
     /// Args: these are stack slots corresponding to the input arguments.
-    pub arg_decls: Vec<ArgDecl<'tcx>>,
+    pub arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
 
     /// Temp declarations: stack slots that for temporaries created by
     /// the compiler. These are assigned once, but they are not SSA
     /// values in that it is possible to borrow them and mutate them
     /// through the resulting reference.
-    pub temp_decls: Vec<TempDecl<'tcx>>,
+    pub temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
 
     /// Names and capture modes of all the closure upvars, assuming
     /// the first argument is either the closure or a reference to it.
@@ -63,24 +95,97 @@ pub struct Mir<'tcx> {
 
     /// A span representing this MIR, for error reporting
     pub span: Span,
+
+    /// A cache for various calculations
+    cache: Cache
 }
 
 /// where execution begins
 pub const START_BLOCK: BasicBlock = BasicBlock(0);
 
 impl<'tcx> Mir<'tcx> {
-    pub fn all_basic_blocks(&self) -> Vec<BasicBlock> {
-        (0..self.basic_blocks.len())
-            .map(|i| BasicBlock::new(i))
-            .collect()
+    pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+               visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
+               promoted: IndexVec<Promoted, Mir<'tcx>>,
+               return_ty: FnOutput<'tcx>,
+               var_decls: IndexVec<Var, VarDecl<'tcx>>,
+               arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
+               temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
+               upvar_decls: Vec<UpvarDecl>,
+               span: Span) -> Self
+    {
+        Mir {
+            basic_blocks: basic_blocks,
+            visibility_scopes: visibility_scopes,
+            promoted: promoted,
+            return_ty: return_ty,
+            var_decls: var_decls,
+            arg_decls: arg_decls,
+            temp_decls: temp_decls,
+            upvar_decls: upvar_decls,
+            span: span,
+            cache: Cache::new()
+        }
+    }
+
+    #[inline]
+    pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+        &self.basic_blocks
+    }
+
+    #[inline]
+    pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+        self.cache.invalidate();
+        &mut self.basic_blocks
     }
 
-    pub fn basic_block_data(&self, bb: BasicBlock) -> &BasicBlockData<'tcx> {
-        &self.basic_blocks[bb.index()]
+    #[inline]
+    pub fn predecessors(&self) -> Ref<IndexVec<BasicBlock, Vec<BasicBlock>>> {
+        self.cache.predecessors(self)
     }
 
-    pub fn basic_block_data_mut(&mut self, bb: BasicBlock) -> &mut BasicBlockData<'tcx> {
-        &mut self.basic_blocks[bb.index()]
+    #[inline]
+    pub fn predecessors_for(&self, bb: BasicBlock) -> Ref<Vec<BasicBlock>> {
+        Ref::map(self.predecessors(), |p| &p[bb])
+    }
+
+    #[inline]
+    pub fn dominators(&self) -> Dominators<BasicBlock> {
+        dominators(self)
+    }
+
+    /// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order)
+    /// to their index in the whole list of locals. This is useful if you
+    /// want to treat all locals the same instead of repeating yourself.
+    pub fn local_index(&self, lvalue: &Lvalue<'tcx>) -> Option<Local> {
+        let idx = match *lvalue {
+            Lvalue::Arg(arg) => arg.index(),
+            Lvalue::Var(var) => {
+                self.arg_decls.len() +
+                var.index()
+            }
+            Lvalue::Temp(temp) => {
+                self.arg_decls.len() +
+                self.var_decls.len() +
+                temp.index()
+            }
+            Lvalue::ReturnPointer => {
+                self.arg_decls.len() +
+                self.var_decls.len() +
+                self.temp_decls.len()
+            }
+            Lvalue::Static(_) |
+            Lvalue::Projection(_) => return None
+        };
+        Some(Local::new(idx))
+    }
+
+    /// Counts the number of locals, such that that local_index
+    /// will always return an index smaller than this count.
+    pub fn count_locals(&self) -> usize {
+        self.arg_decls.len() +
+        self.var_decls.len() +
+        self.temp_decls.len() + 1
     }
 }
 
@@ -89,17 +194,29 @@ impl<'tcx> Index<BasicBlock> for Mir<'tcx> {
 
     #[inline]
     fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
-        self.basic_block_data(index)
+        &self.basic_blocks()[index]
     }
 }
 
 impl<'tcx> IndexMut<BasicBlock> for Mir<'tcx> {
     #[inline]
     fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> {
-        self.basic_block_data_mut(index)
+        &mut self.basic_blocks_mut()[index]
     }
 }
 
+/// Grouped information about the source code origin of a MIR entity.
+/// Intended to be inspected by diagnostics and debuginfo.
+/// Most passes can work with it as a whole, within a single function.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
+pub struct SourceInfo {
+    /// Source span for the AST pertaining to this MIR entity.
+    pub span: Span,
+
+    /// The lexical visibility scope, i.e. which bindings can be seen.
+    pub scope: VisibilityScope
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Mutability and borrow kinds
 
@@ -172,11 +289,8 @@ pub struct VarDecl<'tcx> {
     /// type inferred for this variable (`let x: ty = ...`)
     pub ty: Ty<'tcx>,
 
-    /// scope in which variable was declared
-    pub scope: ScopeId,
-
-    /// span where variable was declared
-    pub span: Span,
+    /// source information (span, scope, etc.) for the declaration
+    pub source_info: SourceInfo,
 }
 
 /// A "temp" is a temporary that we place on the stack. They are
@@ -222,30 +336,7 @@ pub struct UpvarDecl {
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlock
 
-/// The index of a particular basic block. The index is into the `basic_blocks`
-/// list of the `Mir`.
-///
-/// (We use a `u32` internally just to save memory.)
-#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
-pub struct BasicBlock(u32);
-
-impl BasicBlock {
-    pub fn new(index: usize) -> BasicBlock {
-        assert!(index < (u32::MAX as usize));
-        BasicBlock(index as u32)
-    }
-
-    /// Extract the index.
-    pub fn index(self) -> usize {
-        self.0 as usize
-    }
-}
-
-impl Debug for BasicBlock {
-    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
-        write!(fmt, "bb{}", self.0)
-    }
-}
+newtype_index!(BasicBlock, "bb");
 
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlockData and Terminator
@@ -274,8 +365,7 @@ pub struct BasicBlockData<'tcx> {
 
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct Terminator<'tcx> {
-    pub span: Span,
-    pub scope: ScopeId,
+    pub source_info: SourceInfo,
     pub kind: TerminatorKind<'tcx>
 }
 
@@ -327,13 +417,24 @@ pub enum TerminatorKind<'tcx> {
     /// have been filled in by now. This should occur at most once.
     Return,
 
+    /// Indicates a terminator that can never be reached.
+    Unreachable,
+
     /// Drop the Lvalue
     Drop {
-        value: Lvalue<'tcx>,
+        location: Lvalue<'tcx>,
         target: BasicBlock,
         unwind: Option<BasicBlock>
     },
 
+    /// Drop the Lvalue and assign the new value over it
+    DropAndReplace {
+        location: Lvalue<'tcx>,
+        value: Operand<'tcx>,
+        target: BasicBlock,
+        unwind: Option<BasicBlock>,
+    },
+
     /// Block ends with a call of a converging function
     Call {
         /// The function that’s being called
@@ -345,6 +446,16 @@ pub enum TerminatorKind<'tcx> {
         /// Cleanups to be done if the call unwinds.
         cleanup: Option<BasicBlock>
     },
+
+    /// Jump to the target if the condition has the expected value,
+    /// otherwise panic with a message and a cleanup target.
+    Assert {
+        cond: Operand<'tcx>,
+        expected: bool,
+        msg: AssertMessage<'tcx>,
+        target: BasicBlock,
+        cleanup: Option<BasicBlock>
+    }
 }
 
 impl<'tcx> Terminator<'tcx> {
@@ -367,13 +478,22 @@ impl<'tcx> TerminatorKind<'tcx> {
             SwitchInt { targets: ref b, .. } => b[..].into_cow(),
             Resume => (&[]).into_cow(),
             Return => (&[]).into_cow(),
+            Unreachable => (&[]).into_cow(),
             Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(),
             Call { destination: Some((_, ref t)), cleanup: None, .. } =>
                 slice::ref_slice(t).into_cow(),
             Call { destination: None, cleanup: Some(ref c), .. } => slice::ref_slice(c).into_cow(),
             Call { destination: None, cleanup: None, .. } => (&[]).into_cow(),
-            Drop { target, unwind: Some(unwind), .. } => vec![target, unwind].into_cow(),
-            Drop { ref target, .. } => slice::ref_slice(target).into_cow(),
+            DropAndReplace { target, unwind: Some(unwind), .. } |
+            Drop { target, unwind: Some(unwind), .. } => {
+                vec![target, unwind].into_cow()
+            }
+            DropAndReplace { ref target, unwind: None, .. } |
+            Drop { ref target, unwind: None, .. } => {
+                slice::ref_slice(target).into_cow()
+            }
+            Assert { target, cleanup: Some(unwind), .. } => vec![target, unwind].into_cow(),
+            Assert { ref target, .. } => slice::ref_slice(target).into_cow(),
         }
     }
 
@@ -388,12 +508,19 @@ impl<'tcx> TerminatorKind<'tcx> {
             SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
             Resume => Vec::new(),
             Return => Vec::new(),
+            Unreachable => Vec::new(),
             Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c],
             Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
             Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
             Call { destination: None, cleanup: None, .. } => vec![],
+            DropAndReplace { ref mut target, unwind: Some(ref mut unwind), .. } |
             Drop { ref mut target, unwind: Some(ref mut unwind), .. } => vec![target, unwind],
-            Drop { ref mut target, .. } => vec![target]
+            DropAndReplace { ref mut target, unwind: None, .. } |
+            Drop { ref mut target, unwind: None, .. } => {
+                vec![target]
+            }
+            Assert { ref mut target, cleanup: Some(ref mut unwind), .. } => vec![target, unwind],
+            Assert { ref mut target, .. } => vec![target]
         }
     }
 }
@@ -460,7 +587,10 @@ impl<'tcx> TerminatorKind<'tcx> {
             SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
             Return => write!(fmt, "return"),
             Resume => write!(fmt, "resume"),
-            Drop { ref value, .. } => write!(fmt, "drop({:?})", value),
+            Unreachable => write!(fmt, "unreachable"),
+            Drop { ref location, .. } => write!(fmt, "drop({:?})", location),
+            DropAndReplace { ref location, ref value, .. } =>
+                write!(fmt, "replace({:?} <- {:?})", location, value),
             Call { ref func, ref args, ref destination, .. } => {
                 if let Some((ref destination, _)) = *destination {
                     write!(fmt, "{:?} = ", destination)?;
@@ -474,6 +604,26 @@ impl<'tcx> TerminatorKind<'tcx> {
                 }
                 write!(fmt, ")")
             }
+            Assert { ref cond, expected, ref msg, .. } => {
+                write!(fmt, "assert(")?;
+                if !expected {
+                    write!(fmt, "!")?;
+                }
+                write!(fmt, "{:?}, ", cond)?;
+
+                match *msg {
+                    AssertMessage::BoundsCheck { ref len, ref index } => {
+                        write!(fmt, "{:?}, {:?}, {:?}",
+                               "index out of bounds: the len is {} but the index is {}",
+                               len, index)?;
+                    }
+                    AssertMessage::Math(ref err) => {
+                        write!(fmt, "{:?}", err.description())?;
+                    }
+                }
+
+                write!(fmt, ")")
+            }
         }
     }
 
@@ -481,7 +631,7 @@ impl<'tcx> TerminatorKind<'tcx> {
     pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
         use self::TerminatorKind::*;
         match *self {
-            Return | Resume => vec![],
+            Return | Resume | Unreachable => vec![],
             Goto { .. } => vec!["".into()],
             If { .. } => vec!["true".into(), "false".into()],
             Switch { ref adt_def, .. } => {
@@ -505,20 +655,34 @@ impl<'tcx> TerminatorKind<'tcx> {
             Call { destination: Some(_), cleanup: None, .. } => vec!["return".into_cow()],
             Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into_cow()],
             Call { destination: None, cleanup: None, .. } => vec![],
+            DropAndReplace { unwind: None, .. } |
             Drop { unwind: None, .. } => vec!["return".into_cow()],
-            Drop { .. } => vec!["return".into_cow(), "unwind".into_cow()],
+            DropAndReplace { unwind: Some(_), .. } |
+            Drop { unwind: Some(_), .. } => {
+                vec!["return".into_cow(), "unwind".into_cow()]
+            }
+            Assert { cleanup: None, .. } => vec!["".into()],
+            Assert { .. } =>
+                vec!["success".into_cow(), "unwind".into_cow()]
         }
     }
 }
 
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+pub enum AssertMessage<'tcx> {
+    BoundsCheck {
+        len: Operand<'tcx>,
+        index: Operand<'tcx>
+    },
+    Math(ConstMathErr)
+}
 
 ///////////////////////////////////////////////////////////////////////////
 // Statements
 
 #[derive(Clone, RustcEncodable, RustcDecodable)]
 pub struct Statement<'tcx> {
-    pub span: Span,
-    pub scope: ScopeId,
+    pub source_info: SourceInfo,
     pub kind: StatementKind<'tcx>,
 }
 
@@ -539,19 +703,24 @@ impl<'tcx> Debug for Statement<'tcx> {
 ///////////////////////////////////////////////////////////////////////////
 // Lvalues
 
+newtype_index!(Var, "var");
+newtype_index!(Temp, "tmp");
+newtype_index!(Arg, "arg");
+newtype_index!(Local, "local");
+
 /// A path to a value; something that can be evaluated without
 /// changing or disturbing program state.
 #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum Lvalue<'tcx> {
     /// local variable declared by the user
-    Var(u32),
+    Var(Var),
 
     /// temporary introduced during lowering into MIR
-    Temp(u32),
+    Temp(Temp),
 
     /// formal parameter of the function; note that these are NOT the
     /// bindings that the user declares, which are vars
-    Arg(u32),
+    Arg(Arg),
 
     /// static or static mut variable
     Static(DefId),
@@ -597,6 +766,14 @@ pub enum ProjectionElem<'tcx, V> {
         from_end: bool,
     },
 
+    /// These indices are generated by slice patterns.
+    ///
+    /// slice[from:-to] in Python terms.
+    Subslice {
+        from: u32,
+        to: u32,
+    },
+
     /// "Downcast" to a variant of an ADT. Currently, we only introduce
     /// this for ADTs with more than one variant. It may be better to
     /// just introduce it always, or always for enums.
@@ -611,20 +788,7 @@ pub type LvalueProjection<'tcx> = Projection<'tcx, Lvalue<'tcx>, Operand<'tcx>>;
 /// and the index is an operand.
 pub type LvalueElem<'tcx> = ProjectionElem<'tcx, Operand<'tcx>>;
 
-/// Index into the list of fields found in a `VariantDef`
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
-pub struct Field(u32);
-
-impl Field {
-    pub fn new(value: usize) -> Field {
-        assert!(value < (u32::MAX) as usize);
-        Field(value as u32)
-    }
-
-    pub fn index(self) -> usize {
-        self.0 as usize
-    }
-}
+newtype_index!(Field, "field");
 
 impl<'tcx> Lvalue<'tcx> {
     pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> {
@@ -652,12 +816,9 @@ impl<'tcx> Debug for Lvalue<'tcx> {
         use self::Lvalue::*;
 
         match *self {
-            Var(id) =>
-                write!(fmt, "var{:?}", id),
-            Arg(id) =>
-                write!(fmt, "arg{:?}", id),
-            Temp(id) =>
-                write!(fmt, "tmp{:?}", id),
+            Var(id) => write!(fmt, "{:?}", id),
+            Arg(id) => write!(fmt, "{:?}", id),
+            Temp(id) => write!(fmt, "{:?}", id),
             Static(def_id) =>
                 write!(fmt, "{}", ty::tls::with(|tcx| tcx.item_path_str(def_id))),
             ReturnPointer =>
@@ -676,6 +837,14 @@ impl<'tcx> Debug for Lvalue<'tcx> {
                         write!(fmt, "{:?}[{:?} of {:?}]", data.base, offset, min_length),
                     ProjectionElem::ConstantIndex { offset, min_length, from_end: true } =>
                         write!(fmt, "{:?}[-{:?} of {:?}]", data.base, offset, min_length),
+                    ProjectionElem::Subslice { from, to } if to == 0 =>
+                        write!(fmt, "{:?}[{:?}:", data.base, from),
+                    ProjectionElem::Subslice { from, to } if from == 0 =>
+                        write!(fmt, "{:?}[:-{:?}]", data.base, to),
+                    ProjectionElem::Subslice { from, to } =>
+                        write!(fmt, "{:?}[{:?}:-{:?}]", data.base,
+                               from, to),
+
                 },
         }
     }
@@ -684,40 +853,13 @@ impl<'tcx> Debug for Lvalue<'tcx> {
 ///////////////////////////////////////////////////////////////////////////
 // Scopes
 
-impl Index<ScopeId> for Vec<ScopeData> {
-    type Output = ScopeData;
-
-    #[inline]
-    fn index(&self, index: ScopeId) -> &ScopeData {
-        &self[index.index()]
-    }
-}
-
-impl IndexMut<ScopeId> for Vec<ScopeData> {
-    #[inline]
-    fn index_mut(&mut self, index: ScopeId) -> &mut ScopeData {
-        &mut self[index.index()]
-    }
-}
-
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
-pub struct ScopeId(u32);
-
-impl ScopeId {
-    pub fn new(index: usize) -> ScopeId {
-        assert!(index < (u32::MAX as usize));
-        ScopeId(index as u32)
-    }
-
-    pub fn index(self) -> usize {
-        self.0 as usize
-    }
-}
+newtype_index!(VisibilityScope, "scope");
+pub const ARGUMENT_VISIBILITY_SCOPE : VisibilityScope = VisibilityScope(0);
 
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
-pub struct ScopeData {
+pub struct VisibilityScopeData {
     pub span: Span,
-    pub parent_scope: Option<ScopeId>,
+    pub parent_scope: Option<VisibilityScope>,
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -762,6 +904,7 @@ pub enum Rvalue<'tcx> {
     Cast(CastKind, Operand<'tcx>, Ty<'tcx>),
 
     BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>),
+    CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>),
 
     UnaryOp(UnOp, Operand<'tcx>),
 
@@ -775,17 +918,6 @@ pub enum Rvalue<'tcx> {
     /// away after type-checking and before lowering.
     Aggregate(AggregateKind<'tcx>, Vec<Operand<'tcx>>),
 
-    /// Generates a slice of the form `&input[from_start..L-from_end]`
-    /// where `L` is the length of the slice. This is only created by
-    /// slice pattern matching, so e.g. a pattern of the form `[x, y,
-    /// .., z]` might create a slice with `from_start=2` and
-    /// `from_end=1`.
-    Slice {
-        input: Lvalue<'tcx>,
-        from_start: usize,
-        from_end: usize,
-    },
-
     InlineAsm {
         asm: InlineAsm,
         outputs: Vec<Lvalue<'tcx>>,
@@ -855,6 +987,16 @@ pub enum BinOp {
     Gt,
 }
 
+impl BinOp {
+    pub fn is_checkable(self) -> bool {
+        use self::BinOp::*;
+        match self {
+            Add | Sub | Mul | Shl | Shr => true,
+            _ => false
+        }
+    }
+}
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
 pub enum UnOp {
     /// The `!` operator for logical inversion
@@ -873,13 +1015,14 @@ impl<'tcx> Debug for Rvalue<'tcx> {
             Len(ref a) => write!(fmt, "Len({:?})", a),
             Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?})", lv, ty, kind),
             BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b),
+            CheckedBinaryOp(ref op, ref a, ref b) => {
+                write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b)
+            }
             UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
             Box(ref t) => write!(fmt, "Box({:?})", t),
             InlineAsm { ref asm, ref outputs, ref inputs } => {
                 write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs)
             }
-            Slice { ref input, from_start, from_end } =>
-                write!(fmt, "{:?}[{:?}..-{:?}]", input, from_start, from_end),
 
             Ref(_, borrow_kind, ref lv) => {
                 let kind_str = match borrow_kind {
@@ -917,7 +1060,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
                         ppaux::parameterized(fmt, substs, variant_def.did,
                                              ppaux::Ns::Value, &[],
                                              |tcx| {
-                            tcx.lookup_item_type(variant_def.did).generics
+                            Some(tcx.lookup_item_type(variant_def.did).generics)
                         })?;
 
                         match variant_def.kind() {
@@ -983,6 +1126,8 @@ impl<'tcx> Debug for TypedConstVal<'tcx> {
     }
 }
 
+newtype_index!(Promoted, "promoted");
+
 #[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub enum Literal<'tcx> {
     Item {
@@ -994,7 +1139,7 @@ pub enum Literal<'tcx> {
     },
     Promoted {
         // Index into the `promoted` vector of `Mir`.
-        index: usize
+        index: Promoted
     },
 }
 
@@ -1009,15 +1154,16 @@ impl<'tcx> Debug for Literal<'tcx> {
         use self::Literal::*;
         match *self {
             Item { def_id, substs } => {
-                ppaux::parameterized(fmt, substs, def_id, ppaux::Ns::Value, &[],
-                                     |tcx| tcx.lookup_item_type(def_id).generics)
+                ppaux::parameterized(
+                    fmt, substs, def_id, ppaux::Ns::Value, &[],
+                    |tcx| Some(tcx.lookup_item_type(def_id).generics))
             }
             Value { ref value } => {
                 write!(fmt, "const ")?;
                 fmt_const_val(fmt, value)
             }
             Promoted { index } => {
-                write!(fmt, "promoted{}", index)
+                write!(fmt, "{:?}", index)
             }
         }
     }
@@ -1053,3 +1199,33 @@ fn node_to_string(node_id: ast::NodeId) -> String {
 fn item_path_str(def_id: DefId) -> String {
     ty::tls::with(|tcx| tcx.item_path_str(def_id))
 }
+
+impl<'tcx> ControlFlowGraph for Mir<'tcx> {
+
+    type Node = BasicBlock;
+
+    fn num_nodes(&self) -> usize { self.basic_blocks.len() }
+
+    fn start_node(&self) -> Self::Node { START_BLOCK }
+
+    fn predecessors<'graph>(&'graph self, node: Self::Node)
+                            -> <Self as GraphPredecessors<'graph>>::Iter
+    {
+        self.predecessors_for(node).clone().into_iter()
+    }
+    fn successors<'graph>(&'graph self, node: Self::Node)
+                          -> <Self as GraphSuccessors<'graph>>::Iter
+    {
+        self.basic_blocks[node].terminator().successors().into_owned().into_iter()
+    }
+}
+
+impl<'a, 'b> GraphPredecessors<'b> for Mir<'a> {
+    type Item = BasicBlock;
+    type Iter = IntoIter<BasicBlock>;
+}
+
+impl<'a, 'b>  GraphSuccessors<'b> for Mir<'a> {
+    type Item = BasicBlock;
+    type Iter = IntoIter<BasicBlock>;
+}
index a1c0d92f60cd63c874e5ea9fc528249ba13362eb..e3905c39daa9c593d1d750ea54858bb40542e5bc 100644 (file)
@@ -59,6 +59,20 @@ impl<'a, 'gcx, 'tcx> LvalueTy<'tcx> {
                 LvalueTy::Ty {
                     ty: self.to_ty(tcx).builtin_index().unwrap()
                 },
+            ProjectionElem::Subslice { from, to } => {
+                let ty = self.to_ty(tcx);
+                LvalueTy::Ty {
+                    ty: match ty.sty {
+                        ty::TyArray(inner, size) => {
+                            tcx.mk_array(inner, size-(from as usize)-(to as usize))
+                        }
+                        ty::TySlice(..) => ty,
+                        _ => {
+                            bug!("cannot subslice non-array type: `{:?}`", self)
+                        }
+                    }
+                }
+            }
             ProjectionElem::Downcast(adt_def1, index) =>
                 match self.to_ty(tcx).sty {
                     ty::TyEnum(adt_def, substs) => {
@@ -140,11 +154,11 @@ impl<'a, 'gcx, 'tcx> Mir<'tcx> {
     {
         match *lvalue {
             Lvalue::Var(index) =>
-                LvalueTy::Ty { ty: self.var_decls[index as usize].ty },
+                LvalueTy::Ty { ty: self.var_decls[index].ty },
             Lvalue::Temp(index) =>
-                LvalueTy::Ty { ty: self.temp_decls[index as usize].ty },
+                LvalueTy::Ty { ty: self.temp_decls[index].ty },
             Lvalue::Arg(index) =>
-                LvalueTy::Ty { ty: self.arg_decls[index as usize].ty },
+                LvalueTy::Ty { ty: self.arg_decls[index].ty },
             Lvalue::Static(def_id) =>
                 LvalueTy::Ty { ty: tcx.lookup_item_type(def_id).ty },
             Lvalue::ReturnPointer =>
@@ -183,6 +197,13 @@ impl<'a, 'gcx, 'tcx> Mir<'tcx> {
                 let rhs_ty = self.operand_ty(tcx, rhs);
                 Some(self.binop_ty(tcx, op, lhs_ty, rhs_ty))
             }
+            Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
+                let lhs_ty = self.operand_ty(tcx, lhs);
+                let rhs_ty = self.operand_ty(tcx, rhs);
+                let ty = self.binop_ty(tcx, op, lhs_ty, rhs_ty);
+                let ty = tcx.mk_tup(vec![ty, tcx.types.bool]);
+                Some(ty)
+            }
             Rvalue::UnaryOp(_, ref operand) => {
                 Some(self.operand_ty(tcx, operand))
             }
@@ -212,7 +233,6 @@ impl<'a, 'gcx, 'tcx> Mir<'tcx> {
                     }
                 }
             }
-            Rvalue::Slice { .. } => None,
             Rvalue::InlineAsm { .. } => None
         }
     }
index 79c44b2b851c72a86c284673be235d165bcfc2da..4ca3907d4e602bcf94c9945818b91eed0b6eab13 100644 (file)
@@ -13,12 +13,14 @@ use hir;
 use hir::map::DefPathData;
 use hir::def_id::DefId;
 use mir::mir_map::MirMap;
-use mir::repr::Mir;
+use mir::repr::{Mir, Promoted};
 use ty::TyCtxt;
 use syntax::ast::NodeId;
 
+use std::fmt;
+
 /// Where a specific Mir comes from.
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
 pub enum MirSource {
     /// Functions and methods.
     Fn(NodeId),
@@ -30,7 +32,7 @@ pub enum MirSource {
     Static(NodeId, hir::Mutability),
 
     /// Promoted rvalues within a function.
-    Promoted(NodeId, usize)
+    Promoted(NodeId, Promoted)
 }
 
 impl<'a, 'tcx> MirSource {
@@ -70,40 +72,76 @@ impl<'a, 'tcx> MirSource {
 
 /// Various information about pass.
 pub trait Pass {
-    // fn name() for printouts of various sorts?
     // fn should_run(Session) to check if pass should run?
     fn dep_node(&self, def_id: DefId) -> DepNode<DefId> {
         DepNode::MirPass(def_id)
     }
+    fn name(&self) -> &str {
+        let name = unsafe { ::std::intrinsics::type_name::<Self>() };
+        if let Some(tail) = name.rfind(":") {
+            &name[tail+1..]
+        } else {
+            name
+        }
+    }
+    fn disambiguator<'a>(&'a self) -> Option<Box<fmt::Display+'a>> { None }
 }
 
 /// A pass which inspects the whole MirMap.
 pub trait MirMapPass<'tcx>: Pass {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &mut MirMap<'tcx>);
+    fn run_pass<'a>(
+        &mut self,
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        map: &mut MirMap<'tcx>,
+        hooks: &mut [Box<for<'s> MirPassHook<'s>>]);
+}
+
+pub trait MirPassHook<'tcx>: Pass {
+    fn on_mir_pass<'a>(
+        &mut self,
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        src: MirSource,
+        mir: &Mir<'tcx>,
+        pass: &Pass,
+        is_after: bool
+    );
 }
 
 /// A pass which inspects Mir of functions in isolation.
 pub trait MirPass<'tcx>: Pass {
-    fn run_pass_on_promoted<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                item_id: NodeId, index: usize,
-                                mir: &mut Mir<'tcx>) {
-        self.run_pass(tcx, MirSource::Promoted(item_id, index), mir);
-    }
     fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
                     src: MirSource, mir: &mut Mir<'tcx>);
 }
 
 impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &mut MirMap<'tcx>) {
+    fn run_pass<'a>(&mut self,
+                    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                    map: &mut MirMap<'tcx>,
+                    hooks: &mut [Box<for<'s> MirPassHook<'s>>])
+    {
         for (&id, mir) in &mut map.map {
             let def_id = tcx.map.local_def_id(id);
             let _task = tcx.dep_graph.in_task(self.dep_node(def_id));
 
             let src = MirSource::from_node(tcx, id);
+
+            for hook in &mut *hooks {
+                hook.on_mir_pass(tcx, src, mir, self, false);
+            }
             MirPass::run_pass(self, tcx, src, mir);
+            for hook in &mut *hooks {
+                hook.on_mir_pass(tcx, src, mir, self, true);
+            }
 
-            for (i, mir) in mir.promoted.iter_mut().enumerate() {
-                self.run_pass_on_promoted(tcx, id, i, mir);
+            for (i, mir) in mir.promoted.iter_enumerated_mut() {
+                let src = MirSource::Promoted(id, i);
+                for hook in &mut *hooks {
+                    hook.on_mir_pass(tcx, src, mir, self, false);
+                }
+                MirPass::run_pass(self, tcx, src, mir);
+                for hook in &mut *hooks {
+                    hook.on_mir_pass(tcx, src, mir, self, true);
+                }
             }
         }
     }
@@ -112,6 +150,7 @@ impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T {
 /// A manager for MIR passes.
 pub struct Passes {
     passes: Vec<Box<for<'tcx> MirMapPass<'tcx>>>,
+    pass_hooks: Vec<Box<for<'tcx> MirPassHook<'tcx>>>,
     plugin_passes: Vec<Box<for<'tcx> MirMapPass<'tcx>>>
 }
 
@@ -119,6 +158,7 @@ impl<'a, 'tcx> Passes {
     pub fn new() -> Passes {
         let passes = Passes {
             passes: Vec::new(),
+            pass_hooks: Vec::new(),
             plugin_passes: Vec::new()
         };
         passes
@@ -126,10 +166,10 @@ impl<'a, 'tcx> Passes {
 
     pub fn run_passes(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &mut MirMap<'tcx>) {
         for pass in &mut self.plugin_passes {
-            pass.run_pass(tcx, map);
+            pass.run_pass(tcx, map, &mut self.pass_hooks);
         }
         for pass in &mut self.passes {
-            pass.run_pass(tcx, map);
+            pass.run_pass(tcx, map, &mut self.pass_hooks);
         }
     }
 
@@ -137,6 +177,11 @@ impl<'a, 'tcx> Passes {
     pub fn push_pass(&mut self, pass: Box<for<'b> MirMapPass<'b>>) {
         self.passes.push(pass);
     }
+
+    /// Pushes a pass hook.
+    pub fn push_hook(&mut self, hook: Box<for<'b> MirPassHook<'b>>) {
+        self.pass_hooks.push(hook);
+    }
 }
 
 /// Copies the plugin passes.
diff --git a/src/librustc/mir/traversal.rs b/src/librustc/mir/traversal.rs
new file mode 100644 (file)
index 0000000..1af5123
--- /dev/null
@@ -0,0 +1,279 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::vec;
+
+use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::Idx;
+
+use super::repr::*;
+
+/// Preorder traversal of a graph.
+///
+/// Preorder traversal is when each node is visited before an of it's
+/// successors
+///
+/// ```text
+///
+///         A
+///        / \
+///       /   \
+///      B     C
+///       \   /
+///        \ /
+///         D
+/// ```
+///
+/// A preorder traversal of this graph is either `A B D C` or `A C D B`
+#[derive(Clone)]
+pub struct Preorder<'a, 'tcx: 'a> {
+    mir: &'a Mir<'tcx>,
+    visited: BitVector,
+    worklist: Vec<BasicBlock>,
+}
+
+impl<'a, 'tcx> Preorder<'a, 'tcx> {
+    pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> Preorder<'a, 'tcx> {
+        let worklist = vec![root];
+
+        Preorder {
+            mir: mir,
+            visited: BitVector::new(mir.basic_blocks().len()),
+            worklist: worklist
+        }
+    }
+}
+
+pub fn preorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> Preorder<'a, 'tcx> {
+    Preorder::new(mir, START_BLOCK)
+}
+
+impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
+    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+
+    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
+        while let Some(idx) = self.worklist.pop() {
+            if !self.visited.insert(idx.index()) {
+                continue;
+            }
+
+            let data = &self.mir[idx];
+
+            if let Some(ref term) = data.terminator {
+                for &succ in term.successors().iter() {
+                    self.worklist.push(succ);
+                }
+            }
+
+            return Some((idx, data));
+        }
+
+        None
+    }
+}
+
+/// Postorder traversal of a graph.
+///
+/// Postorder traversal is when each node is visited after all of it's
+/// successors, except when the successor is only reachable by a back-edge
+///
+///
+/// ```text
+///
+///         A
+///        / \
+///       /   \
+///      B     C
+///       \   /
+///        \ /
+///         D
+/// ```
+///
+/// A Postorder traversal of this graph is `D B C A` or `D C B A`
+pub struct Postorder<'a, 'tcx: 'a> {
+    mir: &'a Mir<'tcx>,
+    visited: BitVector,
+    visit_stack: Vec<(BasicBlock, vec::IntoIter<BasicBlock>)>
+}
+
+impl<'a, 'tcx> Postorder<'a, 'tcx> {
+    pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> {
+        let mut po = Postorder {
+            mir: mir,
+            visited: BitVector::new(mir.basic_blocks().len()),
+            visit_stack: Vec::new()
+        };
+
+
+        let data = &po.mir[root];
+
+        if let Some(ref term) = data.terminator {
+            po.visited.insert(root.index());
+
+            let succs = term.successors().into_owned().into_iter();
+
+            po.visit_stack.push((root, succs));
+            po.traverse_successor();
+        }
+
+        po
+    }
+
+    fn traverse_successor(&mut self) {
+        // This is quite a complex loop due to 1. the borrow checker not liking it much
+        // and 2. what exactly is going on is not clear
+        //
+        // It does the actual traversal of the graph, while the `next` method on the iterator
+        // just pops off of the stack. `visit_stack` is a stack containing pairs of nodes and
+        // iterators over the sucessors of those nodes. Each iteration attempts to get the next
+        // node from the top of the stack, then pushes that node and an iterator over the
+        // successors to the top of the stack. This loop only grows `visit_stack`, stopping when
+        // we reach a child that has no children that we haven't already visited.
+        //
+        // For a graph that looks like this:
+        //
+        //         A
+        //        / \
+        //       /   \
+        //      B     C
+        //      |     |
+        //      |     |
+        //      D     |
+        //       \   /
+        //        \ /
+        //         E
+        //
+        // The state of the stack starts out with just the root node (`A` in this case);
+        //     [(A, [B, C])]
+        //
+        // When the first call to `traverse_sucessor` happens, the following happens:
+        //
+        //     [(B, [D]),  // `B` taken from the successors of `A`, pushed to the
+        //                 // top of the stack along with the successors of `B`
+        //      (A, [C])]
+        //
+        //     [(D, [E]),  // `D` taken from successors of `B`, pushed to stack
+        //      (B, []),
+        //      (A, [C])]
+        //
+        //     [(E, []),   // `E` taken from successors of `D`, pushed to stack
+        //      (D, []),
+        //      (B, []),
+        //      (A, [C])]
+        //
+        // Now that the top of the stack has no successors we can traverse, each item will
+        // be popped off during iteration until we get back to `A`. This yeilds [E, D, B].
+        //
+        // When we yield `B` and call `traverse_successor`, we push `C` to the stack, but
+        // since we've already visited `E`, that child isn't added to the stack. The last
+        // two iterations yield `C` and finally `A` for a final traversal of [E, D, B, C, A]
+        loop {
+            let bb = if let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() {
+                if let Some(bb) = iter.next() {
+                    bb
+                } else {
+                    break;
+                }
+            } else {
+                break;
+            };
+
+            if self.visited.insert(bb.index()) {
+                if let Some(ref term) = self.mir[bb].terminator {
+                    let succs = term.successors().into_owned().into_iter();
+                    self.visit_stack.push((bb, succs));
+                }
+            }
+        }
+    }
+}
+
+pub fn postorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> Postorder<'a, 'tcx> {
+    Postorder::new(mir, START_BLOCK)
+}
+
+impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
+    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+
+    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
+        let next = self.visit_stack.pop();
+        if next.is_some() {
+            self.traverse_successor();
+        }
+
+        next.map(|(bb, _)| (bb, &self.mir[bb]))
+    }
+}
+
+/// Reverse postorder traversal of a graph
+///
+/// Reverse postorder is the reverse order of a postorder traversal.
+/// This is different to a preorder traversal and represents a natural
+/// linearisation of control-flow.
+///
+/// ```text
+///
+///         A
+///        / \
+///       /   \
+///      B     C
+///       \   /
+///        \ /
+///         D
+/// ```
+///
+/// A reverse postorder traversal of this graph is either `A B C D` or `A C B D`
+/// Note that for a graph containing no loops (i.e. A DAG), this is equivalent to
+/// a topological sort.
+///
+/// Construction of a `ReversePostorder` traversal requires doing a full
+/// postorder traversal of the graph, therefore this traversal should be
+/// constructed as few times as possible. Use the `reset` method to be able
+/// to re-use the traversal
+#[derive(Clone)]
+pub struct ReversePostorder<'a, 'tcx: 'a> {
+    mir: &'a Mir<'tcx>,
+    blocks: Vec<BasicBlock>,
+    idx: usize
+}
+
+impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
+    pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
+        let blocks : Vec<_> = Postorder::new(mir, root).map(|(bb, _)| bb).collect();
+
+        let len = blocks.len();
+
+        ReversePostorder {
+            mir: mir,
+            blocks: blocks,
+            idx: len
+        }
+    }
+
+    pub fn reset(&mut self) {
+        self.idx = self.blocks.len();
+    }
+}
+
+
+pub fn reverse_postorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> ReversePostorder<'a, 'tcx> {
+    ReversePostorder::new(mir, START_BLOCK)
+}
+
+impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
+    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+
+    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
+        if self.idx == 0 { return None; }
+        self.idx -= 1;
+
+        self.blocks.get(self.idx).map(|&bb| (bb, &self.mir[bb]))
+    }
+}
index 88460651352537357a6b7e0183110aa8a1e76681..3f714ff4d5152b3eef0a134fc49b92232e4b0d62 100644 (file)
@@ -15,7 +15,8 @@ use ty::{ClosureSubsts, FnOutput, Region, Ty};
 use mir::repr::*;
 use rustc_const_math::ConstUsize;
 use rustc_data_structures::tuple_slice::TupleSlice;
-use syntax::codemap::Span;
+use rustc_data_structures::indexed_vec::Idx;
+use syntax_pos::Span;
 
 // # The MIR Visitor
 //
@@ -97,9 +98,9 @@ macro_rules! make_mir_visitor {
                 self.super_basic_block_data(block, data);
             }
 
-            fn visit_scope_data(&mut self,
-                                scope_data: & $($mutability)* ScopeData) {
-                self.super_scope_data(scope_data);
+            fn visit_visibility_scope_data(&mut self,
+                                           scope_data: & $($mutability)* VisibilityScopeData) {
+                self.super_visibility_scope_data(scope_data);
             }
 
             fn visit_statement(&mut self,
@@ -127,6 +128,11 @@ macro_rules! make_mir_visitor {
                 self.super_terminator_kind(block, kind);
             }
 
+            fn visit_assert_message(&mut self,
+                                    msg: & $($mutability)* AssertMessage<'tcx>) {
+                self.super_assert_message(msg);
+            }
+
             fn visit_rvalue(&mut self,
                             rvalue: & $($mutability)* Rvalue<'tcx>) {
                 self.super_rvalue(rvalue);
@@ -181,6 +187,11 @@ macro_rules! make_mir_visitor {
                 self.super_span(span);
             }
 
+            fn visit_source_info(&mut self,
+                                 source_info: & $($mutability)* SourceInfo) {
+                self.super_source_info(source_info);
+            }
+
             fn visit_fn_output(&mut self,
                                fn_output: & $($mutability)* FnOutput<'tcx>) {
                 self.super_fn_output(fn_output);
@@ -231,9 +242,9 @@ macro_rules! make_mir_visitor {
                 self.super_arg_decl(arg_decl);
             }
 
-            fn visit_scope_id(&mut self,
-                              scope_id: & $($mutability)* ScopeId) {
-                self.super_scope_id(scope_id);
+            fn visit_visibility_scope(&mut self,
+                                      scope: & $($mutability)* VisibilityScope) {
+                self.super_visibility_scope(scope);
             }
 
             // The `super_xxx` methods comprise the default behavior and are
@@ -241,42 +252,30 @@ macro_rules! make_mir_visitor {
 
             fn super_mir(&mut self,
                          mir: & $($mutability)* Mir<'tcx>) {
-                let Mir {
-                    ref $($mutability)* basic_blocks,
-                    ref $($mutability)* scopes,
-                    promoted: _, // Visited by passes separately.
-                    ref $($mutability)* return_ty,
-                    ref $($mutability)* var_decls,
-                    ref $($mutability)* arg_decls,
-                    ref $($mutability)* temp_decls,
-                    upvar_decls: _,
-                    ref $($mutability)* span,
-                } = *mir;
-
-                for (index, data) in basic_blocks.into_iter().enumerate() {
+                for index in 0..mir.basic_blocks().len() {
                     let block = BasicBlock::new(index);
-                    self.visit_basic_block_data(block, data);
+                    self.visit_basic_block_data(block, &$($mutability)* mir[block]);
                 }
 
-                for scope in scopes {
-                    self.visit_scope_data(scope);
+                for scope in &$($mutability)* mir.visibility_scopes {
+                    self.visit_visibility_scope_data(scope);
                 }
 
-                self.visit_fn_output(return_ty);
+                self.visit_fn_output(&$($mutability)* mir.return_ty);
 
-                for var_decl in var_decls {
+                for var_decl in &$($mutability)* mir.var_decls {
                     self.visit_var_decl(var_decl);
                 }
 
-                for arg_decl in arg_decls {
+                for arg_decl in &$($mutability)* mir.arg_decls {
                     self.visit_arg_decl(arg_decl);
                 }
 
-                for temp_decl in temp_decls {
+                for temp_decl in &$($mutability)* mir.temp_decls {
                     self.visit_temp_decl(temp_decl);
                 }
 
-                self.visit_span(span);
+                self.visit_span(&$($mutability)* mir.span);
             }
 
             fn super_basic_block_data(&mut self,
@@ -297,16 +296,16 @@ macro_rules! make_mir_visitor {
                 }
             }
 
-            fn super_scope_data(&mut self,
-                                scope_data: & $($mutability)* ScopeData) {
-                let ScopeData {
+            fn super_visibility_scope_data(&mut self,
+                                           scope_data: & $($mutability)* VisibilityScopeData) {
+                let VisibilityScopeData {
                     ref $($mutability)* span,
                     ref $($mutability)* parent_scope,
                 } = *scope_data;
 
                 self.visit_span(span);
                 if let Some(ref $($mutability)* parent_scope) = *parent_scope {
-                    self.visit_scope_id(parent_scope);
+                    self.visit_visibility_scope(parent_scope);
                 }
             }
 
@@ -314,13 +313,11 @@ macro_rules! make_mir_visitor {
                                block: BasicBlock,
                                statement: & $($mutability)* Statement<'tcx>) {
                 let Statement {
-                    ref $($mutability)* span,
-                    ref $($mutability)* scope,
+                    ref $($mutability)* source_info,
                     ref $($mutability)* kind,
                 } = *statement;
 
-                self.visit_span(span);
-                self.visit_scope_id(scope);
+                self.visit_source_info(source_info);
                 match *kind {
                     StatementKind::Assign(ref $($mutability)* lvalue,
                                           ref $($mutability)* rvalue) => {
@@ -341,13 +338,11 @@ macro_rules! make_mir_visitor {
                                 block: BasicBlock,
                                 terminator: &$($mutability)* Terminator<'tcx>) {
                 let Terminator {
-                    ref $($mutability)* span,
-                    ref $($mutability)* scope,
+                    ref $($mutability)* source_info,
                     ref $($mutability)* kind,
                 } = *terminator;
 
-                self.visit_span(span);
-                self.visit_scope_id(scope);
+                self.visit_source_info(source_info);
                 self.visit_terminator_kind(block, kind);
             }
 
@@ -391,13 +386,24 @@ macro_rules! make_mir_visitor {
                     }
 
                     TerminatorKind::Resume |
-                    TerminatorKind::Return => {
+                    TerminatorKind::Return |
+                    TerminatorKind::Unreachable => {
                     }
 
-                    TerminatorKind::Drop { ref $($mutability)* value,
+                    TerminatorKind::Drop { ref $($mutability)* location,
                                            target,
                                            unwind } => {
-                        self.visit_lvalue(value, LvalueContext::Drop);
+                        self.visit_lvalue(location, LvalueContext::Drop);
+                        self.visit_branch(block, target);
+                        unwind.map(|t| self.visit_branch(block, t));
+                    }
+
+                    TerminatorKind::DropAndReplace { ref $($mutability)* location,
+                                                     ref $($mutability)* value,
+                                                     target,
+                                                     unwind } => {
+                        self.visit_lvalue(location, LvalueContext::Drop);
+                        self.visit_operand(value);
                         self.visit_branch(block, target);
                         unwind.map(|t| self.visit_branch(block, t));
                     }
@@ -416,6 +422,31 @@ macro_rules! make_mir_visitor {
                         }
                         cleanup.map(|t| self.visit_branch(block, t));
                     }
+
+                    TerminatorKind::Assert { ref $($mutability)* cond,
+                                             expected: _,
+                                             ref $($mutability)* msg,
+                                             target,
+                                             cleanup } => {
+                        self.visit_operand(cond);
+                        self.visit_assert_message(msg);
+                        self.visit_branch(block, target);
+                        cleanup.map(|t| self.visit_branch(block, t));
+                    }
+                }
+            }
+
+            fn super_assert_message(&mut self,
+                                    msg: & $($mutability)* AssertMessage<'tcx>) {
+                match *msg {
+                    AssertMessage::BoundsCheck {
+                        ref $($mutability)* len,
+                        ref $($mutability)* index
+                    } => {
+                        self.visit_operand(len);
+                        self.visit_operand(index);
+                    }
+                    AssertMessage::Math(_) => {}
                 }
             }
 
@@ -451,6 +482,9 @@ macro_rules! make_mir_visitor {
                     }
 
                     Rvalue::BinaryOp(_bin_op,
+                                     ref $($mutability)* lhs,
+                                     ref $($mutability)* rhs) |
+                    Rvalue::CheckedBinaryOp(_bin_op,
                                      ref $($mutability)* lhs,
                                      ref $($mutability)* rhs) => {
                         self.visit_operand(lhs);
@@ -489,15 +523,6 @@ macro_rules! make_mir_visitor {
                         }
                     }
 
-                    Rvalue::Slice { ref $($mutability)* input,
-                                    from_start,
-                                    from_end } => {
-                        self.visit_lvalue(input, LvalueContext::Slice {
-                            from_start: from_start,
-                            from_end: from_end,
-                        });
-                    }
-
                     Rvalue::InlineAsm { ref $($mutability)* outputs,
                                         ref $($mutability)* inputs,
                                         asm: _ } => {
@@ -558,6 +583,8 @@ macro_rules! make_mir_visitor {
                 match *proj {
                     ProjectionElem::Deref => {
                     }
+                    ProjectionElem::Subslice { from: _, to: _ } => {
+                    }
                     ProjectionElem::Field(_field, ref $($mutability)* ty) => {
                         self.visit_ty(ty);
                     }
@@ -579,13 +606,11 @@ macro_rules! make_mir_visitor {
                     mutability: _,
                     name: _,
                     ref $($mutability)* ty,
-                    ref $($mutability)* scope,
-                    ref $($mutability)* span,
+                    ref $($mutability)* source_info,
                 } = *var_decl;
 
                 self.visit_ty(ty);
-                self.visit_scope_id(scope);
-                self.visit_span(span);
+                self.visit_source_info(source_info);
             }
 
             fn super_temp_decl(&mut self,
@@ -608,8 +633,8 @@ macro_rules! make_mir_visitor {
                 self.visit_ty(ty);
             }
 
-            fn super_scope_id(&mut self,
-                              _scope_id: & $($mutability)* ScopeId) {
+            fn super_visibility_scope(&mut self,
+                                      _scope: & $($mutability)* VisibilityScope) {
             }
 
             fn super_branch(&mut self,
@@ -664,6 +689,16 @@ macro_rules! make_mir_visitor {
             fn super_span(&mut self, _span: & $($mutability)* Span) {
             }
 
+            fn super_source_info(&mut self, source_info: & $($mutability)* SourceInfo) {
+                let SourceInfo {
+                    ref $($mutability)* span,
+                    ref $($mutability)* scope,
+                } = *source_info;
+
+                self.visit_span(span);
+                self.visit_visibility_scope(scope);
+            }
+
             fn super_fn_output(&mut self, fn_output: & $($mutability)* FnOutput<'tcx>) {
                 match *fn_output {
                     FnOutput::FnConverging(ref $($mutability)* ty) => {
index da5555dbd64534cafef47127aecf9d8452e8df0b..a37990061920b6fa299a2de535010bb78a8490e7 100644 (file)
@@ -26,11 +26,12 @@ use middle::cstore;
 use syntax::ast::{self, IntTy, UintTy};
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
-use syntax::errors::{ColorConfig, Handler};
 use syntax::parse;
 use syntax::parse::token::InternedString;
 use syntax::feature_gate::UnstableFeatures;
 
+use errors::{ColorConfig, Handler};
+
 use getopts;
 use std::collections::HashMap;
 use std::env;
@@ -112,7 +113,6 @@ pub struct Options {
     // with additional crate configurations during the compile process
     pub crate_types: Vec<CrateType>,
 
-    pub gc: bool,
     pub optimize: OptLevel,
     pub debug_assertions: bool,
     pub debuginfo: DebugInfoLevel,
@@ -242,7 +242,6 @@ pub fn host_triple() -> &'static str {
 pub fn basic_options() -> Options {
     Options {
         crate_types: Vec::new(),
-        gc: false,
         optimize: OptLevel::No,
         debuginfo: NoDebugInfo,
         lint_opts: Vec::new(),
@@ -632,14 +631,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "omit landing pads for unwinding"),
     debug_llvm: bool = (false, parse_bool,
         "enable debug output from LLVM"),
-    count_type_sizes: bool = (false, parse_bool,
-        "count the sizes of aggregate types"),
     meta_stats: bool = (false, parse_bool,
         "gather metadata statistics"),
     print_link_args: bool = (false, parse_bool,
         "print the arguments passed to the linker"),
-    gc: bool = (false, parse_bool,
-        "garbage collect shared data (experimental)"),
     print_llvm_passes: bool = (false, parse_bool,
         "prints the llvm optimization passes being run"),
     ast_json: bool = (false, parse_bool,
@@ -796,6 +791,7 @@ pub fn build_target_config(opts: &Options, sp: &Handler) -> Config {
     };
 
     let (int_type, uint_type) = match &target.target_pointer_width[..] {
+        "16" => (ast::IntTy::I16, ast::UintTy::U16),
         "32" => (ast::IntTy::I32, ast::UintTy::U32),
         "64" => (ast::IntTy::I64, ast::UintTy::U64),
         w    => panic!(sp.fatal(&format!("target specification was invalid: \
@@ -1104,7 +1100,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let no_analysis = debugging_opts.no_analysis;
 
     let mut output_types = HashMap::new();
-    if !debugging_opts.parse_only && !no_trans {
+    if !debugging_opts.parse_only {
         for list in matches.opt_strs("emit") {
             for output_type in list.split(',') {
                 let mut parts = output_type.splitn(2, '=');
@@ -1188,7 +1184,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         }
     };
     let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
-    let gc = debugging_opts.gc;
     let debuginfo = if matches.opt_present("g") {
         if cg.debuginfo.is_some() {
             early_error(error_format, "-g and -C debuginfo both provided");
@@ -1271,7 +1266,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     Options {
         crate_types: crate_types,
-        gc: gc,
         optimize: opt_level,
         debuginfo: debuginfo,
         lint_opts: lint_opts,
@@ -1427,12 +1421,11 @@ mod tests {
     use middle::cstore::DummyCrateStore;
     use session::config::{build_configuration, build_session_options};
     use session::build_session;
-
+    use errors;
     use std::rc::Rc;
     use getopts::{getopts, OptGroup};
     use syntax::attr;
     use syntax::attr::AttrMetaMethods;
-    use syntax::diagnostics;
 
     fn optgroups() -> Vec<OptGroup> {
         super::rustc_optgroups().into_iter()
@@ -1449,7 +1442,7 @@ mod tests {
               Ok(m) => m,
               Err(f) => panic!("test_switch_implies_cfg_test: {}", f)
             };
-        let registry = diagnostics::registry::Registry::new(&[]);
+        let registry = errors::registry::Registry::new(&[]);
         let sessopts = build_session_options(matches);
         let sess = build_session(sessopts, &dep_graph, None, registry, Rc::new(DummyCrateStore));
         let cfg = build_configuration(&sess);
@@ -1469,7 +1462,7 @@ mod tests {
                 panic!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
               }
             };
-        let registry = diagnostics::registry::Registry::new(&[]);
+        let registry = errors::registry::Registry::new(&[]);
         let sessopts = build_session_options(matches);
         let sess = build_session(sessopts, &dep_graph, None, registry,
                                  Rc::new(DummyCrateStore));
@@ -1486,7 +1479,7 @@ mod tests {
             let matches = getopts(&[
                 "-Awarnings".to_string()
             ], &optgroups()).unwrap();
-            let registry = diagnostics::registry::Registry::new(&[]);
+            let registry = errors::registry::Registry::new(&[]);
             let sessopts = build_session_options(&matches);
             let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
@@ -1498,7 +1491,7 @@ mod tests {
                 "-Awarnings".to_string(),
                 "-Dwarnings".to_string()
             ], &optgroups()).unwrap();
-            let registry = diagnostics::registry::Registry::new(&[]);
+            let registry = errors::registry::Registry::new(&[]);
             let sessopts = build_session_options(&matches);
             let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
@@ -1509,7 +1502,7 @@ mod tests {
             let matches = getopts(&[
                 "-Adead_code".to_string()
             ], &optgroups()).unwrap();
-            let registry = diagnostics::registry::Registry::new(&[]);
+            let registry = errors::registry::Registry::new(&[]);
             let sessopts = build_session_options(&matches);
             let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
index 907241d1746d6f7317310245dcb29cdaad7fad8f..57c4af6bed569406138d3096914b43e092ee0ae8 100644 (file)
@@ -9,27 +9,28 @@
 // except according to those terms.
 
 use dep_graph::DepGraph;
+use hir::def_id::DefIndex;
+use hir::svh::Svh;
 use lint;
 use middle::cstore::CrateStore;
 use middle::dependency_format;
 use session::search_paths::PathKind;
-use session::config::PanicStrategy;
+use session::config::{DebugInfoLevel, PanicStrategy};
 use ty::tls;
 use util::nodemap::{NodeMap, FnvHashMap};
 use mir::transform as mir_pass;
 
-use syntax::ast::{NodeId, NodeIdAssigner, Name};
-use syntax::codemap::{Span, MultiSpan};
-use syntax::errors::{self, DiagnosticBuilder};
-use syntax::errors::emitter::{Emitter, BasicEmitter, EmitterWriter};
-use syntax::errors::json::JsonEmitter;
-use syntax::diagnostics;
+use syntax::ast::{NodeId, Name};
+use errors::{self, DiagnosticBuilder};
+use errors::emitter::{Emitter, BasicEmitter, EmitterWriter};
+use syntax::json::JsonEmitter;
 use syntax::feature_gate;
 use syntax::parse;
 use syntax::parse::ParseSess;
 use syntax::parse::token;
 use syntax::{ast, codemap};
 use syntax::feature_gate::AttributeType;
+use syntax_pos::{Span, MultiSpan};
 
 use rustc_back::target::Target;
 use llvm;
@@ -249,15 +250,12 @@ impl Session {
                     msg: String) {
         let lint_id = lint::LintId::of(lint);
         let mut lints = self.lints.borrow_mut();
-        match lints.get_mut(&id) {
-            Some(arr) => {
-                let tuple = (lint_id, sp, msg);
-                if !arr.contains(&tuple) {
-                    arr.push(tuple);
-                }
-                return;
+        if let Some(arr) = lints.get_mut(&id) {
+            let tuple = (lint_id, sp, msg);
+            if !arr.contains(&tuple) {
+                arr.push(tuple);
             }
-            None => {}
+            return;
         }
         lints.insert(id, vec!((lint_id, sp, msg)));
     }
@@ -271,6 +269,9 @@ impl Session {
 
         id
     }
+    pub fn next_node_id(&self) -> NodeId {
+        self.reserve_node_ids(1)
+    }
     pub fn diagnostic<'a>(&'a self) -> &'a errors::Handler {
         &self.parse_sess.span_diagnostic
     }
@@ -282,9 +283,6 @@ impl Session {
     pub fn count_llvm_insns(&self) -> bool {
         self.opts.debugging_opts.count_llvm_insns
     }
-    pub fn count_type_sizes(&self) -> bool {
-        self.opts.debugging_opts.count_type_sizes
-    }
     pub fn time_llvm_passes(&self) -> bool {
         self.opts.debugging_opts.time_llvm_passes
     }
@@ -312,6 +310,19 @@ impl Session {
     pub fn nonzeroing_move_hints(&self) -> bool {
         self.opts.debugging_opts.enable_nonzeroing_move_hints
     }
+
+    pub fn must_not_eliminate_frame_pointers(&self) -> bool {
+        self.opts.debuginfo != DebugInfoLevel::NoDebugInfo ||
+        !self.target.target.options.eliminate_frame_pointer
+    }
+
+    /// Returns the symbol name for the registrar function,
+    /// given the crate Svh and the function DefIndex.
+    pub fn generate_plugin_registrar_symbol(&self, svh: &Svh, index: DefIndex)
+                                            -> String {
+        format!("__rustc_plugin_registrar__{}_{}", svh, index.as_usize())
+    }
+
     pub fn sysroot<'a>(&'a self) -> &'a Path {
         match self.opts.maybe_sysroot {
             Some (ref sysroot) => sysroot,
@@ -334,20 +345,6 @@ impl Session {
     }
 }
 
-impl NodeIdAssigner for Session {
-    fn next_node_id(&self) -> NodeId {
-        self.reserve_node_ids(1)
-    }
-
-    fn peek_node_id(&self) -> NodeId {
-        self.next_node_id.get().checked_add(1).unwrap()
-    }
-
-    fn diagnostic(&self) -> &errors::Handler {
-        self.diagnostic()
-    }
-}
-
 fn split_msg_into_multilines(msg: &str) -> Option<String> {
     // Conditions for enabling multi-line errors:
     if !msg.contains("mismatched types") &&
@@ -412,7 +409,7 @@ fn split_msg_into_multilines(msg: &str) -> Option<String> {
 pub fn build_session(sopts: config::Options,
                      dep_graph: &DepGraph,
                      local_crate_source_file: Option<PathBuf>,
-                     registry: diagnostics::registry::Registry,
+                     registry: errors::registry::Registry,
                      cstore: Rc<for<'a> CrateStore<'a>>)
                      -> Session {
     build_session_with_codemap(sopts,
@@ -426,7 +423,7 @@ pub fn build_session(sopts: config::Options,
 pub fn build_session_with_codemap(sopts: config::Options,
                                   dep_graph: &DepGraph,
                                   local_crate_source_file: Option<PathBuf>,
-                                  registry: diagnostics::registry::Registry,
+                                  registry: errors::registry::Registry,
                                   cstore: Rc<for<'a> CrateStore<'a>>,
                                   codemap: Rc<codemap::CodeMap>)
                                   -> Session {
@@ -443,7 +440,10 @@ pub fn build_session_with_codemap(sopts: config::Options,
 
     let emitter: Box<Emitter> = match sopts.error_format {
         config::ErrorOutputType::HumanReadable(color_config) => {
-            Box::new(EmitterWriter::stderr(color_config, Some(registry), codemap.clone()))
+            Box::new(EmitterWriter::stderr(color_config,
+                                           Some(registry),
+                                           codemap.clone(),
+                                           errors::snippet::FormatMode::EnvironmentSelected))
         }
         config::ErrorOutputType::Json => {
             Box::new(JsonEmitter::stderr(Some(registry), codemap.clone()))
index 414b9fa70c3026381307da3aeeab2076d5be1065..4344eb1ebf6645faf708d6f0c55677fd10c5b004 100644 (file)
@@ -17,7 +17,7 @@ use hir::def_id::DefId;
 use ty::subst::TypeSpace;
 use ty::{self, Ty, TyCtxt};
 use infer::{InferCtxt, TypeOrigin};
-use syntax::codemap::DUMMY_SP;
+use syntax_pos::DUMMY_SP;
 
 #[derive(Copy, Clone)]
 struct InferIsLocal(bool);
index 9a69958fea01432a3b2ad518e91d7784d8ed4f4d..3b9ecb88258540cd011bb543b07be1a8bd5145e7 100644 (file)
@@ -37,8 +37,8 @@ use std::cmp;
 use std::fmt;
 use syntax::ast;
 use syntax::attr::{AttributeMethods, AttrMetaMethods};
-use syntax::codemap::Span;
-use syntax::errors::DiagnosticBuilder;
+use syntax_pos::Span;
+use errors::DiagnosticBuilder;
 
 #[derive(Debug, PartialEq, Eq, Hash)]
 pub struct TraitErrorKey<'tcx> {
@@ -908,6 +908,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                 err.note("only the last field of a struct or enum variant \
                           may have a dynamically sized type");
             }
+            ObligationCauseCode::ConstSized => {
+                err.note("constant expressions must have a statically known size");
+            }
             ObligationCauseCode::SharedStatic => {
                 err.note("shared static variables must have a type that implements `Sync`");
             }
index d9d0367bdcb10a872878dacdd5b68bd2a1a9c7ea..23ffa4db96f77549b9382d7910c968d63ab64b14 100644 (file)
@@ -171,8 +171,12 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> {
         // debug output much nicer to read and so on.
         let obligation = infcx.resolve_type_vars_if_possible(&obligation);
 
-        if infcx.tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate)
-        {
+        debug!("register_predicate_obligation(obligation={:?})", obligation);
+
+        infcx.obligations_in_snapshot.set(true);
+
+        if infcx.tcx.fulfilled_predicates.borrow().check_duplicate(&obligation.predicate) {
+            debug!("register_predicate_obligation: duplicate");
             return
         }
 
@@ -404,6 +408,8 @@ fn process_predicate<'a, 'gcx, 'tcx>(
                     // also includes references to its upvars as part
                     // of its type, and those types are resolved at
                     // the same time.
+                    //
+                    // FIXME(#32286) logic seems false if no upvars
                     pending_obligation.stalled_on =
                         trait_ref_type_vars(selcx, data.to_poly_trait_ref());
 
index c5db2a8a7807b99c6729d9ae51a67ee9a423e10a..17aa6544fe798d169244c092d16fa17ecf6a45e4 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! Trait Resolution. See the Book for more.
+//! Trait Resolution. See README.md for an overview of how this works.
 
 pub use self::SelectionError::*;
 pub use self::FulfillmentErrorCode::*;
@@ -23,15 +23,16 @@ use infer::InferCtxt;
 
 use std::rc::Rc;
 use syntax::ast;
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
 
 pub use self::error_reporting::TraitErrorKey;
 pub use self::coherence::orphan_check;
 pub use self::coherence::overlapping_impls;
 pub use self::coherence::OrphanCheckErr;
 pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
-pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
-pub use self::project::{normalize, Normalized};
+pub use self::project::MismatchedProjectionTypes;
+pub use self::project::{normalize, normalize_projection_type, Normalized};
+pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, ProjectionMode};
 pub use self::object_safety::ObjectSafetyViolation;
 pub use self::object_safety::MethodViolationCode;
 pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};
@@ -126,6 +127,9 @@ pub enum ObligationCauseCode<'tcx> {
     // Types of fields (other than the last) in a struct must be sized.
     FieldSized,
 
+    // Constant expressions must be sized.
+    ConstSized,
+
     // static items must have `Sync` type
     SharedStatic,
 
index 5c7095beb79c8e6221aa99dbbb9931061b93ed7c..30faf1a5f8b91b75fc13bf0ea69aaaec3f7acef3 100644 (file)
@@ -24,12 +24,13 @@ use super::VtableImplData;
 use super::util;
 
 use hir::def_id::DefId;
-use infer::{self, InferOk, TypeOrigin};
+use infer::{InferOk, TypeOrigin};
+use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
+use syntax::parse::token;
+use syntax::ast;
 use ty::subst::Subst;
 use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
 use ty::fold::{TypeFoldable, TypeFolder};
-use syntax::parse::token;
-use syntax::ast;
 use util::common::FN_OUTPUT_NAME;
 
 use std::rc::Rc;
@@ -182,7 +183,8 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
         let skol_obligation = obligation.with(skol_predicate);
         match project_and_unify_type(selcx, &skol_obligation) {
             Ok(result) => {
-                match infcx.leak_check(false, &skol_map, snapshot) {
+                let span = obligation.cause.span;
+                match infcx.leak_check(false, span, &skol_map, snapshot) {
                     Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &result)),
                     Err(e) => Err(MismatchedProjectionTypes { err: e }),
                 }
@@ -207,7 +209,7 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
     debug!("project_and_unify_type(obligation={:?})",
            obligation);
 
-    let Normalized { value: normalized_ty, obligations } =
+    let Normalized { value: normalized_ty, mut obligations } =
         match opt_normalize_projection_type(selcx,
                                             obligation.predicate.projection_ty.clone(),
                                             obligation.cause.clone(),
@@ -224,8 +226,9 @@ fn project_and_unify_type<'cx, 'gcx, 'tcx>(
     let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span);
     match infcx.eq_types(true, origin, normalized_ty, obligation.predicate.ty) {
         Ok(InferOk { obligations: inferred_obligations, .. }) => {
-            // FIXME(#32730) propagate obligations
+            // FIXME(#32730) once obligations are generated in inference, drop this assertion
             assert!(inferred_obligations.is_empty());
+            obligations.extend(inferred_obligations);
             Ok(Some(obligations))
         },
         Err(err) => Err(MismatchedProjectionTypes { err: err }),
@@ -255,9 +258,13 @@ pub fn normalize_with_depth<'a, 'b, 'gcx, 'tcx, T>(
 
     where T : TypeFoldable<'tcx>
 {
+    debug!("normalize_with_depth(depth={}, value={:?})", depth, value);
     let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, depth);
     let result = normalizer.fold(value);
-
+    debug!("normalize_with_depth: depth={} result={:?} with {} obligations",
+           depth, result, normalizer.obligations.len());
+    debug!("normalize_with_depth: depth={} obligations={:?}",
+           depth, normalizer.obligations);
     Normalized {
         value: result,
         obligations: normalizer.obligations,
@@ -329,13 +336,16 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
                 // binder). It would be better to normalize in a
                 // binding-aware fashion.
 
-                let Normalized { value: ty, obligations } =
+                let Normalized { value: normalized_ty, obligations } =
                     normalize_projection_type(self.selcx,
                                               data.clone(),
                                               self.cause.clone(),
                                               self.depth);
+                debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \
+                        with {} add'l obligations",
+                       self.depth, ty, normalized_ty, obligations.len());
                 self.obligations.extend(obligations);
-                ty
+                normalized_ty
             }
 
             _ => {
@@ -403,64 +413,161 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
     depth: usize)
     -> Option<NormalizedTy<'tcx>>
 {
-    debug!("normalize_projection_type(\
+    let infcx = selcx.infcx();
+
+    let projection_ty = infcx.resolve_type_vars_if_possible(&projection_ty);
+
+    debug!("opt_normalize_projection_type(\
            projection_ty={:?}, \
            depth={})",
            projection_ty,
            depth);
 
+    // FIXME(#20304) For now, I am caching here, which is good, but it
+    // means we don't capture the type variables that are created in
+    // the case of ambiguity. Which means we may create a large stream
+    // of such variables. OTOH, if we move the caching up a level, we
+    // would not benefit from caching when proving `T: Trait<U=Foo>`
+    // bounds. It might be the case that we want two distinct caches,
+    // or else another kind of cache entry.
+
+    match infcx.projection_cache.borrow_mut().try_start(projection_ty) {
+        Ok(()) => { }
+        Err(ProjectionCacheEntry::Ambiguous) => {
+            // If we found ambiguity the last time, that generally
+            // means we will continue to do so until some type in the
+            // key changes (and we know it hasn't, because we just
+            // fully resolved it). One exception though is closure
+            // types, which can transition from having a fixed kind to
+            // no kind with no visible change in the key.
+            //
+            // FIXME(#32286) refactor this so that closure type
+            // changes
+            debug!("opt_normalize_projection_type: \
+                    found cache entry: ambiguous");
+            if !projection_ty.has_closure_types() {
+                return None;
+            }
+        }
+        Err(ProjectionCacheEntry::InProgress) => {
+            // If while normalized A::B, we are asked to normalize
+            // A::B, just return A::B itself. This is a conservative
+            // answer, in the sense that A::B *is* clearly equivalent
+            // to A::B, though there may be a better value we can
+            // find.
+
+            // Under lazy normalization, this can arise when
+            // bootstrapping.  That is, imagine an environment with a
+            // where-clause like `A::B == u32`. Now, if we are asked
+            // to normalize `A::B`, we will want to check the
+            // where-clauses in scope. So we will try to unify `A::B`
+            // with `A::B`, which can trigger a recursive
+            // normalization. In that case, I think we will want this code:
+            //
+            // ```
+            // let ty = selcx.tcx().mk_projection(projection_ty.trait_ref,
+            //                                    projection_ty.item_name);
+            // return Some(NormalizedTy { value: v, obligations: vec![] });
+            // ```
+
+            debug!("opt_normalize_projection_type: \
+                    found cache entry: in-progress");
+
+            // But for now, let's classify this as an overflow:
+            let recursion_limit = selcx.tcx().sess.recursion_limit.get();
+            let obligation = Obligation::with_depth(cause.clone(),
+                                                    recursion_limit,
+                                                    projection_ty);
+            selcx.infcx().report_overflow_error(&obligation, false);
+        }
+        Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
+            // If we find the value in the cache, then the obligations
+            // have already been returned from the previous entry (and
+            // should therefore have been honored).
+            debug!("opt_normalize_projection_type: \
+                    found normalized ty `{:?}`",
+                   ty);
+            return Some(NormalizedTy { value: ty, obligations: vec![] });
+        }
+        Err(ProjectionCacheEntry::Error) => {
+            debug!("opt_normalize_projection_type: \
+                    found error");
+            return Some(normalize_to_error(selcx, projection_ty, cause, depth));
+        }
+    }
+
     let obligation = Obligation::with_depth(cause.clone(), depth, projection_ty.clone());
     match project_type(selcx, &obligation) {
-        Ok(ProjectedTy::Progress(projected_ty, mut obligations)) => {
+        Ok(ProjectedTy::Progress(Progress { ty: projected_ty,
+                                            mut obligations,
+                                            cacheable })) => {
             // if projection succeeded, then what we get out of this
             // is also non-normalized (consider: it was derived from
             // an impl, where-clause etc) and hence we must
             // re-normalize it
 
-            debug!("normalize_projection_type: projected_ty={:?} depth={} obligations={:?}",
+            debug!("opt_normalize_projection_type: \
+                    projected_ty={:?} \
+                    depth={} \
+                    obligations={:?} \
+                    cacheable={:?}",
                    projected_ty,
                    depth,
-                   obligations);
+                   obligations,
+                   cacheable);
 
-            if projected_ty.has_projection_types() {
+            let result = if projected_ty.has_projection_types() {
                 let mut normalizer = AssociatedTypeNormalizer::new(selcx, cause, depth+1);
                 let normalized_ty = normalizer.fold(&projected_ty);
 
-                debug!("normalize_projection_type: normalized_ty={:?} depth={}",
+                debug!("opt_normalize_projection_type: \
+                        normalized_ty={:?} depth={}",
                        normalized_ty,
                        depth);
 
                 obligations.extend(normalizer.obligations);
-                Some(Normalized {
+                Normalized {
                     value: normalized_ty,
                     obligations: obligations,
-                })
+                }
             } else {
-                Some(Normalized {
+                Normalized {
                     value: projected_ty,
                     obligations: obligations,
-                })
-            }
+                }
+            };
+            infcx.projection_cache.borrow_mut()
+                                  .complete(projection_ty, &result, cacheable);
+            Some(result)
         }
         Ok(ProjectedTy::NoProgress(projected_ty)) => {
-            debug!("normalize_projection_type: projected_ty={:?} no progress",
+            debug!("opt_normalize_projection_type: \
+                    projected_ty={:?} no progress",
                    projected_ty);
-            Some(Normalized {
+            let result = Normalized {
                 value: projected_ty,
                 obligations: vec!()
-            })
+            };
+            infcx.projection_cache.borrow_mut()
+                                  .complete(projection_ty, &result, true);
+            Some(result)
         }
         Err(ProjectionTyError::TooManyCandidates) => {
-            debug!("normalize_projection_type: too many candidates");
+            debug!("opt_normalize_projection_type: \
+                    too many candidates");
+            infcx.projection_cache.borrow_mut()
+                                  .ambiguous(projection_ty);
             None
         }
         Err(ProjectionTyError::TraitSelectionError(_)) => {
-            debug!("normalize_projection_type: ERROR");
+            debug!("opt_normalize_projection_type: ERROR");
             // if we got an error processing the `T as Trait` part,
             // just return `ty::err` but add the obligation `T :
             // Trait`, which when processed will cause the error to be
             // reported later
 
+            infcx.projection_cache.borrow_mut()
+                                  .error(projection_ty);
             Some(normalize_to_error(selcx, projection_ty, cause, depth))
         }
     }
@@ -503,11 +610,43 @@ fn normalize_to_error<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 'tc
 }
 
 enum ProjectedTy<'tcx> {
-    Progress(Ty<'tcx>, Vec<PredicateObligation<'tcx>>),
+    Progress(Progress<'tcx>),
     NoProgress(Ty<'tcx>),
 }
 
+struct Progress<'tcx> {
+    ty: Ty<'tcx>,
+    obligations: Vec<PredicateObligation<'tcx>>,
+    cacheable: bool,
+}
+
+impl<'tcx> Progress<'tcx> {
+    fn error<'a,'gcx>(tcx: TyCtxt<'a,'gcx,'tcx>) -> Self {
+        Progress {
+            ty: tcx.types.err,
+            obligations: vec![],
+            cacheable: true
+        }
+    }
+
+    fn with_addl_obligations(mut self,
+                             mut obligations: Vec<PredicateObligation<'tcx>>)
+                             -> Self {
+        debug!("with_addl_obligations: self.obligations.len={} obligations.len={}",
+               self.obligations.len(), obligations.len());
+
+        debug!("with_addl_obligations: self.obligations={:?} obligations={:?}",
+               self.obligations, obligations);
+
+        self.obligations.append(&mut obligations);
+        self
+    }
+}
+
 /// Compute the result of a projection type (if we can).
+///
+/// IMPORTANT:
+/// - `obligation` must be fully normalized
 fn project_type<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>)
@@ -522,13 +661,12 @@ fn project_type<'cx, 'gcx, 'tcx>(
         selcx.infcx().report_overflow_error(&obligation, true);
     }
 
-    let obligation_trait_ref =
-        selcx.infcx().resolve_type_vars_if_possible(&obligation.predicate.trait_ref);
+    let obligation_trait_ref = &obligation.predicate.trait_ref;
 
     debug!("project: obligation_trait_ref={:?}", obligation_trait_ref);
 
     if obligation_trait_ref.references_error() {
-        return Ok(ProjectedTy::Progress(selcx.tcx().types.err, vec!()));
+        return Ok(ProjectedTy::Progress(Progress::error(selcx.tcx())));
     }
 
     let mut candidates = ProjectionTyCandidateSet {
@@ -606,16 +744,17 @@ fn project_type<'cx, 'gcx, 'tcx>(
 
     match candidates.vec.pop() {
         Some(candidate) => {
-            let (ty, obligations) = confirm_candidate(selcx,
-                                                      obligation,
-                                                      &obligation_trait_ref,
-                                                      candidate);
-            Ok(ProjectedTy::Progress(ty, obligations))
+            Ok(ProjectedTy::Progress(
+                confirm_candidate(selcx,
+                                  obligation,
+                                  &obligation_trait_ref,
+                                  candidate)))
         }
         None => {
-            Ok(ProjectedTy::NoProgress(selcx.tcx().mk_projection(
-                obligation.predicate.trait_ref.clone(),
-                obligation.predicate.item_name)))
+            Ok(ProjectedTy::NoProgress(
+                selcx.tcx().mk_projection(
+                    obligation.predicate.trait_ref.clone(),
+                    obligation.predicate.item_name)))
         }
     }
 }
@@ -710,7 +849,8 @@ fn assemble_candidates_from_predicates<'cx, 'gcx, 'tcx, I>(
                                               origin,
                                               data_poly_trait_ref,
                                               obligation_poly_trait_ref)
-                        // FIXME(#32730) propagate obligations
+                        // FIXME(#32730) once obligations are propagated from unification in
+                        // inference, drop this assertion
                         .map(|InferOk { obligations, .. }| assert!(obligations.is_empty()))
                         .is_ok()
                 });
@@ -917,7 +1057,7 @@ fn confirm_candidate<'cx, 'gcx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     obligation_trait_ref: &ty::TraitRef<'tcx>,
     candidate: ProjectionTyCandidate<'tcx>)
-    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+    -> Progress<'tcx>
 {
     debug!("confirm_candidate(candidate={:?}, obligation={:?})",
            candidate,
@@ -939,7 +1079,7 @@ fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     obligation_trait_ref: &ty::TraitRef<'tcx>)
-    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+    -> Progress<'tcx>
 {
     let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref();
     let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate());
@@ -977,7 +1117,7 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation:  &ProjectionTyObligation<'tcx>,
     obligation_trait_ref: &ty::TraitRef<'tcx>)
-    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+    -> Progress<'tcx>
 {
     let self_ty = obligation_trait_ref.self_ty();
     let object_ty = selcx.infcx().shallow_resolve(self_ty);
@@ -989,7 +1129,7 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
             span_bug!(
                 obligation.cause.span,
                 "confirm_object_candidate called with non-object: {:?}",
-                object_ty);
+                object_ty)
         }
     };
     let projection_bounds = data.projection_bounds_with_self_ty(selcx.tcx(), object_ty);
@@ -1033,7 +1173,7 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
                 debug!("confirm_object_candidate: no env-predicate \
                         found in object type `{:?}`; ill-formed",
                        object_ty);
-                return (selcx.tcx().types.err, vec!());
+                return Progress::error(selcx.tcx());
             }
         }
     };
@@ -1045,10 +1185,10 @@ fn confirm_fn_pointer_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     fn_pointer_vtable: VtableFnPointerData<'tcx, PredicateObligation<'tcx>>)
-    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+    -> Progress<'tcx>
 {
-    // FIXME(#32730) propagate obligations (fn pointer vtable nested obligations ONLY come from
-    // unification in inference)
+    // FIXME(#32730) drop this assertion once obligations are propagated from inference (fn pointer
+    // vtable nested obligations ONLY come from unification in inference)
     assert!(fn_pointer_vtable.nested.is_empty());
     let fn_type = selcx.infcx().shallow_resolve(fn_pointer_vtable.fn_ty);
     let sig = fn_type.fn_sig();
@@ -1059,23 +1199,29 @@ fn confirm_closure_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>)
-    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+    -> Progress<'tcx>
 {
     let closure_typer = selcx.closure_typer();
     let closure_type = closure_typer.closure_type(vtable.closure_def_id, vtable.substs);
     let Normalized {
         value: closure_type,
-        mut obligations
+        obligations
     } = normalize_with_depth(selcx,
                              obligation.cause.clone(),
                              obligation.recursion_depth+1,
                              &closure_type);
-    let (ty, mut cc_obligations) = confirm_callable_candidate(selcx,
-                                                              obligation,
-                                                              &closure_type.sig,
-                                                              util::TupleArgumentsFlag::No);
-    obligations.append(&mut cc_obligations);
-    (ty, obligations)
+
+    debug!("confirm_closure_candidate: obligation={:?},closure_type={:?},obligations={:?}",
+           obligation,
+           closure_type,
+           obligations);
+
+    confirm_callable_candidate(selcx,
+                               obligation,
+                               &closure_type.sig,
+                               util::TupleArgumentsFlag::No)
+        .with_addl_obligations(obligations)
+        .with_addl_obligations(vtable.nested)
 }
 
 fn confirm_callable_candidate<'cx, 'gcx, 'tcx>(
@@ -1083,7 +1229,7 @@ fn confirm_callable_candidate<'cx, 'gcx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     fn_sig: &ty::PolyFnSig<'tcx>,
     flag: util::TupleArgumentsFlag)
-    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+    -> Progress<'tcx>
 {
     let tcx = selcx.tcx();
 
@@ -1116,46 +1262,38 @@ fn confirm_param_env_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     poly_projection: ty::PolyProjectionPredicate<'tcx>)
-    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+    -> Progress<'tcx>
 {
     let infcx = selcx.infcx();
-
-    let projection =
-        infcx.replace_late_bound_regions_with_fresh_var(
-            obligation.cause.span,
-            infer::LateBoundRegionConversionTime::HigherRankedType,
-            &poly_projection).0;
-
-    assert_eq!(projection.projection_ty.item_name,
-               obligation.predicate.item_name);
-
     let origin = TypeOrigin::RelateOutputImplTypes(obligation.cause.span);
-    match infcx.eq_trait_refs(false,
-                              origin,
-                              obligation.predicate.trait_ref.clone(),
-                              projection.projection_ty.trait_ref.clone()) {
-        Ok(InferOk { obligations, .. }) => {
-            // FIXME(#32730) propagate obligations
+    let trait_ref = obligation.predicate.trait_ref;
+    match infcx.match_poly_projection_predicate(origin, poly_projection, trait_ref) {
+        Ok(InferOk { value: ty_match, obligations }) => {
+            // FIXME(#32730) once obligations are generated in inference, drop this assertion
             assert!(obligations.is_empty());
+            Progress {
+                ty: ty_match.value,
+                obligations: obligations,
+                cacheable: ty_match.unconstrained_regions.is_empty(),
+            }
         }
         Err(e) => {
             span_bug!(
                 obligation.cause.span,
-                "Failed to unify `{:?}` and `{:?}` in projection: {}",
+                "Failed to unify obligation `{:?}` \
+                 with poly_projection `{:?}`: {:?}",
                 obligation,
-                projection,
+                poly_projection,
                 e);
         }
     }
-
-    (projection.ty, vec!())
 }
 
 fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>)
-    -> (Ty<'tcx>, Vec<PredicateObligation<'tcx>>)
+    -> Progress<'tcx>
 {
     let VtableImplData { substs, nested, impl_def_id } = impl_vtable;
 
@@ -1176,7 +1314,11 @@ fn confirm_impl_candidate<'cx, 'gcx, 'tcx>(
                 tcx.types.err
             });
             let substs = translate_substs(selcx.infcx(), impl_def_id, substs, node_item.node);
-            (ty.subst(tcx, substs), nested)
+            Progress {
+                ty: ty.subst(tcx, substs),
+                obligations: nested,
+                cacheable: true
+            }
         }
         None => {
             span_bug!(obligation.cause.span,
@@ -1219,3 +1361,91 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
             .next()
     }
 }
+
+// # Cache
+
+pub struct ProjectionCache<'tcx> {
+    map: SnapshotMap<ty::ProjectionTy<'tcx>, ProjectionCacheEntry<'tcx>>,
+}
+
+#[derive(Clone, Debug)]
+enum ProjectionCacheEntry<'tcx> {
+    InProgress,
+    Ambiguous,
+    Error,
+    NormalizedTy(Ty<'tcx>),
+}
+
+// NB: intentionally not Clone
+pub struct ProjectionCacheSnapshot {
+    snapshot: Snapshot
+}
+
+impl<'tcx> ProjectionCache<'tcx> {
+    pub fn new() -> Self {
+        ProjectionCache {
+            map: SnapshotMap::new()
+        }
+    }
+
+    pub fn snapshot(&mut self) -> ProjectionCacheSnapshot {
+        ProjectionCacheSnapshot { snapshot: self.map.snapshot() }
+    }
+
+    pub fn rollback_to(&mut self, snapshot: ProjectionCacheSnapshot) {
+        self.map.rollback_to(snapshot.snapshot);
+    }
+
+    pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) {
+        self.map.commit(snapshot.snapshot);
+    }
+
+    /// Try to start normalize `key`; returns an error if
+    /// normalization already occured (this error corresponds to a
+    /// cache hit, so it's actually a good thing).
+    fn try_start(&mut self, key: ty::ProjectionTy<'tcx>)
+                 -> Result<(), ProjectionCacheEntry<'tcx>> {
+        match self.map.get(&key) {
+            Some(entry) => return Err(entry.clone()),
+            None => { }
+        }
+
+        self.map.insert(key, ProjectionCacheEntry::InProgress);
+        Ok(())
+    }
+
+    /// Indicates that `key` was normalized to `value`. If `cacheable` is false,
+    /// then this result is sadly not cacheable.
+    fn complete(&mut self,
+                key: ty::ProjectionTy<'tcx>,
+                value: &NormalizedTy<'tcx>,
+                cacheable: bool) {
+        let fresh_key = if cacheable {
+            debug!("ProjectionCacheEntry::complete: adding cache entry: key={:?}, value={:?}",
+                   key, value);
+            self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value.value))
+        } else {
+            debug!("ProjectionCacheEntry::complete: cannot cache: key={:?}, value={:?}",
+                   key, value);
+            !self.map.remove(key)
+        };
+
+        assert!(!fresh_key, "never started projecting `{:?}`", key);
+    }
+
+    /// Indicates that trying to normalize `key` resulted in
+    /// ambiguity. No point in trying it again then until we gain more
+    /// type information (in which case, the "fully resolved" key will
+    /// be different).
+    fn ambiguous(&mut self, key: ty::ProjectionTy<'tcx>) {
+        let fresh = self.map.insert(key, ProjectionCacheEntry::Ambiguous);
+        assert!(!fresh, "never started projecting `{:?}`", key);
+    }
+
+    /// Indicates that trying to normalize `key` resulted in
+    /// error.
+    fn error(&mut self, key: ty::ProjectionTy<'tcx>) {
+        let fresh = self.map.insert(key, ProjectionCacheEntry::Error);
+        assert!(!fresh, "never started projecting `{:?}`", key);
+    }
+}
index 5307749b87b6adf3515bbc2a7f360f46809f49a0..7a20b43b8f2e69a470c67810c95a1be71a48a819 100644 (file)
@@ -46,6 +46,7 @@ use rustc_data_structures::snapshot_vec::{SnapshotVecDelegate, SnapshotVec};
 use std::cell::RefCell;
 use std::fmt;
 use std::marker::PhantomData;
+use std::mem;
 use std::rc::Rc;
 use syntax::abi::Abi;
 use hir;
@@ -1237,6 +1238,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
                                                    skol_trait_predicate.trait_ref.clone(),
                                                    &skol_map,
                                                    snapshot);
+
+                self.infcx.pop_skolemized(skol_map, snapshot);
+
                 assert!(result);
                 true
             }
@@ -1263,7 +1267,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             Err(_) => { return false; }
         }
 
-        self.infcx.leak_check(false, skol_map, snapshot).is_ok()
+        self.infcx.leak_check(false, obligation.cause.span, skol_map, snapshot).is_ok()
     }
 
     /// Given an obligation like `<SomeTrait for T>`, search the obligations that the caller
@@ -1422,9 +1426,16 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             self.tcx(),
             obligation.predicate.0.trait_ref.self_ty(),
             |impl_def_id| {
-                self.probe(|this, snapshot| {
-                    if let Ok(_) = this.match_impl(impl_def_id, obligation, snapshot) {
-                        candidates.vec.push(ImplCandidate(impl_def_id));
+                self.probe(|this, snapshot| { /* [1] */
+                    match this.match_impl(impl_def_id, obligation, snapshot) {
+                        Ok(skol_map) => {
+                            candidates.vec.push(ImplCandidate(impl_def_id));
+
+                            // NB: we can safely drop the skol map
+                            // since we are in a probe [1]
+                            mem::drop(skol_map);
+                        }
+                        Err(_) => { }
                     }
                 });
             }
@@ -1509,9 +1520,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             return;
         }
 
-        self.probe(|this, snapshot| {
-            let (self_ty, _) =
-                this.infcx().skolemize_late_bound_regions(&obligation.self_ty(), snapshot);
+        self.probe(|this, _snapshot| {
+            // the code below doesn't care about regions, and the
+            // self-ty here doesn't escape this probe, so just erase
+            // any LBR.
+            let self_ty = this.tcx().erase_late_bound_regions(&obligation.self_ty());
             let poly_trait_ref = match self_ty.sty {
                 ty::TyTrait(ref data) => {
                     match this.tcx().lang_items.to_builtin_kind(obligation.predicate.def_id()) {
@@ -2710,7 +2723,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             })?;
         self.inferred_obligations.extend(obligations);
 
-        if let Err(e) = self.infcx.leak_check(false, &skol_map, snapshot) {
+        if let Err(e) = self.infcx.leak_check(false,
+                                              obligation.cause.span,
+                                              &skol_map,
+                                              snapshot) {
             debug!("match_impl: failed leak check due to `{}`", e);
             return Err(());
         }
index b2d14dab9a0b0463ce6f623e8bec3cb150e389f4..9348def1311eb01de1616d24878b3e00769a3c25 100644 (file)
@@ -27,7 +27,7 @@ use middle::region;
 use ty::subst::{Subst, Substs};
 use traits::{self, ProjectionMode, ObligationCause, Normalized};
 use ty::{self, TyCtxt};
-use syntax::codemap::DUMMY_SP;
+use syntax_pos::DUMMY_SP;
 
 pub mod specialization_graph;
 
@@ -187,51 +187,49 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
                                        source_trait_ref: ty::TraitRef<'tcx>,
                                        target_impl: DefId)
                                        -> Result<&'tcx Substs<'tcx>, ()> {
-    infcx.commit_if_ok(|_| {
-        let selcx = &mut SelectionContext::new(&infcx);
-        let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
-        let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
-                                                                       target_impl,
-                                                                       &target_substs);
-
-        // do the impls unify? If not, no specialization.
-        if let Err(_) = infcx.eq_trait_refs(true,
-                                            TypeOrigin::Misc(DUMMY_SP),
-                                            source_trait_ref,
-                                            target_trait_ref) {
-            debug!("fulfill_implication: {:?} does not unify with {:?}",
-                   source_trait_ref,
-                   target_trait_ref);
-            return Err(());
-        }
+    let selcx = &mut SelectionContext::new(&infcx);
+    let target_substs = fresh_type_vars_for_impl(&infcx, DUMMY_SP, target_impl);
+    let (target_trait_ref, obligations) = impl_trait_ref_and_oblig(selcx,
+                                                                   target_impl,
+                                                                   &target_substs);
+
+    // do the impls unify? If not, no specialization.
+    if let Err(_) = infcx.eq_trait_refs(true,
+                                        TypeOrigin::Misc(DUMMY_SP),
+                                        source_trait_ref,
+                                        target_trait_ref) {
+        debug!("fulfill_implication: {:?} does not unify with {:?}",
+               source_trait_ref,
+               target_trait_ref);
+        return Err(());
+    }
 
-        // attempt to prove all of the predicates for impl2 given those for impl1
-        // (which are packed up in penv)
+    // attempt to prove all of the predicates for impl2 given those for impl1
+    // (which are packed up in penv)
 
-        let mut fulfill_cx = FulfillmentContext::new();
-        for oblig in obligations.into_iter() {
-            fulfill_cx.register_predicate_obligation(&infcx, oblig);
-        }
+    let mut fulfill_cx = FulfillmentContext::new();
+    for oblig in obligations.into_iter() {
+        fulfill_cx.register_predicate_obligation(&infcx, oblig);
+    }
 
-        if let Err(errors) = infcx.drain_fulfillment_cx(&mut fulfill_cx, &()) {
-            // no dice!
-            debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
-                    {:?}",
-                   source_trait_ref,
-                   target_trait_ref,
-                   errors,
-                   infcx.parameter_environment.caller_bounds);
-            Err(())
-        } else {
-            debug!("fulfill_implication: an impl for {:?} specializes {:?}",
-                   source_trait_ref,
-                   target_trait_ref);
-
-            // Now resolve the *substitution* we built for the target earlier, replacing
-            // the inference variables inside with whatever we got from fulfillment.
-            Ok(infcx.resolve_type_vars_if_possible(&target_substs))
-        }
-    })
+    if let Err(errors) = infcx.drain_fulfillment_cx(&mut fulfill_cx, &()) {
+        // no dice!
+        debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \
+                {:?}",
+               source_trait_ref,
+               target_trait_ref,
+               errors,
+               infcx.parameter_environment.caller_bounds);
+        Err(())
+    } else {
+        debug!("fulfill_implication: an impl for {:?} specializes {:?}",
+               source_trait_ref,
+               target_trait_ref);
+
+        // Now resolve the *substitution* we built for the target earlier, replacing
+        // the inference variables inside with whatever we got from fulfillment.
+        Ok(infcx.resolve_type_vars_if_possible(&target_substs))
+    }
 }
 
 pub struct SpecializesCache {
index f8149565aa66b64e50263236845c8bf53d53497f..a097c0093b2e116ec470f8f8f270b74b6f67fdd9 100644 (file)
@@ -12,7 +12,7 @@ use hir::def_id::DefId;
 use infer::InferCtxt;
 use ty::subst::{Subst, Substs};
 use ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use util::common::ErrorReported;
 use util::nodemap::FnvHashSet;
 
index 71e49031347b8fcb4514070e1939e324e012be9a..47ca7d335ab822b0c8b1a1bd40f1083c278fba3a 100644 (file)
@@ -15,7 +15,7 @@ use ty::{self, Ty, TyCtxt, TypeAndMut, TypeFoldable};
 use ty::LvaluePreference::{NoPreference};
 
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use hir;
 
@@ -235,8 +235,9 @@ impl<'a, 'gcx, 'tcx> ty::TyS<'tcx> {
             None => {
                 span_bug!(
                     expr_span,
-                    "the {}th autoderef failed: {}",
+                    "the {}th autoderef for {} failed: {}",
                     autoderef,
+                    expr_id,
                     adjusted_ty);
             }
         }
index 33b33092b25c04c672558f9411904c891be416ec..14b0a8070983c84a3628b665e1d07648689768ea 100644 (file)
@@ -168,13 +168,12 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
             // which is incorrect.  This value was computed based on the crutch
             // value for the type contents of list.  The correct value is
             // TC::OwnsOwned.  This manifested as issue #4821.
-            match cache.get(&ty) {
-                Some(tc) => { return *tc; }
-                None => {}
+            if let Some(tc) = cache.get(&ty) {
+                return *tc;
             }
-            match tcx.tc_cache.borrow().get(&ty) {    // Must check both caches!
-                Some(tc) => { return *tc; }
-                None => {}
+            // Must check both caches!
+            if let Some(tc) = tcx.tc_cache.borrow().get(&ty) {
+                return *tc;
             }
             cache.insert(ty, TC::None);
 
index 45aa6f881e8e70652a1fc79dd716378ea263191a..219cb5e383a8d00ebc3b8dc270ca48bcc1e6a593 100644 (file)
@@ -665,7 +665,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     /// reference to the context, to allow formatting values that need it.
     pub fn create_and_enter<F, R>(s: &'tcx Session,
                                   arenas: &'tcx CtxtArenas<'tcx>,
-                                  def_map: RefCell<DefMap>,
+                                  def_map: DefMap,
                                   named_region_map: resolve_lifetime::NamedRegionMap,
                                   map: ast_map::Map<'tcx>,
                                   freevars: FreevarMap,
@@ -693,7 +693,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             item_variance_map: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             variance_computed: Cell::new(false),
             sess: s,
-            def_map: def_map,
+            def_map: RefCell::new(def_map),
             tables: RefCell::new(Tables::empty()),
             impl_trait_refs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
             trait_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
@@ -862,7 +862,7 @@ pub mod tls {
 
     use std::cell::Cell;
     use std::fmt;
-    use syntax::codemap;
+    use syntax_pos;
 
     /// Marker types used for the scoped TLS slot.
     /// The type context cannot be used directly because the scoped TLS
@@ -875,7 +875,7 @@ pub mod tls {
                                      *const ThreadLocalInterners)>> = Cell::new(None)
     }
 
-    fn span_debug(span: codemap::Span, f: &mut fmt::Formatter) -> fmt::Result {
+    fn span_debug(span: syntax_pos::Span, f: &mut fmt::Formatter) -> fmt::Result {
         with(|tcx| {
             write!(f, "{}", tcx.sess.codemap().span_to_string(span))
         })
@@ -884,7 +884,7 @@ pub mod tls {
     pub fn enter_global<'gcx, F, R>(gcx: GlobalCtxt<'gcx>, f: F) -> R
         where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'gcx>) -> R
     {
-        codemap::SPAN_DEBUG.with(|span_dbg| {
+        syntax_pos::SPAN_DEBUG.with(|span_dbg| {
             let original_span_debug = span_dbg.get();
             span_dbg.set(span_debug);
             let result = enter(&gcx, &gcx.global_interners, f);
index b5190f313309fe18a4a9f79358adfb38e6fe3486..bddc2dbdb7e779e63c6237837ecda20a86ce89cf 100644 (file)
@@ -16,8 +16,8 @@ use ty::{self, BoundRegion, Region, Ty, TyCtxt};
 use std::fmt;
 use syntax::abi;
 use syntax::ast::{self, Name};
-use syntax::codemap::Span;
-use syntax::errors::DiagnosticBuilder;
+use errors::DiagnosticBuilder;
+use syntax_pos::Span;
 
 use hir;
 
index a1da3017fcd043f9eff961ab05be01da47777f51..44b450784ed2a9c51e87cb1b6d891cbd7cecb234 100644 (file)
@@ -176,14 +176,18 @@ impl FlagComputation {
 
     fn add_region(&mut self, r: ty::Region) {
         match r {
-            ty::ReVar(..) |
+            ty::ReVar(..) => {
+                self.add_flags(TypeFlags::HAS_RE_INFER);
+                self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX);
+            }
             ty::ReSkolemized(..) => {
                 self.add_flags(TypeFlags::HAS_RE_INFER);
+                self.add_flags(TypeFlags::HAS_RE_SKOL);
                 self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX);
             }
             ty::ReLateBound(debruijn, _) => { self.add_depth(debruijn.depth); }
             ty::ReEarlyBound(..) => { self.add_flags(TypeFlags::HAS_RE_EARLY_BOUND); }
-            ty::ReStatic => {}
+            ty::ReStatic | ty::ReErased => {}
             _ => { self.add_flags(TypeFlags::HAS_FREE_REGIONS); }
         }
 
index 4a14185b6e3add0f47048a4a4dbfcf9100b3ef1f..b334964bf489b4e4fac8e9e533e4fc771b887b88 100644 (file)
@@ -99,6 +99,16 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
                             TypeFlags::HAS_RE_INFER |
                             TypeFlags::HAS_FREE_REGIONS)
     }
+    fn is_normalized_for_trans(&self) -> bool {
+        !self.has_type_flags(TypeFlags::HAS_RE_EARLY_BOUND |
+                             TypeFlags::HAS_RE_INFER |
+                             TypeFlags::HAS_FREE_REGIONS |
+                             TypeFlags::HAS_TY_INFER |
+                             TypeFlags::HAS_PARAMS |
+                             TypeFlags::HAS_PROJECTION |
+                             TypeFlags::HAS_TY_ERR |
+                             TypeFlags::HAS_SELF)
+    }
     /// Indicates whether this value references only 'global'
     /// types/lifetimes that are the same regardless of what fn we are
     /// in. This is used for caching. Errs on the side of returning
@@ -411,12 +421,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         collector.regions
     }
 
-    /// Replace any late-bound regions bound in `value` with `'static`. Useful in trans but also
+    /// Replace any late-bound regions bound in `value` with `'erased`. Useful in trans but also
     /// method lookup and a few other places where precise region relationships are not required.
     pub fn erase_late_bound_regions<T>(self, value: &Binder<T>) -> T
         where T : TypeFoldable<'tcx>
     {
-        self.replace_late_bound_regions(value, |_| ty::ReStatic).0
+        self.replace_late_bound_regions(value, |_| ty::ReErased).0
     }
 
     /// Rewrite any late-bound regions so that they are anonymous.  Region numbers are
@@ -511,9 +521,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.0 }
 
             fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-                match self.tcx().normalized_cache.borrow().get(&ty).cloned() {
-                    None => {}
-                    Some(u) => return u
+                if let Some(u) = self.tcx().normalized_cache.borrow().get(&ty).cloned() {
+                    return u;
                 }
 
                 // FIXME(eddyb) should local contexts have a cache too?
@@ -537,15 +546,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             fn fold_region(&mut self, r: ty::Region) -> ty::Region {
                 // because late-bound regions affect subtyping, we can't
                 // erase the bound/free distinction, but we can replace
-                // all free regions with 'static.
+                // all free regions with 'erased.
                 //
                 // Note that we *CAN* replace early-bound regions -- the
                 // type system never "sees" those, they get substituted
-                // away. In trans, they will always be erased to 'static
+                // away. In trans, they will always be erased to 'erased
                 // whenever a substitution occurs.
                 match r {
                     ty::ReLateBound(..) => r,
-                    _ => ty::ReStatic
+                    _ => ty::ReErased
                 }
             }
         }
@@ -641,7 +650,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
             // does this represent a region that cannot be named
             // in a global way? used in fulfillment caching.
             match r {
-                ty::ReStatic | ty::ReEmpty => {}
+                ty::ReStatic | ty::ReEmpty | ty::ReErased => {}
                 _ => return true,
             }
         }
@@ -704,4 +713,3 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector {
         false
     }
 }
-
index 5246c6739d9609ed9918f038eca0175dc3a29798..74c05feb6d16a5822464da1319f591f3b8d62b69 100644 (file)
@@ -13,13 +13,40 @@ use middle::cstore::LOCAL_CRATE;
 use hir::def_id::{DefId, CRATE_DEF_INDEX};
 use ty::{self, Ty, TyCtxt};
 use syntax::ast;
+use syntax::parse::token;
+
+use std::cell::Cell;
+
+thread_local! {
+    static FORCE_ABSOLUTE: Cell<bool> = Cell::new(false)
+}
+
+/// Enforces that item_path_str always returns an absolute path.
+/// This is useful when building symbols that contain types,
+/// where we want the crate name to be part of the symbol.
+pub fn with_forced_absolute_paths<F: FnOnce() -> R, R>(f: F) -> R {
+    FORCE_ABSOLUTE.with(|force| {
+        let old = force.get();
+        force.set(true);
+        let result = f();
+        force.set(old);
+        result
+    })
+}
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     /// Returns a string identifying this def-id. This string is
     /// suitable for user output. It is relative to the current crate
-    /// root.
+    /// root, unless with_forced_absolute_paths was used.
     pub fn item_path_str(self, def_id: DefId) -> String {
-        let mut buffer = LocalPathBuffer::new(RootMode::Local);
+        let mode = FORCE_ABSOLUTE.with(|force| {
+            if force.get() {
+                RootMode::Absolute
+            } else {
+                RootMode::Local
+            }
+        });
+        let mut buffer = LocalPathBuffer::new(mode);
         self.push_item_path(&mut buffer, def_id);
         buffer.into_string()
     }
@@ -75,7 +102,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             RootMode::Absolute => {
                 // In absolute mode, just write the crate name
                 // unconditionally.
-                buffer.push(&self.crate_name(cnum));
+                if cnum == LOCAL_CRATE {
+                    buffer.push(&self.crate_name(cnum));
+                } else {
+                    buffer.push(&self.sess.cstore.original_crate_name(cnum));
+                }
             }
         }
     }
@@ -108,7 +139,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
                 }
             }
 
-            cur_path.push(self.sess.cstore.item_name(cur_def));
+            cur_path.push(self.sess.cstore.opt_item_name(cur_def).unwrap_or_else(||
+                token::intern("<unnamed>")));
             match visible_parent_map.get(&cur_def) {
                 Some(&def) => cur_def = def,
                 None => return false,
index 82a3b0b8db28467c7d32097ec3ca66da4e0d64a2..61c8aa8fcebf368688950ccbc70503f57dc1d816 100644 (file)
@@ -17,9 +17,11 @@ use session::Session;
 use traits;
 use ty::{self, Ty, TyCtxt, TypeFoldable};
 
+use util::common::slice_pat;
+
 use syntax::ast::{FloatTy, IntTy, UintTy};
 use syntax::attr;
-use syntax::codemap::DUMMY_SP;
+use syntax_pos::DUMMY_SP;
 
 use std::cmp;
 use std::fmt;
@@ -98,17 +100,17 @@ impl TargetDataLayout {
 
         let mut dl = TargetDataLayout::default();
         for spec in sess.target.target.data_layout.split("-") {
-            match &spec.split(":").collect::<Vec<_>>()[..] {
-                ["e"] => dl.endian = Endian::Little,
-                ["E"] => dl.endian = Endian::Big,
-                ["a", a..] => dl.aggregate_align = align(a, "a"),
-                ["f32", a..] => dl.f32_align = align(a, "f32"),
-                ["f64", a..] => dl.f64_align = align(a, "f64"),
-                [p @ "p", s, a..] | [p @ "p0", s, a..] => {
+            match slice_pat(&&spec.split(":").collect::<Vec<_>>()[..]) {
+                &["e"] => dl.endian = Endian::Little,
+                &["E"] => dl.endian = Endian::Big,
+                &["a", ref a..] => dl.aggregate_align = align(a, "a"),
+                &["f32", ref a..] => dl.f32_align = align(a, "f32"),
+                &["f64", ref a..] => dl.f64_align = align(a, "f64"),
+                &[p @ "p", s, ref a..] | &[p @ "p0", s, ref a..] => {
                     dl.pointer_size = size(s, p);
                     dl.pointer_align = align(a, p);
                 }
-                [s, a..] if s.starts_with("i") => {
+                &[s, ref a..] if s.starts_with("i") => {
                     let ty_align = match s[1..].parse::<u64>() {
                         Ok(1) => &mut dl.i8_align,
                         Ok(8) => &mut dl.i8_align,
@@ -123,7 +125,7 @@ impl TargetDataLayout {
                     };
                     *ty_align = align(a, s);
                 }
-                [s, a..] if s.starts_with("v") => {
+                &[s, ref a..] if s.starts_with("v") => {
                     let v_size = size(&s[1..], "v");
                     let a = align(a, s);
                     if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
@@ -170,6 +172,7 @@ impl TargetDataLayout {
     /// address space on 64-bit ARMv8 and x86_64.
     pub fn obj_size_bound(&self) -> u64 {
         match self.pointer_size.bits() {
+            16 => 1 << 15,
             32 => 1 << 31,
             64 => 1 << 47,
             bits => bug!("obj_size_bound: unknown pointer bit size {}", bits)
@@ -178,6 +181,7 @@ impl TargetDataLayout {
 
     pub fn ptr_sized_integer(&self) -> Integer {
         match self.pointer_size.bits() {
+            16 => I16,
             32 => I32,
             64 => I64,
             bits => bug!("ptr_sized_integer: unknown pointer bit size {}", bits)
index dfb4ec739247d112a7545e76e7fb6553aeaefbe9..14db922d298101e82c941202deb7834743b9f4f4 100644 (file)
@@ -22,7 +22,7 @@ use dep_graph::{self, DepNode};
 use hir::map as ast_map;
 use middle;
 use middle::cstore::{self, LOCAL_CRATE};
-use hir::def::{self, Def, ExportMap};
+use hir::def::{Def, PathResolution, ExportMap};
 use hir::def_id::DefId;
 use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
 use middle::region::{CodeExtent, ROOT_CODE_EXTENT};
@@ -44,8 +44,8 @@ use std::slice;
 use std::vec::IntoIter;
 use syntax::ast::{self, CrateNum, Name, NodeId};
 use syntax::attr::{self, AttrMetaMethods};
-use syntax::codemap::{DUMMY_SP, Span};
 use syntax::parse::token::InternedString;
+use syntax_pos::{DUMMY_SP, Span};
 
 use rustc_const_math::ConstInt;
 
@@ -60,6 +60,7 @@ pub use self::sty::{ClosureTy, InferTy, ParamTy, ProjectionTy, TraitTy};
 pub use self::sty::{ClosureSubsts, TypeAndMut};
 pub use self::sty::{TraitRef, TypeVariants, PolyTraitRef};
 pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region};
+pub use self::sty::Issue32330;
 pub use self::sty::{TyVid, IntVid, FloatVid, RegionVid, SkolemizedRegionVid};
 pub use self::sty::BoundRegion::*;
 pub use self::sty::FnOutput::*;
@@ -307,13 +308,11 @@ impl Visibility {
         match *visibility {
             hir::Public => Visibility::Public,
             hir::Visibility::Crate => Visibility::Restricted(ast::CRATE_NODE_ID),
-            hir::Visibility::Restricted { id, .. } => match tcx.def_map.borrow().get(&id) {
-                Some(resolution) => Visibility::Restricted({
-                    tcx.map.as_local_node_id(resolution.base_def.def_id()).unwrap()
-                }),
+            hir::Visibility::Restricted { id, .. } => match tcx.expect_def(id) {
                 // If there is no resolution, `resolve` will have already reported an error, so
                 // assume that the visibility is public to avoid reporting more privacy errors.
-                None => Visibility::Public,
+                Def::Err => Visibility::Public,
+                def => Visibility::Restricted(tcx.map.as_local_node_id(def.def_id()).unwrap()),
             },
             hir::Inherited => Visibility::Restricted(tcx.map.get_module_parent(id)),
         }
@@ -514,19 +513,20 @@ bitflags! {
         const HAS_SELF           = 1 << 1,
         const HAS_TY_INFER       = 1 << 2,
         const HAS_RE_INFER       = 1 << 3,
-        const HAS_RE_EARLY_BOUND = 1 << 4,
-        const HAS_FREE_REGIONS   = 1 << 5,
-        const HAS_TY_ERR         = 1 << 6,
-        const HAS_PROJECTION     = 1 << 7,
-        const HAS_TY_CLOSURE     = 1 << 8,
+        const HAS_RE_SKOL        = 1 << 4,
+        const HAS_RE_EARLY_BOUND = 1 << 5,
+        const HAS_FREE_REGIONS   = 1 << 6,
+        const HAS_TY_ERR         = 1 << 7,
+        const HAS_PROJECTION     = 1 << 8,
+        const HAS_TY_CLOSURE     = 1 << 9,
 
         // true if there are "names" of types and regions and so forth
         // that are local to a particular fn
-        const HAS_LOCAL_NAMES   = 1 << 9,
+        const HAS_LOCAL_NAMES    = 1 << 10,
 
         // Present if the type belongs in a local type context.
         // Only set for TyInfer other than Fresh.
-        const KEEP_IN_LOCAL_TCX = 1 << 10,
+        const KEEP_IN_LOCAL_TCX  = 1 << 11,
 
         const NEEDS_SUBST        = TypeFlags::HAS_PARAMS.bits |
                                    TypeFlags::HAS_SELF.bits |
@@ -739,7 +739,8 @@ impl RegionParameterDef {
         })
     }
     pub fn to_bound_region(&self) -> ty::BoundRegion {
-        ty::BoundRegion::BrNamed(self.def_id, self.name)
+        // this is an early bound region, so unaffected by #32330
+        ty::BoundRegion::BrNamed(self.def_id, self.name, Issue32330::WontChange)
     }
 }
 
@@ -946,7 +947,28 @@ impl<'tcx> TraitPredicate<'tcx> {
 
     /// Creates the dep-node for selecting/evaluating this trait reference.
     fn dep_node(&self) -> DepNode<DefId> {
-        DepNode::TraitSelect(self.def_id())
+        // Ideally, the dep-node would just have all the input types
+        // in it.  But they are limited to including def-ids. So as an
+        // approximation we include the def-ids for all nominal types
+        // found somewhere. This means that we will e.g. conflate the
+        // dep-nodes for `u32: SomeTrait` and `u64: SomeTrait`, but we
+        // would have distinct dep-nodes for `Vec<u32>: SomeTrait`,
+        // `Rc<u32>: SomeTrait`, and `(Vec<u32>, Rc<u32>): SomeTrait`.
+        // Note that it's always sound to conflate dep-nodes, it just
+        // leads to more recompilation.
+        let def_ids: Vec<_> =
+            self.input_types()
+                .iter()
+                .flat_map(|t| t.walk())
+                .filter_map(|t| match t.sty {
+                    ty::TyStruct(adt_def, _) |
+                    ty::TyEnum(adt_def, _) =>
+                        Some(adt_def.did),
+                    _ =>
+                        None
+                })
+                .collect();
+        DepNode::TraitSelect(self.def_id(), def_ids)
     }
 
     pub fn input_types(&self) -> &[Ty<'tcx>] {
@@ -992,7 +1014,7 @@ pub type PolyTypeOutlivesPredicate<'tcx> = PolyOutlivesPredicate<Ty<'tcx>, ty::R
 /// equality between arbitrary types. Processing an instance of Form
 /// #2 eventually yields one of these `ProjectionPredicate`
 /// instances to normalize the LHS.
-#[derive(Clone, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 pub struct ProjectionPredicate<'tcx> {
     pub projection_ty: ProjectionTy<'tcx>,
     pub ty: Ty<'tcx>,
@@ -1768,9 +1790,8 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
                                         stack: &mut Vec<AdtDefMaster<'tcx>>)
     {
 
-        let dep_node = DepNode::SizedConstraint(self.did);
-
-        if self.sized_constraint.get(dep_node).is_some() {
+        let dep_node = || DepNode::SizedConstraint(self.did);
+        if self.sized_constraint.get(dep_node()).is_some() {
             return;
         }
 
@@ -1780,7 +1801,7 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
             //
             // Consider the type as Sized in the meanwhile to avoid
             // further errors.
-            self.sized_constraint.fulfill(dep_node, tcx.types.err);
+            self.sized_constraint.fulfill(dep_node(), tcx.types.err);
             return;
         }
 
@@ -1803,14 +1824,14 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
             _ => tcx.mk_tup(tys)
         };
 
-        match self.sized_constraint.get(dep_node) {
+        match self.sized_constraint.get(dep_node()) {
             Some(old_ty) => {
                 debug!("calculate_sized_constraint: {:?} recurred", self);
                 assert_eq!(old_ty, tcx.types.err)
             }
             None => {
                 debug!("calculate_sized_constraint: {:?} => {:?}", self, ty);
-                self.sized_constraint.fulfill(dep_node, ty)
+                self.sized_constraint.fulfill(dep_node(), ty)
             }
         }
     }
@@ -2216,7 +2237,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         match self.map.find(id) {
             Some(ast_map::NodeLocal(pat)) => {
                 match pat.node {
-                    PatKind::Ident(_, ref path1, _) => path1.node.as_str(),
+                    PatKind::Binding(_, ref path1, _) => path1.node.as_str(),
                     _ => {
                         bug!("Variable id {} maps to {:?}, not local", id, pat);
                     },
@@ -2226,34 +2247,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    pub fn resolve_expr(self, expr: &hir::Expr) -> Def {
-        match self.def_map.borrow().get(&expr.id) {
-            Some(def) => def.full_def(),
-            None => {
-                span_bug!(expr.span, "no def-map entry for expr {}", expr.id);
-            }
-        }
-    }
-
     pub fn expr_is_lval(self, expr: &hir::Expr) -> bool {
          match expr.node {
             hir::ExprPath(..) => {
-                // We can't use resolve_expr here, as this needs to run on broken
-                // programs. We don't need to through - associated items are all
-                // rvalues.
-                match self.def_map.borrow().get(&expr.id) {
-                    Some(&def::PathResolution {
-                        base_def: Def::Static(..), ..
-                    }) | Some(&def::PathResolution {
-                        base_def: Def::Upvar(..), ..
-                    }) | Some(&def::PathResolution {
-                        base_def: Def::Local(..), ..
-                    }) => {
-                        true
-                    }
-                    Some(&def::PathResolution { base_def: Def::Err, .. })=> true,
-                    Some(..) => false,
-                    None => span_bug!(expr.span, "no def for path {}", expr.id)
+                // This function can be used during type checking when not all paths are
+                // fully resolved. Partially resolved paths in expressions can only legally
+                // refer to associated items which are always rvalues.
+                match self.expect_resolution(expr.id).base_def {
+                    Def::Local(..) | Def::Upvar(..) | Def::Static(..) | Def::Err => true,
+                    _ => false,
                 }
             }
 
@@ -2436,8 +2438,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    pub fn trait_ref_to_def_id(self, tr: &hir::TraitRef) -> DefId {
-        self.def_map.borrow().get(&tr.ref_id).expect("no def-map entry for trait").def_id()
+    /// Returns a path resolution for node id if it exists, panics otherwise.
+    pub fn expect_resolution(self, id: NodeId) -> PathResolution {
+        *self.def_map.borrow().get(&id).expect("no def-map entry for node id")
+    }
+
+    /// Returns a fully resolved definition for node id if it exists, panics otherwise.
+    pub fn expect_def(self, id: NodeId) -> Def {
+        self.expect_resolution(id).full_def()
+    }
+
+    /// Returns a fully resolved definition for node id if it exists, or none if no
+    /// definition exists, panics on partial resolutions to catch errors.
+    pub fn expect_def_or_none(self, id: NodeId) -> Option<Def> {
+        self.def_map.borrow().get(&id).map(|resolution| resolution.full_def())
     }
 
     pub fn def_key(self, id: DefId) -> ast_map::DefKey {
@@ -2480,6 +2494,18 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
             || self.sess.cstore.item_type(self.global_tcx(), did))
     }
 
+    pub fn opt_lookup_item_type(self, did: DefId) -> Option<TypeScheme<'gcx>> {
+        if let Some(scheme) = self.tcache.borrow_mut().get(&did) {
+            return Some(scheme.clone());
+        }
+
+        if did.krate == LOCAL_CRATE {
+            None
+        } else {
+            Some(self.sess.cstore.item_type(self.global_tcx(), did))
+        }
+    }
+
     /// Given the did of a trait, returns its canonical trait ref.
     pub fn lookup_trait_def(self, did: DefId) -> &'gcx TraitDef<'gcx> {
         lookup_locally_or_in_crate_store(
@@ -2835,7 +2861,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         for def in generics.regions.as_slice() {
             let region =
                 ReFree(FreeRegion { scope: free_id_outlive,
-                                    bound_region: BrNamed(def.def_id, def.name) });
+                                    bound_region: def.to_bound_region() });
             debug!("push_region_params {:?}", region);
             regions.push(def.space, region);
         }
index 77e980ff3196520c05600cf42bcf5245c56c7e6e..1e2920ca87ea6d712b3a2f64839160f1fcc7a542 100644 (file)
@@ -827,7 +827,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef {
             def_id: self.def_id,
             space: self.space,
             index: self.index,
-            bounds: self.bounds.fold_with(folder)
+            bounds: self.bounds.fold_with(folder),
         }
     }
 
index 32434d40e61a9d4ece268d69dcc99162bb41f16b..7c69618068a6b7c89597d3ae6cf23270d622e693 100644 (file)
@@ -58,7 +58,7 @@ pub enum BoundRegion {
     ///
     /// The def-id is needed to distinguish free regions in
     /// the event of shadowing.
-    BrNamed(DefId, Name),
+    BrNamed(DefId, Name, Issue32330),
 
     /// Fresh bound identifiers created during GLB computations.
     BrFresh(u32),
@@ -68,6 +68,25 @@ pub enum BoundRegion {
     BrEnv
 }
 
+/// True if this late-bound region is unconstrained, and hence will
+/// become early-bound once #32330 is fixed.
+#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash,
+         RustcEncodable, RustcDecodable)]
+pub enum Issue32330 {
+    WontChange,
+
+    /// this region will change from late-bound to early-bound once
+    /// #32330 is fixed.
+    WillChange {
+        /// fn where is region declared
+        fn_def_id: DefId,
+
+        /// name of region; duplicates the info in BrNamed but convenient
+        /// to have it here, and this code is only temporary
+        region_name: ast::Name,
+    }
+}
+
 // NB: If you change this, you'll probably want to change the corresponding
 // AST structure in libsyntax/ast.rs as well.
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
@@ -473,6 +492,13 @@ impl<'tcx> FnOutput<'tcx> {
             ty::FnDiverging => def
         }
     }
+
+    pub fn maybe_converging(self) -> Option<Ty<'tcx>> {
+        match self {
+            ty::FnConverging(t) => Some(t),
+            ty::FnDiverging => None
+        }
+    }
 }
 
 pub type PolyFnOutput<'tcx> = Binder<FnOutput<'tcx>>;
@@ -686,6 +712,9 @@ pub enum Region {
     /// The only way to get an instance of ReEmpty is to have a region
     /// variable with no constraints.
     ReEmpty,
+
+    /// Erased region, used by trait selection, in MIR and during trans.
+    ReErased,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
@@ -697,7 +726,7 @@ pub struct EarlyBoundRegion {
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct TyVid {
-    pub index: u32
+    pub index: u32,
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
index 2db9ceb8a05c8ddc8517fd7364f1c16b76682759..595d965ffce262eede9050e22016b4c24b4ecb7e 100644 (file)
@@ -22,7 +22,7 @@ use std::fmt;
 use std::iter::IntoIterator;
 use std::slice::Iter;
 use std::vec::{Vec, IntoIter};
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
 
 ///////////////////////////////////////////////////////////////////////////
 
@@ -89,7 +89,7 @@ impl<'a, 'gcx, 'tcx> Substs<'tcx> {
 
     pub fn erase_regions(self) -> Substs<'tcx> {
         let Substs { types, regions } = self;
-        let regions = regions.map(|_| ty::ReStatic);
+        let regions = regions.map(|_| ty::ReErased);
         Substs { types: types, regions: regions }
     }
 
index 4f6188ea3c51f5936006f79a9ae689e3f542047d..21c14e6fe4c3b16c2d39eac6769274bcb445c47b 100644 (file)
@@ -27,7 +27,7 @@ use std::cmp;
 use std::hash::{Hash, SipHasher, Hasher};
 use syntax::ast::{self, Name};
 use syntax::attr::{self, SignedInt, UnsignedInt};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use hir;
 
@@ -62,6 +62,7 @@ impl IntTypeExt for attr::IntType {
             SignedInt(ast::IntTy::I32)   => ConstInt::I32(0),
             SignedInt(ast::IntTy::I64)   => ConstInt::I64(0),
             SignedInt(ast::IntTy::Is) => match tcx.sess.target.int_type {
+                ast::IntTy::I16 => ConstInt::Isize(ConstIsize::Is16(0)),
                 ast::IntTy::I32 => ConstInt::Isize(ConstIsize::Is32(0)),
                 ast::IntTy::I64 => ConstInt::Isize(ConstIsize::Is64(0)),
                 _ => bug!(),
@@ -71,6 +72,7 @@ impl IntTypeExt for attr::IntType {
             UnsignedInt(ast::UintTy::U32) => ConstInt::U32(0),
             UnsignedInt(ast::UintTy::U64) => ConstInt::U64(0),
             UnsignedInt(ast::UintTy::Us) => match tcx.sess.target.uint_type {
+                ast::UintTy::U16 => ConstInt::Usize(ConstUsize::Us16(0)),
                 ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32(0)),
                 ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64(0)),
                 _ => bug!(),
@@ -173,11 +175,11 @@ impl<'tcx> ParameterEnvironment<'tcx> {
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn pat_contains_ref_binding(self, pat: &hir::Pat) -> Option<hir::Mutability> {
-        pat_util::pat_contains_ref_binding(&self.def_map, pat)
+        pat_util::pat_contains_ref_binding(pat)
     }
 
     pub fn arm_contains_ref_binding(self, arm: &hir::Arm) -> Option<hir::Mutability> {
-        pat_util::arm_contains_ref_binding(&self.def_map, arm)
+        pat_util::arm_contains_ref_binding(arm)
     }
 
     /// Returns the type of element at index `i` in tuple or tuple-like type `t`.
@@ -346,7 +348,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 
             let region = |state: &mut SipHasher, r: ty::Region| {
                 match r {
-                    ty::ReStatic => {}
+                    ty::ReStatic | ty::ReErased => {}
                     ty::ReLateBound(db, ty::BrAnon(i)) => {
                         db.hash(state);
                         i.hash(state);
@@ -710,16 +712,13 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
                         // struct Foo;
                         // struct Bar<T> { x: Bar<Foo> }
 
-                        match iter.next() {
-                            Some(&seen_type) => {
-                                if same_struct_or_enum(seen_type, def) {
-                                    debug!("SelfRecursive: {:?} contains {:?}",
-                                           seen_type,
-                                           ty);
-                                    return Representability::SelfRecursive;
-                                }
+                        if let Some(&seen_type) = iter.next() {
+                            if same_struct_or_enum(seen_type, def) {
+                                debug!("SelfRecursive: {:?} contains {:?}",
+                                       seen_type,
+                                       ty);
+                                return Representability::SelfRecursive;
                             }
-                            None => {}
                         }
 
                         // We also need to know whether the first item contains other types
index a25994ea69981f23dc7f9eb1cc0bd764c48b2c2b..37ba936d2f42a73f71a6e3193cdfbf183345d6e5 100644 (file)
@@ -16,7 +16,7 @@ use traits;
 use ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use std::iter::once;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use util::common::ErrorReported;
 
 /// Returns the set of obligations needed to make `ty` well-formed.
index bdfb97549d5d512647ce80179eb8d38579cd1378..57c429152c89935d72c8add0e7d2b551afd760d0 100644 (file)
@@ -247,3 +247,15 @@ pub fn path2cstr(p: &Path) -> CString {
 pub fn path2cstr(p: &Path) -> CString {
     CString::new(p.to_str().unwrap()).unwrap()
 }
+
+// FIXME(stage0): remove this
+// HACK: this is needed because the interpretation of slice
+// patterns changed between stage0 and now.
+#[cfg(stage0)]
+pub fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'a &'b [T] {
+    t
+}
+#[cfg(not(stage0))]
+pub fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'b [T] {
+    *t
+}
index 1a802064b61270d253ae8c9a669e1ba856deb2cf..0bfb7c1ed55321da22e6c9fe412b2dedcbc04686 100644 (file)
@@ -69,15 +69,12 @@ pub enum Ns {
     Value
 }
 
-fn number_of_supplied_defaults<'a, 'gcx, 'tcx, GG>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                                                   substs: &subst::Substs,
-                                                   space: subst::ParamSpace,
-                                                   get_generics: GG)
-                                                   -> usize
-    where GG: FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> ty::Generics<'tcx>
+fn number_of_supplied_defaults<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                               substs: &subst::Substs,
+                                               space: subst::ParamSpace,
+                                               generics: ty::Generics<'tcx>)
+                                               -> usize
 {
-    let generics = get_generics(tcx);
-
     let has_self = substs.self_ty().is_some();
     let ty_params = generics.types.get_slice(space);
     let tps = substs.types.get_slice(space);
@@ -115,7 +112,8 @@ pub fn parameterized<GG>(f: &mut fmt::Formatter,
                          projections: &[ty::ProjectionPredicate],
                          get_generics: GG)
                          -> fmt::Result
-    where GG: for<'a, 'gcx, 'tcx> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> ty::Generics<'tcx>
+    where GG: for<'a, 'gcx, 'tcx> FnOnce(TyCtxt<'a, 'gcx, 'tcx>)
+                                         -> Option<ty::Generics<'tcx>>
 {
     if let (Ns::Value, Some(self_ty)) = (ns, substs.self_ty()) {
         write!(f, "<{} as ", self_ty)?;
@@ -150,39 +148,46 @@ pub fn parameterized<GG>(f: &mut fmt::Formatter,
             write!(f, "{}", cont)
         }
     };
-    let print_region = |f: &mut fmt::Formatter, region: &ty::Region| -> _ {
-        if verbose {
-            write!(f, "{:?}", region)
-        } else {
-            let s = region.to_string();
-            if s.is_empty() {
-                // This happens when the value of the region
-                // parameter is not easily serialized. This may be
-                // because the user omitted it in the first place,
-                // or because it refers to some block in the code,
-                // etc. I'm not sure how best to serialize this.
-                write!(f, "'_")
+
+    let print_regions = |f: &mut fmt::Formatter, start: &str, regions: &[ty::Region]| {
+        // Don't print any regions if they're all erased.
+        if regions.iter().all(|r| *r == ty::ReErased) {
+            return Ok(());
+        }
+
+        for region in regions {
+            start_or_continue(f, start, ", ")?;
+            if verbose {
+                write!(f, "{:?}", region)?;
             } else {
-                write!(f, "{}", s)
+                let s = region.to_string();
+                if s.is_empty() {
+                    // This happens when the value of the region
+                    // parameter is not easily serialized. This may be
+                    // because the user omitted it in the first place,
+                    // or because it refers to some block in the code,
+                    // etc. I'm not sure how best to serialize this.
+                    write!(f, "'_")?;
+                } else {
+                    write!(f, "{}", s)?;
+                }
             }
         }
+
+        Ok(())
     };
 
-    for region in substs.regions.get_slice(subst::TypeSpace) {
-        start_or_continue(f, "<", ", ")?;
-        print_region(f, region)?;
-    }
+    print_regions(f, "<", substs.regions.get_slice(subst::TypeSpace))?;
 
     let num_supplied_defaults = if verbose {
         0
     } else {
-        // It is important to execute this conditionally, only if -Z
-        // verbose is false. Otherwise, debug logs can sometimes cause
-        // ICEs trying to fetch the generics early in the pipeline. This
-        // is kind of a hacky workaround in that -Z verbose is required to
-        // avoid those ICEs.
         ty::tls::with(|tcx| {
-            number_of_supplied_defaults(tcx, substs, subst::TypeSpace, get_generics)
+            if let Some(generics) = get_generics(tcx) {
+                number_of_supplied_defaults(tcx, substs, subst::TypeSpace, generics)
+            } else {
+                0
+            }
         })
     };
 
@@ -214,10 +219,7 @@ pub fn parameterized<GG>(f: &mut fmt::Formatter,
             write!(f, "::{}", item_name)?;
         }
 
-        for region in substs.regions.get_slice(subst::FnSpace) {
-            start_or_continue(f, "::<", ", ")?;
-            print_region(f, region)?;
-        }
+        print_regions(f, "::<", substs.regions.get_slice(subst::FnSpace))?;
 
         // FIXME: consider being smart with defaults here too
         for ty in substs.types.get_slice(subst::FnSpace) {
@@ -261,7 +263,7 @@ fn in_binder<'a, 'gcx, 'tcx, T, U>(f: &mut fmt::Formatter,
     let new_value = tcx.replace_late_bound_regions(&value, |br| {
         let _ = start_or_continue(f, "for<", ", ");
         ty::ReLateBound(ty::DebruijnIndex::new(1), match br {
-            ty::BrNamed(_, name) => {
+            ty::BrNamed(_, name, _) => {
                 let _ = write!(f, "{}", name);
                 br
             }
@@ -270,7 +272,9 @@ fn in_binder<'a, 'gcx, 'tcx, T, U>(f: &mut fmt::Formatter,
             ty::BrEnv => {
                 let name = token::intern("'r");
                 let _ = write!(f, "{}", name);
-                ty::BrNamed(tcx.map.local_def_id(CRATE_NODE_ID), name)
+                ty::BrNamed(tcx.map.local_def_id(CRATE_NODE_ID),
+                            name,
+                            ty::Issue32330::WontChange)
             }
         })
     }).0;
@@ -310,7 +314,7 @@ impl<'tcx> fmt::Display for TraitAndProjections<'tcx> {
                       trait_ref.def_id,
                       Ns::Type,
                       projection_bounds,
-                      |tcx| tcx.lookup_trait_def(trait_ref.def_id).generics.clone())
+                      |tcx| Some(tcx.lookup_trait_def(trait_ref.def_id).generics.clone()))
     }
 }
 
@@ -485,7 +489,7 @@ impl fmt::Display for ty::BoundRegion {
         }
 
         match *self {
-            BrNamed(_, name) => write!(f, "{}", name),
+            BrNamed(_, name, _) => write!(f, "{}", name),
             BrAnon(_) | BrFresh(_) | BrEnv => Ok(())
         }
     }
@@ -496,8 +500,9 @@ impl fmt::Debug for ty::BoundRegion {
         match *self {
             BrAnon(n) => write!(f, "BrAnon({:?})", n),
             BrFresh(n) => write!(f, "BrFresh({:?})", n),
-            BrNamed(did, name) => {
-                write!(f, "BrNamed({:?}:{:?}, {:?})", did.krate, did.index, name)
+            BrNamed(did, name, issue32330) => {
+                write!(f, "BrNamed({:?}:{:?}, {:?}, {:?})",
+                       did.krate, did.index, name, issue32330)
             }
             BrEnv => "BrEnv".fmt(f),
         }
@@ -536,7 +541,9 @@ impl fmt::Debug for ty::Region {
                 write!(f, "ReSkolemized({}, {:?})", id.index, bound_region)
             }
 
-            ty::ReEmpty => write!(f, "ReEmpty")
+            ty::ReEmpty => write!(f, "ReEmpty"),
+
+            ty::ReErased => write!(f, "ReErased")
         }
     }
 }
@@ -600,7 +607,8 @@ impl fmt::Display for ty::Region {
                 write!(f, "{}", br)
             }
             ty::ReScope(_) |
-            ty::ReVar(_) => Ok(()),
+            ty::ReVar(_) |
+            ty::ReErased => Ok(()),
             ty::ReStatic => write!(f, "'static"),
             ty::ReEmpty => write!(f, "'<empty>"),
         }
@@ -811,7 +819,7 @@ impl fmt::Display for ty::Binder<ty::OutlivesPredicate<ty::Region, ty::Region>>
 impl<'tcx> fmt::Display for ty::TraitRef<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         parameterized(f, self.substs, self.def_id, Ns::Type, &[],
-                      |tcx| tcx.lookup_trait_def(self.def_id).generics.clone())
+                      |tcx| Some(tcx.lookup_trait_def(self.def_id).generics.clone()))
     }
 }
 
@@ -863,8 +871,9 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
                 }
 
                 write!(f, "{} {{", bare_fn.sig.0)?;
-                parameterized(f, substs, def_id, Ns::Value, &[],
-                              |tcx| tcx.lookup_item_type(def_id).generics)?;
+                parameterized(
+                    f, substs, def_id, Ns::Value, &[],
+                    |tcx| tcx.opt_lookup_item_type(def_id).map(|t| t.generics))?;
                 write!(f, "}}")
             }
             TyFnPtr(ref bare_fn) => {
@@ -887,8 +896,12 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
                           !tcx.tcache.borrow().contains_key(&def.did) {
                         write!(f, "{}<..>", tcx.item_path_str(def.did))
                     } else {
-                        parameterized(f, substs, def.did, Ns::Type, &[],
-                                      |tcx| tcx.lookup_item_type(def.did).generics)
+                        parameterized(
+                            f, substs, def.did, Ns::Type, &[],
+                            |tcx| {
+                                tcx.opt_lookup_item_type(def.did).
+                                    map(|t| t.generics)
+                            })
                     }
                 })
             }
index e2025eaa8ee06d6537c264631f4b59c213066a2e..afc2e04d446a1140b4724f3192cfb6f0e21dbde5 100644 (file)
@@ -291,8 +291,8 @@ macro_rules! bitflags {
 #[cfg(test)]
 #[allow(non_upper_case_globals)]
 mod tests {
-    use std::hash::{Hasher, Hash, SipHasher};
-    use std::option::Option::{Some, None};
+    use std::hash::{Hash, Hasher, SipHasher};
+    use std::option::Option::{None, Some};
 
     bitflags! {
         #[doc = "> The first principle is that you must not fool yourself — and"]
index fbc267aaa6a06c54f2e5dd9469eac49996bc4215..d53318f1768480674f384b513b06e281fda7ff82 100644 (file)
@@ -12,6 +12,9 @@ test = false
 [dependencies]
 log = { path = "../liblog" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
 graphviz = { path = "../libgraphviz" }
 rustc = { path = "../librustc" }
+rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_mir = { path = "../librustc_mir" }
+rustc_errors = { path = "../librustc_errors" }
index a4aa7ae15744dcb0778eb7711e4470737d775098..80fa86a007ed3381180e582ae14bfc9407c9884e 100644 (file)
@@ -8,9 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// FIXME: move this to `rustc_data_structures` and potentially merge
+// with `bitvec` there.
+
 use std::mem;
 
-/// `BitSlice` provides helper methods for treating a `[usize]`
+pub type Word = usize;
+
+/// `BitSlice` provides helper methods for treating a `[Word]`
 /// as a bitvector.
 pub trait BitSlice {
     fn clear_bit(&mut self, idx: usize) -> bool;
@@ -18,12 +23,12 @@ pub trait BitSlice {
     fn get_bit(&self, idx: usize) -> bool;
 }
 
-impl BitSlice for [usize] {
+impl BitSlice for [Word] {
     /// Clears bit at `idx` to 0; returns true iff this changed `self.`
     fn clear_bit(&mut self, idx: usize) -> bool {
         let words = self;
         debug!("clear_bit: words={} idx={}",
-               bits_to_string(words, words.len() * mem::size_of::<usize>()), bit_str(idx));
+               bits_to_string(words, words.len() * mem::size_of::<Word>()), bit_str(idx));
         let BitLookup { word, bit_in_word, bit_mask } = bit_lookup(idx);
         debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, bit_mask);
         let oldv = words[word];
@@ -36,7 +41,7 @@ impl BitSlice for [usize] {
     fn set_bit(&mut self, idx: usize) -> bool {
         let words = self;
         debug!("set_bit: words={} idx={}",
-               bits_to_string(words, words.len() * mem::size_of::<usize>()), bit_str(idx));
+               bits_to_string(words, words.len() * mem::size_of::<Word>()), bit_str(idx));
         let BitLookup { word, bit_in_word, bit_mask } = bit_lookup(idx);
         debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, bit_mask);
         let oldv = words[word];
@@ -54,52 +59,85 @@ impl BitSlice for [usize] {
 }
 
 struct BitLookup {
-    /// An index of the word holding the bit in original `[usize]` of query.
+    /// An index of the word holding the bit in original `[Word]` of query.
     word: usize,
     /// Index of the particular bit within the word holding the bit.
     bit_in_word: usize,
     /// Word with single 1-bit set corresponding to where the bit is located.
-    bit_mask: usize,
+    bit_mask: Word,
 }
 
 #[inline]
 fn bit_lookup(bit: usize) -> BitLookup {
-    let usize_bits = mem::size_of::<usize>() * 8;
-    let word = bit / usize_bits;
-    let bit_in_word = bit % usize_bits;
+    let word_bits = mem::size_of::<Word>() * 8;
+    let word = bit / word_bits;
+    let bit_in_word = bit % word_bits;
     let bit_mask = 1 << bit_in_word;
     BitLookup { word: word, bit_in_word: bit_in_word, bit_mask: bit_mask }
 }
 
 
-fn bit_str(bit: usize) -> String {
-    let byte = bit >> 8;
-    let lobits = 1 << (bit & 0xFF);
+fn bit_str(bit: Word) -> String {
+    let byte = bit >> 3;
+    let lobits = 1 << (bit & 0b111);
     format!("[{}:{}-{:02x}]", bit, byte, lobits)
 }
 
-pub fn bits_to_string(words: &[usize], bytes: usize) -> String {
+pub fn bits_to_string(words: &[Word], bits: usize) -> String {
     let mut result = String::new();
     let mut sep = '[';
 
     // Note: this is a little endian printout of bytes.
 
+    // i tracks how many bits we have printed so far.
     let mut i = 0;
     for &word in words.iter() {
         let mut v = word;
-        for _ in 0..mem::size_of::<usize>() {
-            let byte = v & 0xFF;
-            if i >= bytes {
-                assert!(byte == 0);
-            } else {
-                result.push(sep);
-                result.push_str(&format!("{:02x}", byte));
-            }
+        loop { // for each byte in `v`:
+            let remain = bits - i;
+            // If less than a byte remains, then mask just that many bits.
+            let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF };
+            assert!(mask <= 0xFF);
+            let byte = v & mask;
+
+            result.push(sep);
+            result.push_str(&format!("{:02x}", byte));
+
+            if remain <= 8 { break; }
             v >>= 8;
-            i += 1;
+            i += 8;
             sep = '-';
         }
     }
     result.push(']');
     return result
 }
+
+#[inline]
+pub fn bitwise<Op:BitwiseOperator>(out_vec: &mut [usize],
+                                   in_vec: &[usize],
+                                   op: &Op) -> bool {
+    assert_eq!(out_vec.len(), in_vec.len());
+    let mut changed = false;
+    for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) {
+        let old_val = *out_elt;
+        let new_val = op.join(old_val, *in_elt);
+        *out_elt = new_val;
+        changed |= old_val != new_val;
+    }
+    changed
+}
+
+pub trait BitwiseOperator {
+    /// Applies some bit-operation pointwise to each of the bits in the two inputs.
+    fn join(&self, pred1: usize, pred2: usize) -> usize;
+}
+
+pub struct Union;
+impl BitwiseOperator for Union {
+    fn join(&self, a: usize, b: usize) -> usize { a | b }
+}
+pub struct Subtract;
+impl BitwiseOperator for Subtract {
+    fn join(&self, a: usize, b: usize) -> usize { a & !b }
+}
index 36f95f62d0606ef49480e73a9827029e087786b7..9cae270984f0047cce5f873e6004b5c47fe6722b 100644 (file)
@@ -27,7 +27,7 @@ use rustc::middle::mem_categorization::Categorization;
 use rustc::middle::region;
 use rustc::ty::{self, TyCtxt};
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::hir;
 
 use std::rc::Rc;
index 6ab85d7d449dc7c2000c6a13c3923fe50ad3cfe9..d3d6fa9eb52b59dc46db40f6b15d5350eb1d951b 100644 (file)
@@ -27,8 +27,8 @@ use rustc::middle::mem_categorization as mc;
 use std::mem;
 use std::rc::Rc;
 use syntax::ast;
-use syntax::codemap::{Span, DUMMY_SP};
 use syntax::attr::AttrMetaMethods;
+use syntax_pos::{Span, DUMMY_SP};
 
 #[derive(PartialEq, Eq, PartialOrd, Ord)]
 enum Fragment {
index 83322215e30ca7d28ee97ad0c0c4d82a37f8fa39..9431dcdbcac8ef897f7200be332b8360ca1c4c4e 100644 (file)
@@ -22,7 +22,7 @@ use rustc::ty;
 
 use std::rc::Rc;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::hir::{self, PatKind};
 
 struct GatherMoveInfo<'tcx> {
@@ -98,7 +98,7 @@ pub fn gather_move_from_pat<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
                                       move_pat: &hir::Pat,
                                       cmt: mc::cmt<'tcx>) {
     let pat_span_path_opt = match move_pat.node {
-        PatKind::Ident(_, ref path1, _) => {
+        PatKind::Binding(_, ref path1, _) => {
             Some(MoveSpanAndPath{span: move_pat.span,
                                  name: path1.node})
         },
@@ -122,15 +122,12 @@ fn gather_move<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
 
     let potentially_illegal_move =
                 check_and_get_illegal_move_origin(bccx, &move_info.cmt);
-    match potentially_illegal_move {
-        Some(illegal_move_origin) => {
-            debug!("illegal_move_origin={:?}", illegal_move_origin);
-            let error = MoveError::with_move_info(illegal_move_origin,
-                                                  move_info.span_path_opt);
-            move_error_collector.add_error(error);
-            return
-        }
-        None => ()
+    if let Some(illegal_move_origin) = potentially_illegal_move {
+        debug!("illegal_move_origin={:?}", illegal_move_origin);
+        let error = MoveError::with_move_info(illegal_move_origin,
+                                              move_info.span_path_opt);
+        move_error_collector.add_error(error);
+        return;
     }
 
     match opt_loan_path(&move_info.cmt) {
index c2492bba6247330c0cb6ca9d30a8477c7d596c71..e34c6e567bd8ece197adcf425ef455f1e2d9eb22 100644 (file)
@@ -19,7 +19,7 @@ use rustc::middle::region;
 use rustc::ty;
 
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 type R = Result<(),()>;
 
index 7d4f02bfe1109cdb9bd1a9dc4717d813b742bc57..c982fc091d24c0d8ddebce581190335a86635ca8 100644 (file)
@@ -25,8 +25,8 @@ use rustc::middle::region;
 use rustc::ty::{self, TyCtxt};
 
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::ast::NodeId;
+use syntax_pos::Span;
 use rustc::hir;
 use rustc::hir::Expr;
 use rustc::hir::intravisit;
@@ -369,7 +369,8 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
                     ty::ReLateBound(..) |
                     ty::ReEarlyBound(..) |
                     ty::ReVar(..) |
-                    ty::ReSkolemized(..) => {
+                    ty::ReSkolemized(..) |
+                    ty::ReErased => {
                         span_bug!(
                             cmt.span,
                             "invalid borrow lifetime: {:?}",
index c1e83588570e7eeec7e6d01d870ee7811057b22b..fc17633d63b93b5a50d9ef1e681e528028c80dba 100644 (file)
@@ -14,8 +14,8 @@ use rustc::middle::mem_categorization::Categorization;
 use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
 use rustc::ty;
 use syntax::ast;
-use syntax::codemap;
-use syntax::errors::DiagnosticBuilder;
+use syntax_pos;
+use errors::DiagnosticBuilder;
 use rustc::hir;
 
 pub struct MoveErrorCollector<'tcx> {
@@ -56,7 +56,7 @@ impl<'tcx> MoveError<'tcx> {
 
 #[derive(Clone)]
 pub struct MoveSpanAndPath {
-    pub span: codemap::Span,
+    pub span: syntax_pos::Span,
     pub name: ast::Name,
 }
 
@@ -169,7 +169,7 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
 }
 
 fn note_move_destination(mut err: DiagnosticBuilder,
-                         move_to_span: codemap::Span,
+                         move_to_span: syntax_pos::Span,
                          pat_name: ast::Name,
                          is_first_note: bool) -> DiagnosticBuilder {
     if is_first_note {
index ee8c3aff2ec80545f6c229fe8cb043c3420f0856..3d9df4c8bd0082c2220af04904811d9d4a2a8146 100644 (file)
@@ -15,7 +15,7 @@ use rustc::middle::expr_use_visitor as euv;
 use rustc::middle::mem_categorization as mc;
 use rustc::middle::mem_categorization::Categorization;
 use rustc::ty;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use borrowck::ToInteriorKind;
 
index aa885eb47424d8ade55971fa0bbe562ae3a77c15..155b615d83c7b704262e424fc49a652cafab8b27 100644 (file)
@@ -49,6 +49,8 @@ impl<'tcx> Lift for LvalueElem<'tcx> {
                 ProjectionElem::Field(f.clone(), ty.clone()),
             ProjectionElem::Index(ref i) =>
                 ProjectionElem::Index(i.lift()),
+            ProjectionElem::Subslice {from, to} =>
+                ProjectionElem::Subslice { from: from, to: to },
             ProjectionElem::ConstantIndex {offset,min_length,from_end} =>
                 ProjectionElem::ConstantIndex {
                     offset: offset,
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs
deleted file mode 100644 (file)
index d6dd176..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use syntax::attr::AttrMetaMethods;
-
-use rustc::ty::TyCtxt;
-use rustc::mir::repr::{self, Mir};
-
-use std::io;
-use std::mem;
-use std::usize;
-
-use super::MirBorrowckCtxt;
-use super::gather_moves::{Location, MoveData, MovePathData, MovePathIndex, MoveOutIndex, PathMap};
-use super::graphviz;
-use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
-
-pub trait Dataflow {
-    fn dataflow(&mut self);
-}
-
-impl<'b, 'a: 'b, 'tcx: 'a> Dataflow for MirBorrowckCtxt<'b, 'a, 'tcx> {
-    fn dataflow(&mut self) {
-        self.build_gen_and_kill_sets();
-        self.pre_dataflow_instrumentation().unwrap();
-        self.propagate();
-        self.post_dataflow_instrumentation().unwrap();
-    }
-}
-
-struct PropagationContext<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn>
-    where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue)
-{
-    mbcx: &'c mut MirBorrowckCtxt<'b, 'a, 'tcx>,
-    changed: bool,
-    on_return: OnReturn
-}
-
-impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
-    fn propagate(&mut self) {
-        let mut temp = vec![0; self.flow_state.sets.words_per_block];
-        let mut propcx = PropagationContext {
-            mbcx: &mut *self,
-            changed: true,
-            on_return: |move_data, in_out, dest_lval| {
-                let move_path_index = move_data.rev_lookup.find(dest_lval);
-                on_all_children_bits(in_out,
-                                     &move_data.path_map,
-                                     &move_data.move_paths,
-                                     move_path_index,
-                                     &|in_out, mpi| {
-                                         in_out.clear_bit(mpi.idx());
-                                     });
-            },
-        };
-        while propcx.changed {
-            propcx.changed = false;
-            propcx.reset(&mut temp);
-            propcx.walk_cfg(&mut temp);
-        }
-    }
-
-    fn build_gen_and_kill_sets(&mut self) {
-        // First we need to build the gen- and kill-sets. The
-        // gather_moves information provides a high-level mapping from
-        // mir-locations to the MoveOuts (and those correspond
-        // directly to gen-sets here). But we still need to figure out
-        // the kill-sets.
-
-        let move_data = &self.flow_state.operator;
-        let move_paths = &move_data.move_paths;
-        let loc_map = &move_data.loc_map;
-        let path_map = &move_data.path_map;
-        let rev_lookup = &move_data.rev_lookup;
-
-        for bb in self.mir.all_basic_blocks() {
-            let &repr::BasicBlockData { ref statements,
-                                        ref terminator,
-                                        is_cleanup: _ } =
-                self.mir.basic_block_data(bb);
-
-            let mut sets = self.flow_state.sets.for_block(bb.index());
-            for (j, stmt) in statements.iter().enumerate() {
-                let loc = Location { block: bb, index: j };
-                debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
-                       stmt, loc, &loc_map[loc]);
-                for move_index in &loc_map[loc] {
-                    // Every path deinitialized by a *particular move*
-                    // has corresponding bit, "gen'ed" (i.e. set)
-                    // here, in dataflow vector
-                    zero_to_one(&mut sets.gen_set, *move_index);
-                }
-                match stmt.kind {
-                    repr::StatementKind::Assign(ref lvalue, _) => {
-                        // assigning into this `lvalue` kills all
-                        // MoveOuts from it, and *also* all MoveOuts
-                        // for children and associated fragment sets.
-                        let move_path_index = rev_lookup.find(lvalue);
-
-                        on_all_children_bits(sets.kill_set,
-                                             path_map,
-                                             move_paths,
-                                             move_path_index,
-                                             &|kill_set, mpi| {
-                                                 kill_set.set_bit(mpi.idx());
-                                             });
-                    }
-                }
-            }
-
-            let loc = Location { block: bb, index: statements.len() };
-            debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
-                   terminator, loc, &loc_map[loc]);
-            for move_index in &loc_map[loc] {
-                zero_to_one(&mut sets.gen_set, *move_index);
-            }
-        }
-
-        fn zero_to_one(gen_set: &mut [usize], move_index: MoveOutIndex) {
-            let retval = gen_set.set_bit(move_index.idx());
-            assert!(retval);
-        }
-    }
-}
-
-fn on_all_children_bits<Each>(set: &mut [usize],
-                              path_map: &PathMap,
-                              move_paths: &MovePathData,
-                              move_path_index: MovePathIndex,
-                              each_child: &Each)
-    where Each: Fn(&mut [usize], MoveOutIndex)
-{
-    // 1. invoke `each_child` callback for all moves that directly
-    //    influence path for `move_path_index`
-    for move_index in &path_map[move_path_index] {
-        each_child(set, *move_index);
-    }
-
-    // 2. for each child of the path (that is named in this
-    //    function), recur.
-    //
-    // (Unnamed children are irrelevant to dataflow; by
-    // definition they have no associated moves.)
-    let mut next_child_index = move_paths[move_path_index].first_child;
-    while let Some(child_index) = next_child_index {
-        on_all_children_bits(set, path_map, move_paths, child_index, each_child);
-        next_child_index = move_paths[child_index].next_sibling;
-    }
-}
-
-impl<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn> PropagationContext<'c, 'b, 'a, 'tcx, OnReturn>
-    where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue)
-{
-    fn reset(&mut self, bits: &mut [usize]) {
-        let e = if self.mbcx.flow_state.operator.initial_value() {usize::MAX} else {0};
-        for b in bits {
-            *b = e;
-        }
-    }
-
-    fn walk_cfg(&mut self, in_out: &mut [usize]) {
-        let &mut MirBorrowckCtxt { ref mir, ref mut flow_state, .. } = self.mbcx;
-        for (idx, bb) in mir.basic_blocks.iter().enumerate() {
-            {
-                let sets = flow_state.sets.for_block(idx);
-                debug_assert!(in_out.len() == sets.on_entry.len());
-                in_out.clone_from_slice(sets.on_entry);
-                bitwise(in_out, sets.gen_set, &Union);
-                bitwise(in_out, sets.kill_set, &Subtract);
-            }
-            flow_state.propagate_bits_into_graph_successors_of(in_out,
-                                                               &mut self.changed,
-                                                               bb,
-                                                               &self.on_return);
-        }
-    }
-}
-
-impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
-    fn pre_dataflow_instrumentation(&self) -> io::Result<()> {
-        self.if_attr_meta_name_found(
-            "borrowck_graphviz_preflow",
-            |this, path: &str| {
-                graphviz::print_borrowck_graph_to(this, "preflow", path)
-            })
-    }
-
-    fn post_dataflow_instrumentation(&self) -> io::Result<()> {
-        self.if_attr_meta_name_found(
-            "borrowck_graphviz_postflow",
-            |this, path: &str| {
-                graphviz::print_borrowck_graph_to(this, "postflow", path)
-            })
-    }
-
-    fn if_attr_meta_name_found<F>(&self,
-                                  name: &str,
-                                  callback: F) -> io::Result<()>
-        where F: for <'aa, 'bb> FnOnce(&'aa Self, &'bb str) -> io::Result<()>
-    {
-        for attr in self.attributes {
-            if attr.check_name("rustc_mir") {
-                let items = attr.meta_item_list();
-                for item in items.iter().flat_map(|l| l.iter()) {
-                    if item.check_name(name) {
-                        if let Some(s) = item.value_str() {
-                            return callback(self, &s);
-                        } else {
-                            self.bcx.tcx.sess.span_err(
-                                item.span,
-                                &format!("{} attribute requires a path", item.name()));
-                        }
-                    }
-                }
-            }
-        }
-
-        Ok(())
-    }
-}
-
-/// Maps each block to a set of bits
-#[derive(Clone, Debug)]
-struct Bits {
-    bits: Vec<usize>,
-}
-
-impl Bits {
-    fn new(init_word: usize, num_words: usize) -> Self {
-        Bits { bits: vec![init_word; num_words] }
-    }
-}
-
-pub struct DataflowState<O: BitDenotation>
-{
-    /// All the sets for the analysis. (Factored into its
-    /// own structure so that we can borrow it mutably
-    /// on its own separate from other fields.)
-    pub sets: AllSets,
-
-    /// operator used to initialize, combine, and interpret bits.
-    operator: O,
-}
-
-pub struct AllSets {
-    /// Analysis bitwidth for each block.
-    bits_per_block: usize,
-
-    /// Number of words associated with each block entry
-    /// equal to bits_per_block / usize::BITS, rounded up.
-    words_per_block: usize,
-
-    /// For each block, bits generated by executing the statements in
-    /// the block. (For comparison, the Terminator for each block is
-    /// handled in a flow-specific manner during propagation.)
-    gen_sets: Bits,
-
-    /// For each block, bits killed by executing the statements in the
-    /// block. (For comparison, the Terminator for each block is
-    /// handled in a flow-specific manner during propagation.)
-    kill_sets: Bits,
-
-    /// For each block, bits valid on entry to the block.
-    on_entry_sets: Bits,
-}
-
-pub struct BlockSets<'a> {
-    on_entry: &'a mut [usize],
-    gen_set: &'a mut [usize],
-    kill_set: &'a mut [usize],
-}
-
-impl AllSets {
-    pub fn bits_per_block(&self) -> usize { self.bits_per_block }
-    pub fn bytes_per_block(&self) -> usize { (self.bits_per_block + 7) / 8 }
-    pub fn for_block(&mut self, block_idx: usize) -> BlockSets {
-        let offset = self.words_per_block * block_idx;
-        let range = offset..(offset + self.words_per_block);
-        BlockSets {
-            on_entry: &mut self.on_entry_sets.bits[range.clone()],
-            gen_set: &mut self.gen_sets.bits[range.clone()],
-            kill_set: &mut self.kill_sets.bits[range],
-        }
-    }
-
-    fn lookup_set_for<'a>(&self, sets: &'a Bits, block_idx: usize) -> &'a [usize] {
-        let offset = self.words_per_block * block_idx;
-        &sets.bits[offset..(offset + self.words_per_block)]
-    }
-    pub fn gen_set_for(&self, block_idx: usize) -> &[usize] {
-        self.lookup_set_for(&self.gen_sets, block_idx)
-    }
-    pub fn kill_set_for(&self, block_idx: usize) -> &[usize] {
-        self.lookup_set_for(&self.kill_sets, block_idx)
-    }
-    pub fn on_entry_set_for(&self, block_idx: usize) -> &[usize] {
-        self.lookup_set_for(&self.on_entry_sets, block_idx)
-    }
-}
-
-impl<O: BitDenotation> DataflowState<O> {
-    fn each_bit<F>(&self, words: &[usize], mut f: F)
-        where F: FnMut(usize) {
-        //! Helper for iterating over the bits in a bitvector.
-
-        for (word_index, &word) in words.iter().enumerate() {
-            if word != 0 {
-                let usize_bits: usize = mem::size_of::<usize>();
-                let base_index = word_index * usize_bits;
-                for offset in 0..usize_bits {
-                    let bit = 1 << offset;
-                    if (word & bit) != 0 {
-                        // NB: we round up the total number of bits
-                        // that we store in any given bit set so that
-                        // it is an even multiple of usize::BITS. This
-                        // means that there may be some stray bits at
-                        // the end that do not correspond to any
-                        // actual value; that's why we first check
-                        // that we are in range of bits_per_block.
-                        let bit_index = base_index + offset as usize;
-                        if bit_index >= self.sets.bits_per_block() {
-                            return;
-                        } else {
-                            f(bit_index);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    pub fn interpret_set(&self, words: &[usize]) -> Vec<&O::Bit> {
-        let mut v = Vec::new();
-        self.each_bit(words, |i| {
-            v.push(self.operator.interpret(i));
-        });
-        v
-    }
-}
-
-pub trait BitwiseOperator {
-    /// Joins two predecessor bits together, typically either `|` or `&`
-    fn join(&self, pred1: usize, pred2: usize) -> usize;
-}
-
-/// Parameterization for the precise form of data flow that is used.
-pub trait DataflowOperator : BitwiseOperator {
-    /// Specifies the initial value for each bit in the `on_entry` set
-    fn initial_value(&self) -> bool;
-}
-
-pub trait BitDenotation: DataflowOperator {
-    /// Specifies what is represented by each bit in the dataflow bitvector.
-    type Bit;
-    /// Size of each bivector allocated for each block in the analysis.
-    fn bits_per_block(&self) -> usize;
-    /// Provides the meaning of each entry in the dataflow bitvector.
-    /// (Mostly intended for use for better debug instrumentation.)
-    fn interpret(&self, idx: usize) -> &Self::Bit;
-}
-
-impl<D: BitDenotation> DataflowState<D> {
-    pub fn new(mir: &Mir, denotation: D) -> Self {
-        let bits_per_block = denotation.bits_per_block();
-        let usize_bits = mem::size_of::<usize>() * 8;
-        let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits;
-        let num_blocks = mir.basic_blocks.len();
-        let num_words = num_blocks * words_per_block;
-
-        let entry = if denotation.initial_value() { usize::MAX } else {0};
-
-        let zeroes = Bits::new(0, num_words);
-        let on_entry = Bits::new(entry, num_words);
-
-        DataflowState {
-            sets: AllSets {
-                bits_per_block: bits_per_block,
-                words_per_block: words_per_block,
-                gen_sets: zeroes.clone(),
-                kill_sets: zeroes,
-                on_entry_sets: on_entry,
-            },
-            operator: denotation,
-        }
-    }
-}
-
-impl<D: BitDenotation> DataflowState<D> {
-    /// Propagates the bits of `in_out` into all the successors of `bb`,
-    /// using bitwise operator denoted by `self.operator`.
-    ///
-    /// For most blocks, this is entirely uniform. However, for blocks
-    /// that end with a call terminator, the effect of the call on the
-    /// dataflow state may depend on whether the call returned
-    /// successfully or unwound. To reflect this, the `on_return`
-    /// callback mutates `in_out` when propagating `in_out` via a call
-    /// terminator; such mutation is performed *last*, to ensure its
-    /// side-effects do not leak elsewhere (e.g. into unwind target).
-    fn propagate_bits_into_graph_successors_of<OnReturn>(
-        &mut self,
-        in_out: &mut [usize],
-        changed: &mut bool,
-        bb: &repr::BasicBlockData,
-        on_return: OnReturn) where OnReturn: Fn(&D, &mut [usize], &repr::Lvalue)
-    {
-        match bb.terminator().kind {
-            repr::TerminatorKind::Return |
-            repr::TerminatorKind::Resume => {}
-            repr::TerminatorKind::Goto { ref target } |
-            repr::TerminatorKind::Drop { ref target, value: _, unwind: None } => {
-                self.propagate_bits_into_entry_set_for(in_out, changed, target);
-            }
-            repr::TerminatorKind::Drop { ref target, value: _, unwind: Some(ref unwind) } => {
-                self.propagate_bits_into_entry_set_for(in_out, changed, target);
-                self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
-            }
-            repr::TerminatorKind::If { ref targets, .. } => {
-                self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0);
-                self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1);
-            }
-            repr::TerminatorKind::Switch { ref targets, .. } |
-            repr::TerminatorKind::SwitchInt { ref targets, .. } => {
-                for target in targets {
-                    self.propagate_bits_into_entry_set_for(in_out, changed, target);
-                }
-            }
-            repr::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => {
-                if let Some(ref unwind) = *cleanup {
-                    self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
-                }
-                if let Some((ref dest_lval, ref dest_bb)) = *destination {
-                    // N.B.: This must be done *last*, after all other
-                    // propagation, as documented in comment above.
-                    on_return(&self.operator, in_out, dest_lval);
-                    self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb);
-                }
-            }
-        }
-    }
-
-    fn propagate_bits_into_entry_set_for(&mut self,
-                                         in_out: &[usize],
-                                         changed: &mut bool,
-                                         bb: &repr::BasicBlock) {
-        let entry_set = self.sets.for_block(bb.index()).on_entry;
-        let set_changed = bitwise(entry_set, in_out, &self.operator);
-        if set_changed {
-            *changed = true;
-        }
-    }
-}
-
-
-impl<'a, 'tcx> DataflowState<MoveData<'tcx>> {
-    pub fn new_move_analysis(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
-        let move_data = MoveData::gather_moves(mir, tcx);
-        DataflowState::new(mir, move_data)
-    }
-}
-
-impl<'tcx> BitwiseOperator for MoveData<'tcx> {
-    #[inline]
-    fn join(&self, pred1: usize, pred2: usize) -> usize {
-        pred1 | pred2 // moves from both preds are in scope
-    }
-}
-
-impl<'tcx> DataflowOperator for MoveData<'tcx> {
-    #[inline]
-    fn initial_value(&self) -> bool {
-        false // no loans in scope by default
-    }
-}
-
-#[inline]
-fn bitwise<Op:BitwiseOperator>(out_vec: &mut [usize],
-                               in_vec: &[usize],
-                               op: &Op) -> bool {
-    assert_eq!(out_vec.len(), in_vec.len());
-    let mut changed = false;
-    for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) {
-        let old_val = *out_elt;
-        let new_val = op.join(old_val, *in_elt);
-        *out_elt = new_val;
-        changed |= old_val != new_val;
-    }
-    changed
-}
-
-struct Union;
-impl BitwiseOperator for Union {
-    fn join(&self, a: usize, b: usize) -> usize { a | b }
-}
-struct Subtract;
-impl BitwiseOperator for Subtract {
-    fn join(&self, a: usize, b: usize) -> usize { a & !b }
-}
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs
new file mode 100644 (file)
index 0000000..91be50d
--- /dev/null
@@ -0,0 +1,344 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Hook into libgraphviz for rendering dataflow graphs for MIR.
+
+use syntax::ast::NodeId;
+use rustc::mir::repr::{BasicBlock, Mir};
+use rustc_data_structures::indexed_vec::Idx;
+
+use dot;
+use dot::IntoCow;
+
+use std::fmt::Debug;
+use std::fs::File;
+use std::io;
+use std::io::prelude::*;
+use std::marker::PhantomData;
+use std::mem;
+use std::path::Path;
+
+use super::super::MoveDataParamEnv;
+use super::super::MirBorrowckCtxtPreDataflow;
+use bitslice::bits_to_string;
+use indexed_set::{IdxSet};
+use super::{BitDenotation, DataflowState};
+
+impl<O: BitDenotation> DataflowState<O> {
+    fn each_bit<F>(&self, ctxt: &O::Ctxt, words: &IdxSet<O::Idx>, mut f: F)
+        where F: FnMut(O::Idx) {
+        //! Helper for iterating over the bits in a bitvector.
+
+        let bits_per_block = self.operator.bits_per_block(ctxt);
+        let usize_bits: usize = mem::size_of::<usize>() * 8;
+
+        for (word_index, &word) in words.words().iter().enumerate() {
+            if word != 0 {
+                let base_index = word_index * usize_bits;
+                for offset in 0..usize_bits {
+                    let bit = 1 << offset;
+                    if (word & bit) != 0 {
+                        // NB: we round up the total number of bits
+                        // that we store in any given bit set so that
+                        // it is an even multiple of usize::BITS. This
+                        // means that there may be some stray bits at
+                        // the end that do not correspond to any
+                        // actual value; that's why we first check
+                        // that we are in range of bits_per_block.
+                        let bit_index = base_index + offset as usize;
+                        if bit_index >= bits_per_block {
+                            return;
+                        } else {
+                            f(O::Idx::new(bit_index));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    pub fn interpret_set<'c, P>(&self,
+                                ctxt: &'c O::Ctxt,
+                                words: &IdxSet<O::Idx>,
+                                render_idx: &P)
+                                -> Vec<&'c Debug>
+        where P: for <'b> Fn(&'b O::Ctxt, O::Idx) -> &'b Debug
+    {
+        let mut v = Vec::new();
+        self.each_bit(ctxt, words, |i| {
+            v.push(render_idx(ctxt, i));
+        });
+        v
+    }
+}
+
+pub trait MirWithFlowState<'tcx> {
+    type BD: BitDenotation<Ctxt=MoveDataParamEnv<'tcx>>;
+    fn node_id(&self) -> NodeId;
+    fn mir(&self) -> &Mir<'tcx>;
+    fn analysis_ctxt(&self) -> &<Self::BD as BitDenotation>::Ctxt;
+    fn flow_state(&self) -> &DataflowState<Self::BD>;
+}
+
+impl<'a, 'tcx: 'a, BD> MirWithFlowState<'tcx> for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
+    where 'a, 'tcx: 'a, BD: BitDenotation<Ctxt=MoveDataParamEnv<'tcx>>
+{
+    type BD = BD;
+    fn node_id(&self) -> NodeId { self.node_id }
+    fn mir(&self) -> &Mir<'tcx> { self.flow_state.mir() }
+    fn analysis_ctxt(&self) -> &BD::Ctxt { &self.flow_state.ctxt }
+    fn flow_state(&self) -> &DataflowState<Self::BD> { &self.flow_state.flow_state }
+}
+
+struct Graph<'a, 'tcx, MWF:'a, P> where
+    MWF: MirWithFlowState<'tcx>
+{
+    mbcx: &'a MWF,
+    phantom: PhantomData<&'tcx ()>,
+    render_idx: P,
+}
+
+pub fn print_borrowck_graph_to<'a, 'tcx, BD, P>(
+    mbcx: &MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>,
+    path: &Path,
+    render_idx: P)
+    -> io::Result<()>
+    where BD: BitDenotation<Ctxt=MoveDataParamEnv<'tcx>>,
+          P: for <'b> Fn(&'b BD::Ctxt, BD::Idx) -> &'b Debug
+{
+    let g = Graph { mbcx: mbcx, phantom: PhantomData, render_idx: render_idx };
+    let mut v = Vec::new();
+    dot::render(&g, &mut v)?;
+    debug!("print_borrowck_graph_to path: {} node_id: {}",
+           path.display(), mbcx.node_id);
+    File::create(path).and_then(|mut f| f.write_all(&v))
+}
+
+pub type Node = BasicBlock;
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Edge { source: BasicBlock, index: usize }
+
+fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec<Edge> {
+    let succ_len = mir[bb].terminator().successors().len();
+    (0..succ_len).map(|index| Edge { source: bb, index: index}).collect()
+}
+
+impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P>
+    where MWF: MirWithFlowState<'tcx>,
+          P: for <'b> Fn(&'b <MWF::BD as BitDenotation>::Ctxt,
+                         <MWF::BD as BitDenotation>::Idx)
+                         -> &'b Debug,
+{
+    type Node = Node;
+    type Edge = Edge;
+    fn graph_id(&self) -> dot::Id {
+        dot::Id::new(format!("graph_for_node_{}",
+                             self.mbcx.node_id()))
+            .unwrap()
+    }
+
+    fn node_id(&self, n: &Node) -> dot::Id {
+        dot::Id::new(format!("bb_{}", n.index()))
+            .unwrap()
+    }
+
+    fn node_label(&self, n: &Node) -> dot::LabelText {
+        // A standard MIR label, as generated by write_node_label, is
+        // presented in a single column in a table.
+        //
+        // The code below does a bunch of formatting work to format a
+        // node (i.e. MIR basic-block) label with extra
+        // dataflow-enriched information.  In particular, the goal is
+        // to add extra columns that present the three dataflow
+        // bitvectors, and the data those bitvectors represent.
+        //
+        // It presents it in the following format (where I am
+        // presenting the table rendering via ASCII art, one line per
+        // row of the table, and a chunk size of 3 rather than 5):
+        //
+        // ------  -----------------------  ------------  --------------------
+        //                    [e1, e3, e4]
+        //             [e8, e9] "= ENTRY:"  <ENTRY-BITS>
+        // ------  -----------------------  ------------  --------------------
+        // Left
+        // Most
+        // Column
+        // Is
+        // Just
+        // Normal
+        // Series
+        // Of
+        // MIR
+        // Stmts
+        // ------  -----------------------  ------------  --------------------
+        //           [g1, g4, g5] "= GEN:"  <GEN-BITS>
+        // ------  -----------------------  ------------  --------------------
+        //                         "KILL:"  <KILL-BITS>   "=" [k1, k3, k8]
+        //                                                [k9]
+        // ------  -----------------------  ------------  --------------------
+        //
+        // (In addition, the added dataflow is rendered with a colored
+        // background just so it will stand out compared to the
+        // statements.)
+        let mut v = Vec::new();
+        let i = n.index();
+        let chunk_size = 5;
+        const BG_FLOWCONTENT: &'static str = r#"bgcolor="pink""#;
+        const ALIGN_RIGHT: &'static str = r#"align="right""#;
+        const FACE_MONOSPACE: &'static str = r#"FACE="Courier""#;
+        fn chunked_present_left<W:io::Write>(w: &mut W,
+                                             interpreted: &[&Debug],
+                                             chunk_size: usize)
+                                             -> io::Result<()>
+        {
+            // This function may emit a sequence of <tr>'s, but it
+            // always finishes with an (unfinished)
+            // <tr><td></td><td>
+            //
+            // Thus, after being called, one should finish both the
+            // pending <td> as well as the <tr> itself.
+            let mut seen_one = false;
+            for c in interpreted.chunks(chunk_size) {
+                if seen_one {
+                    // if not the first row, finish off the previous row
+                    write!(w, "</td><td></td><td></td></tr>")?;
+                }
+                write!(w, "<tr><td></td><td {bg} {align}>{objs:?}",
+                       bg = BG_FLOWCONTENT,
+                       align = ALIGN_RIGHT,
+                       objs = c)?;
+                seen_one = true;
+            }
+            if !seen_one {
+                write!(w, "<tr><td></td><td {bg} {align}>[]",
+                       bg = BG_FLOWCONTENT,
+                       align = ALIGN_RIGHT)?;
+            }
+            Ok(())
+        }
+        ::rustc_mir::graphviz::write_node_label(
+            *n, self.mbcx.mir(), &mut v, 4,
+            |w| {
+                let ctxt = self.mbcx.analysis_ctxt();
+                let flow = self.mbcx.flow_state();
+                let entry_interp = flow.interpret_set(ctxt,
+                                                      flow.sets.on_entry_set_for(i),
+                                                      &self.render_idx);
+                chunked_present_left(w, &entry_interp[..], chunk_size)?;
+                let bits_per_block = flow.sets.bits_per_block();
+                let entry = flow.sets.on_entry_set_for(i);
+                debug!("entry set for i={i} bits_per_block: {bpb} entry: {e:?} interp: {ei:?}",
+                       i=i, e=entry, bpb=bits_per_block, ei=entry_interp);
+                write!(w, "= ENTRY:</td><td {bg}><FONT {face}>{entrybits:?}</FONT></td>\
+                                        <td></td></tr>",
+                       bg = BG_FLOWCONTENT,
+                       face = FACE_MONOSPACE,
+                       entrybits=bits_to_string(entry.words(), bits_per_block))
+            },
+            |w| {
+                let ctxt = self.mbcx.analysis_ctxt();
+                let flow = self.mbcx.flow_state();
+                let gen_interp =
+                    flow.interpret_set(ctxt, flow.sets.gen_set_for(i), &self.render_idx);
+                let kill_interp =
+                    flow.interpret_set(ctxt, flow.sets.kill_set_for(i), &self.render_idx);
+                chunked_present_left(w, &gen_interp[..], chunk_size)?;
+                let bits_per_block = flow.sets.bits_per_block();
+                {
+                    let gen = flow.sets.gen_set_for(i);
+                    debug!("gen set for i={i} bits_per_block: {bpb} gen: {g:?} interp: {gi:?}",
+                           i=i, g=gen, bpb=bits_per_block, gi=gen_interp);
+                    write!(w, " = GEN:</td><td {bg}><FONT {face}>{genbits:?}</FONT></td>\
+                                           <td></td></tr>",
+                           bg = BG_FLOWCONTENT,
+                           face = FACE_MONOSPACE,
+                           genbits=bits_to_string(gen.words(), bits_per_block))?;
+                }
+
+                {
+                    let kill = flow.sets.kill_set_for(i);
+                    debug!("kill set for i={i} bits_per_block: {bpb} kill: {k:?} interp: {ki:?}",
+                           i=i, k=kill, bpb=bits_per_block, ki=kill_interp);
+                    write!(w, "<tr><td></td><td {bg} {align}>KILL:</td>\
+                                            <td {bg}><FONT {face}>{killbits:?}</FONT></td>",
+                           bg = BG_FLOWCONTENT,
+                           align = ALIGN_RIGHT,
+                           face = FACE_MONOSPACE,
+                           killbits=bits_to_string(kill.words(), bits_per_block))?;
+                }
+
+                // (chunked_present_right)
+                let mut seen_one = false;
+                for k in kill_interp.chunks(chunk_size) {
+                    if !seen_one {
+                        // continuation of row; this is fourth <td>
+                        write!(w, "<td {bg}>= {kill:?}</td></tr>",
+                               bg = BG_FLOWCONTENT,
+                               kill=k)?;
+                    } else {
+                        // new row, with indent of three <td>'s
+                        write!(w, "<tr><td></td><td></td><td></td><td {bg}>{kill:?}</td></tr>",
+                               bg = BG_FLOWCONTENT,
+                               kill=k)?;
+                    }
+                    seen_one = true;
+                }
+                if !seen_one {
+                    write!(w, "<td {bg}>= []</td></tr>",
+                           bg = BG_FLOWCONTENT)?;
+                }
+
+                Ok(())
+            })
+            .unwrap();
+        dot::LabelText::html(String::from_utf8(v).unwrap())
+    }
+
+    fn node_shape(&self, _n: &Node) -> Option<dot::LabelText> {
+        Some(dot::LabelText::label("none"))
+    }
+}
+
+impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P>
+    where MWF: MirWithFlowState<'tcx>
+{
+    type Node = Node;
+    type Edge = Edge;
+    fn nodes(&self) -> dot::Nodes<Node> {
+        self.mbcx.mir()
+            .basic_blocks()
+            .indices()
+            .collect::<Vec<_>>()
+            .into_cow()
+    }
+
+    fn edges(&self) -> dot::Edges<Edge> {
+        let mir = self.mbcx.mir();
+        // base initial capacity on assumption every block has at
+        // least one outgoing edge (Which should be true for all
+        // blocks but one, the exit-block).
+        let mut edges = Vec::with_capacity(mir.basic_blocks().len());
+        for bb in mir.basic_blocks().indices() {
+            let outgoing = outgoing(mir, bb);
+            edges.extend(outgoing.into_iter());
+        }
+        edges.into_cow()
+    }
+
+    fn source(&self, edge: &Edge) -> Node {
+        edge.source
+    }
+
+    fn target(&self, edge: &Edge) -> Node {
+        let mir = self.mbcx.mir();
+        mir[edge.source].terminator().successors()[edge.index]
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
new file mode 100644 (file)
index 0000000..932b748
--- /dev/null
@@ -0,0 +1,572 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::ty::TyCtxt;
+use rustc::mir::repr::{self, Mir};
+use rustc_data_structures::indexed_vec::Idx;
+
+use super::super::gather_moves::{Location};
+use super::super::gather_moves::{MoveOutIndex, MovePathIndex};
+use super::super::MoveDataParamEnv;
+use super::super::DropFlagState;
+use super::super::drop_flag_effects_for_function_entry;
+use super::super::drop_flag_effects_for_location;
+use super::super::on_all_children_bits;
+
+use super::{BitDenotation, BlockSets, DataflowOperator};
+
+use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
+use bitslice::{BitwiseOperator};
+use indexed_set::{IdxSet};
+
+// Dataflow analyses are built upon some interpretation of the
+// bitvectors attached to each basic block, represented via a
+// zero-sized structure.
+
+/// `MaybeInitializedLvals` tracks all l-values that might be
+/// initialized upon reaching a particular point in the control flow
+/// for a function.
+///
+/// For example, in code like the following, we have corresponding
+/// dataflow information shown in the right-hand comments.
+///
+/// ```rust
+/// struct S;
+/// fn foo(pred: bool) {                       // maybe-init:
+///                                            // {}
+///     let a = S; let b = S; let c; let d;    // {a, b}
+///
+///     if pred {
+///         drop(a);                           // {   b}
+///         b = S;                             // {   b}
+///
+///     } else {
+///         drop(b);                           // {a}
+///         d = S;                             // {a,       d}
+///
+///     }                                      // {a, b,    d}
+///
+///     c = S;                                 // {a, b, c, d}
+/// }
+/// ```
+///
+/// To determine whether an l-value *must* be initialized at a
+/// particular control-flow point, one can take the set-difference
+/// between this data and the data from `MaybeUninitializedLvals` at the
+/// corresponding control-flow point.
+///
+/// Similarly, at a given `drop` statement, the set-intersection
+/// between this data and `MaybeUninitializedLvals` yields the set of
+/// l-values that would require a dynamic drop-flag at that statement.
+pub struct MaybeInitializedLvals<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &'a Mir<'tcx>,
+}
+
+impl<'a, 'tcx: 'a> MaybeInitializedLvals<'a, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
+        MaybeInitializedLvals { tcx: tcx, mir: mir }
+    }
+}
+
+/// `MaybeUninitializedLvals` tracks all l-values that might be
+/// uninitialized upon reaching a particular point in the control flow
+/// for a function.
+///
+/// For example, in code like the following, we have corresponding
+/// dataflow information shown in the right-hand comments.
+///
+/// ```rust
+/// struct S;
+/// fn foo(pred: bool) {                       // maybe-uninit:
+///                                            // {a, b, c, d}
+///     let a = S; let b = S; let c; let d;    // {      c, d}
+///
+///     if pred {
+///         drop(a);                           // {a,    c, d}
+///         b = S;                             // {a,    c, d}
+///
+///     } else {
+///         drop(b);                           // {   b, c, d}
+///         d = S;                             // {   b, c   }
+///
+///     }                                      // {a, b, c, d}
+///
+///     c = S;                                 // {a, b,    d}
+/// }
+/// ```
+///
+/// To determine whether an l-value *must* be uninitialized at a
+/// particular control-flow point, one can take the set-difference
+/// between this data and the data from `MaybeInitializedLvals` at the
+/// corresponding control-flow point.
+///
+/// Similarly, at a given `drop` statement, the set-intersection
+/// between this data and `MaybeInitializedLvals` yields the set of
+/// l-values that would require a dynamic drop-flag at that statement.
+pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &'a Mir<'tcx>,
+}
+
+impl<'a, 'tcx: 'a> MaybeUninitializedLvals<'a, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
+        MaybeUninitializedLvals { tcx: tcx, mir: mir }
+    }
+}
+
+/// `DefinitelyInitializedLvals` tracks all l-values that are definitely
+/// initialized upon reaching a particular point in the control flow
+/// for a function.
+///
+/// FIXME: Note that once flow-analysis is complete, this should be
+/// the set-complement of MaybeUninitializedLvals; thus we can get rid
+/// of one or the other of these two. I'm inclined to get rid of
+/// MaybeUninitializedLvals, simply because the sets will tend to be
+/// smaller in this analysis and thus easier for humans to process
+/// when debugging.
+///
+/// For example, in code like the following, we have corresponding
+/// dataflow information shown in the right-hand comments.
+///
+/// ```rust
+/// struct S;
+/// fn foo(pred: bool) {                       // definite-init:
+///                                            // {          }
+///     let a = S; let b = S; let c; let d;    // {a, b      }
+///
+///     if pred {
+///         drop(a);                           // {   b,     }
+///         b = S;                             // {   b,     }
+///
+///     } else {
+///         drop(b);                           // {a,        }
+///         d = S;                             // {a,       d}
+///
+///     }                                      // {          }
+///
+///     c = S;                                 // {       c  }
+/// }
+/// ```
+///
+/// To determine whether an l-value *may* be uninitialized at a
+/// particular control-flow point, one can take the set-complement
+/// of this data.
+///
+/// Similarly, at a given `drop` statement, the set-difference between
+/// this data and `MaybeInitializedLvals` yields the set of l-values
+/// that would require a dynamic drop-flag at that statement.
+pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &'a Mir<'tcx>,
+}
+
+impl<'a, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
+        DefinitelyInitializedLvals { tcx: tcx, mir: mir }
+    }
+}
+
+/// `MovingOutStatements` tracks the statements that perform moves out
+/// of particular l-values. More precisely, it tracks whether the
+/// *effect* of such moves (namely, the uninitialization of the
+/// l-value in question) can reach some point in the control-flow of
+/// the function, or if that effect is "killed" by some intervening
+/// operation reinitializing that l-value.
+///
+/// The resulting dataflow is a more enriched version of
+/// `MaybeUninitializedLvals`. Both structures on their own only tell
+/// you if an l-value *might* be uninitialized at a given point in the
+/// control flow. But `MovingOutStatements` also includes the added
+/// data of *which* particular statement causing the deinitialization
+/// that the borrow checker's error meessage may need to report.
+#[allow(dead_code)]
+pub struct MovingOutStatements<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &'a Mir<'tcx>,
+}
+
+impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> {
+    fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
+                   state: DropFlagState)
+    {
+        match state {
+            DropFlagState::Absent => sets.kill(&path),
+            DropFlagState::Present => sets.gen(&path),
+        }
+    }
+}
+
+impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> {
+    fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
+                   state: DropFlagState)
+    {
+        match state {
+            DropFlagState::Absent => sets.gen(&path),
+            DropFlagState::Present => sets.kill(&path),
+        }
+    }
+}
+
+impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> {
+    fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
+                   state: DropFlagState)
+    {
+        match state {
+            DropFlagState::Absent => sets.kill(&path),
+            DropFlagState::Present => sets.gen(&path),
+        }
+    }
+}
+
+impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> {
+    type Idx = MovePathIndex;
+    type Ctxt = MoveDataParamEnv<'tcx>;
+    fn name() -> &'static str { "maybe_init" }
+    fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
+        ctxt.move_data.move_paths.len()
+    }
+
+    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>)
+    {
+        drop_flag_effects_for_function_entry(
+            self.tcx, self.mir, ctxt,
+            |path, s| {
+                assert!(s == DropFlagState::Present);
+                sets.on_entry.add(&path);
+            });
+    }
+
+    fn statement_effect(&self,
+                        ctxt: &Self::Ctxt,
+                        sets: &mut BlockSets<MovePathIndex>,
+                        bb: repr::BasicBlock,
+                        idx: usize)
+    {
+        drop_flag_effects_for_location(
+            self.tcx, self.mir, ctxt,
+            Location { block: bb, index: idx },
+            |path, s| Self::update_bits(sets, path, s)
+        )
+    }
+
+    fn terminator_effect(&self,
+                         ctxt: &Self::Ctxt,
+                         sets: &mut BlockSets<MovePathIndex>,
+                         bb: repr::BasicBlock,
+                         statements_len: usize)
+    {
+        drop_flag_effects_for_location(
+            self.tcx, self.mir, ctxt,
+            Location { block: bb, index: statements_len },
+            |path, s| Self::update_bits(sets, path, s)
+        )
+    }
+
+    fn propagate_call_return(&self,
+                             ctxt: &Self::Ctxt,
+                             in_out: &mut IdxSet<MovePathIndex>,
+                             _call_bb: repr::BasicBlock,
+                             _dest_bb: repr::BasicBlock,
+                             dest_lval: &repr::Lvalue) {
+        // when a call returns successfully, that means we need to set
+        // the bits for that dest_lval to 1 (initialized).
+        let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval);
+        on_all_children_bits(self.tcx, self.mir, &ctxt.move_data,
+                             move_path_index,
+                             |mpi| { in_out.add(&mpi); });
+    }
+}
+
+impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> {
+    type Idx = MovePathIndex;
+    type Ctxt = MoveDataParamEnv<'tcx>;
+    fn name() -> &'static str { "maybe_uninit" }
+    fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
+        ctxt.move_data.move_paths.len()
+    }
+
+    // sets on_entry bits for Arg lvalues
+    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
+        // set all bits to 1 (uninit) before gathering counterevidence
+        for e in sets.on_entry.words_mut() { *e = !0; }
+
+        drop_flag_effects_for_function_entry(
+            self.tcx, self.mir, ctxt,
+            |path, s| {
+                assert!(s == DropFlagState::Present);
+                sets.on_entry.remove(&path);
+            });
+    }
+
+    fn statement_effect(&self,
+                        ctxt: &Self::Ctxt,
+                        sets: &mut BlockSets<MovePathIndex>,
+                        bb: repr::BasicBlock,
+                        idx: usize)
+    {
+        drop_flag_effects_for_location(
+            self.tcx, self.mir, ctxt,
+            Location { block: bb, index: idx },
+            |path, s| Self::update_bits(sets, path, s)
+        )
+    }
+
+    fn terminator_effect(&self,
+                         ctxt: &Self::Ctxt,
+                         sets: &mut BlockSets<MovePathIndex>,
+                         bb: repr::BasicBlock,
+                         statements_len: usize)
+    {
+        drop_flag_effects_for_location(
+            self.tcx, self.mir, ctxt,
+            Location { block: bb, index: statements_len },
+            |path, s| Self::update_bits(sets, path, s)
+        )
+    }
+
+    fn propagate_call_return(&self,
+                             ctxt: &Self::Ctxt,
+                             in_out: &mut IdxSet<MovePathIndex>,
+                             _call_bb: repr::BasicBlock,
+                             _dest_bb: repr::BasicBlock,
+                             dest_lval: &repr::Lvalue) {
+        // when a call returns successfully, that means we need to set
+        // the bits for that dest_lval to 1 (initialized).
+        let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval);
+        on_all_children_bits(self.tcx, self.mir, &ctxt.move_data,
+                             move_path_index,
+                             |mpi| { in_out.remove(&mpi); });
+    }
+}
+
+impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> {
+    type Idx = MovePathIndex;
+    type Ctxt = MoveDataParamEnv<'tcx>;
+    fn name() -> &'static str { "definite_init" }
+    fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
+        ctxt.move_data.move_paths.len()
+    }
+
+    // sets on_entry bits for Arg lvalues
+    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<MovePathIndex>) {
+        for e in sets.on_entry.words_mut() { *e = 0; }
+
+        drop_flag_effects_for_function_entry(
+            self.tcx, self.mir, ctxt,
+            |path, s| {
+                assert!(s == DropFlagState::Present);
+                sets.on_entry.add(&path);
+            });
+    }
+
+    fn statement_effect(&self,
+                        ctxt: &Self::Ctxt,
+                        sets: &mut BlockSets<MovePathIndex>,
+                        bb: repr::BasicBlock,
+                        idx: usize)
+    {
+        drop_flag_effects_for_location(
+            self.tcx, self.mir, ctxt,
+            Location { block: bb, index: idx },
+            |path, s| Self::update_bits(sets, path, s)
+        )
+    }
+
+    fn terminator_effect(&self,
+                         ctxt: &Self::Ctxt,
+                         sets: &mut BlockSets<MovePathIndex>,
+                         bb: repr::BasicBlock,
+                         statements_len: usize)
+    {
+        drop_flag_effects_for_location(
+            self.tcx, self.mir, ctxt,
+            Location { block: bb, index: statements_len },
+            |path, s| Self::update_bits(sets, path, s)
+        )
+    }
+
+    fn propagate_call_return(&self,
+                             ctxt: &Self::Ctxt,
+                             in_out: &mut IdxSet<MovePathIndex>,
+                             _call_bb: repr::BasicBlock,
+                             _dest_bb: repr::BasicBlock,
+                             dest_lval: &repr::Lvalue) {
+        // when a call returns successfully, that means we need to set
+        // the bits for that dest_lval to 1 (initialized).
+        let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval);
+        on_all_children_bits(self.tcx, self.mir, &ctxt.move_data,
+                             move_path_index,
+                             |mpi| { in_out.add(&mpi); });
+    }
+}
+
+impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
+    type Idx = MoveOutIndex;
+    type Ctxt = MoveDataParamEnv<'tcx>;
+    fn name() -> &'static str { "moving_out" }
+    fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize {
+        ctxt.move_data.moves.len()
+    }
+
+    fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets<MoveOutIndex>) {
+        // no move-statements have been executed prior to function
+        // execution, so this method has no effect on `_sets`.
+    }
+    fn statement_effect(&self,
+                        ctxt: &Self::Ctxt,
+                        sets: &mut BlockSets<MoveOutIndex>,
+                        bb: repr::BasicBlock,
+                        idx: usize) {
+        let (tcx, mir, move_data) = (self.tcx, self.mir, &ctxt.move_data);
+        let stmt = &mir[bb].statements[idx];
+        let loc_map = &move_data.loc_map;
+        let path_map = &move_data.path_map;
+        let rev_lookup = &move_data.rev_lookup;
+
+        let loc = Location { block: bb, index: idx };
+        debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
+               stmt, loc, &loc_map[loc]);
+        for move_index in &loc_map[loc] {
+            // Every path deinitialized by a *particular move*
+            // has corresponding bit, "gen'ed" (i.e. set)
+            // here, in dataflow vector
+            zero_to_one(sets.gen_set.words_mut(), *move_index);
+        }
+        let bits_per_block = self.bits_per_block(ctxt);
+        match stmt.kind {
+            repr::StatementKind::Assign(ref lvalue, _) => {
+                // assigning into this `lvalue` kills all
+                // MoveOuts from it, and *also* all MoveOuts
+                // for children and associated fragment sets.
+                let move_path_index = rev_lookup.find(lvalue);
+                on_all_children_bits(tcx,
+                                     mir,
+                                     move_data,
+                                     move_path_index,
+                                     |mpi| for moi in &path_map[mpi] {
+                                         assert!(moi.index() < bits_per_block);
+                                         sets.kill_set.add(&moi);
+                                     });
+            }
+        }
+    }
+
+    fn terminator_effect(&self,
+                         ctxt: &Self::Ctxt,
+                         sets: &mut BlockSets<MoveOutIndex>,
+                         bb: repr::BasicBlock,
+                         statements_len: usize)
+    {
+        let (mir, move_data) = (self.mir, &ctxt.move_data);
+        let term = mir[bb].terminator();
+        let loc_map = &move_data.loc_map;
+        let loc = Location { block: bb, index: statements_len };
+        debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
+               term, loc, &loc_map[loc]);
+        let bits_per_block = self.bits_per_block(ctxt);
+        for move_index in &loc_map[loc] {
+            assert!(move_index.index() < bits_per_block);
+            zero_to_one(sets.gen_set.words_mut(), *move_index);
+        }
+    }
+
+    fn propagate_call_return(&self,
+                             ctxt: &Self::Ctxt,
+                             in_out: &mut IdxSet<MoveOutIndex>,
+                             _call_bb: repr::BasicBlock,
+                             _dest_bb: repr::BasicBlock,
+                             dest_lval: &repr::Lvalue) {
+        let move_data = &ctxt.move_data;
+        let move_path_index = move_data.rev_lookup.find(dest_lval);
+        let bits_per_block = self.bits_per_block(ctxt);
+
+        let path_map = &move_data.path_map;
+        on_all_children_bits(self.tcx,
+                             self.mir,
+                             move_data,
+                             move_path_index,
+                             |mpi| for moi in &path_map[mpi] {
+                                 assert!(moi.index() < bits_per_block);
+                                 in_out.remove(&moi);
+                             });
+    }
+}
+
+fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
+    let retval = bitvec.set_bit(move_index.index());
+    assert!(retval);
+}
+
+impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> {
+    #[inline]
+    fn join(&self, pred1: usize, pred2: usize) -> usize {
+        pred1 | pred2 // moves from both preds are in scope
+    }
+}
+
+impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> {
+    #[inline]
+    fn join(&self, pred1: usize, pred2: usize) -> usize {
+        pred1 | pred2 // "maybe" means we union effects of both preds
+    }
+}
+
+impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> {
+    #[inline]
+    fn join(&self, pred1: usize, pred2: usize) -> usize {
+        pred1 | pred2 // "maybe" means we union effects of both preds
+    }
+}
+
+impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> {
+    #[inline]
+    fn join(&self, pred1: usize, pred2: usize) -> usize {
+        pred1 & pred2 // "definitely" means we intersect effects of both preds
+    }
+}
+
+// The way that dataflow fixed point iteration works, you want to
+// start at bottom and work your way to a fixed point. Control-flow
+// merges will apply the `join` operator to each block entry's current
+// state (which starts at that bottom value).
+//
+// This means, for propagation across the graph, that you either want
+// to start at all-zeroes and then use Union as your merge when
+// propagating, or you start at all-ones and then use Intersect as
+// your merge when propagating.
+
+impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> {
+    #[inline]
+    fn bottom_value() -> bool {
+        false // bottom = no loans in scope by default
+    }
+}
+
+impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> {
+    #[inline]
+    fn bottom_value() -> bool {
+        false // bottom = uninitialized
+    }
+}
+
+impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> {
+    #[inline]
+    fn bottom_value() -> bool {
+        false // bottom = initialized (start_block_effect counters this at outset)
+    }
+}
+
+impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> {
+    #[inline]
+    fn bottom_value() -> bool {
+        true // bottom = initialized (start_block_effect counters this at outset)
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
new file mode 100644 (file)
index 0000000..a9b4de4
--- /dev/null
@@ -0,0 +1,507 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc_data_structures::indexed_vec::Idx;
+
+use rustc::ty::TyCtxt;
+use rustc::mir::repr::{self, Mir};
+
+use std::fmt::Debug;
+use std::io;
+use std::mem;
+use std::path::PathBuf;
+use std::usize;
+
+use super::MirBorrowckCtxtPreDataflow;
+use super::MoveDataParamEnv;
+
+use bitslice::{bitwise, BitwiseOperator};
+use indexed_set::{IdxSet, IdxSetBuf};
+
+pub use self::sanity_check::sanity_check_via_rustc_peek;
+pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals};
+pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements};
+
+mod graphviz;
+mod sanity_check;
+mod impls;
+
+pub trait Dataflow<BD: BitDenotation> {
+    fn dataflow<P>(&mut self, p: P) where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug;
+}
+
+impl<'a, 'tcx: 'a, BD> Dataflow<BD> for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
+    where BD: BitDenotation<Ctxt=MoveDataParamEnv<'tcx>> + DataflowOperator
+{
+    fn dataflow<P>(&mut self, p: P) where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug {
+        self.flow_state.build_sets();
+        self.pre_dataflow_instrumentation(|c,i| p(c,i)).unwrap();
+        self.flow_state.propagate();
+        self.post_dataflow_instrumentation(|c,i| p(c,i)).unwrap();
+    }
+}
+
+struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O>
+    where O: 'b + BitDenotation, O::Ctxt: 'a
+{
+    builder: &'b mut DataflowAnalysis<'a, 'tcx, O>,
+    changed: bool,
+}
+
+impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD>
+    where BD: BitDenotation + DataflowOperator
+{
+    fn propagate(&mut self) {
+        let mut temp = IdxSetBuf::new_empty(self.flow_state.sets.bits_per_block);
+        let mut propcx = PropagationContext {
+            builder: self,
+            changed: true,
+        };
+        while propcx.changed {
+            propcx.changed = false;
+            propcx.reset(&mut temp);
+            propcx.walk_cfg(&mut temp);
+        }
+    }
+
+    fn build_sets(&mut self) {
+        // First we need to build the entry-, gen- and kill-sets. The
+        // gather_moves information provides a high-level mapping from
+        // mir-locations to the MoveOuts (and those correspond
+        // directly to gen-sets here). But we still need to figure out
+        // the kill-sets.
+
+        {
+            let sets = &mut self.flow_state.sets.for_block(repr::START_BLOCK.index());
+            self.flow_state.operator.start_block_effect(&self.ctxt, sets);
+        }
+
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
+            let &repr::BasicBlockData { ref statements,
+                                        ref terminator,
+                                        is_cleanup: _ } = data;
+
+            let sets = &mut self.flow_state.sets.for_block(bb.index());
+            for j_stmt in 0..statements.len() {
+                self.flow_state.operator.statement_effect(&self.ctxt, sets, bb, j_stmt);
+            }
+
+            if terminator.is_some() {
+                let stmts_len = statements.len();
+                self.flow_state.operator.terminator_effect(&self.ctxt, sets, bb, stmts_len);
+            }
+        }
+    }
+}
+
+impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD>
+    where BD: BitDenotation + DataflowOperator
+{
+    fn reset(&mut self, bits: &mut IdxSet<BD::Idx>) {
+        let e = if BD::bottom_value() {!0} else {0};
+        for b in bits.words_mut() {
+            *b = e;
+        }
+    }
+
+    fn walk_cfg(&mut self, in_out: &mut IdxSet<BD::Idx>) {
+        let mir = self.builder.mir;
+        for (bb_idx, bb_data) in mir.basic_blocks().iter().enumerate() {
+            let builder = &mut self.builder;
+            {
+                let sets = builder.flow_state.sets.for_block(bb_idx);
+                debug_assert!(in_out.words().len() == sets.on_entry.words().len());
+                in_out.clone_from(sets.on_entry);
+                in_out.union(sets.gen_set);
+                in_out.subtract(sets.kill_set);
+            }
+            builder.propagate_bits_into_graph_successors_of(
+                in_out, &mut self.changed, (repr::BasicBlock::new(bb_idx), bb_data));
+        }
+    }
+}
+
+fn dataflow_path(context: &str, prepost: &str, path: &str) -> PathBuf {
+    format!("{}_{}", context, prepost);
+    let mut path = PathBuf::from(path);
+    let new_file_name = {
+        let orig_file_name = path.file_name().unwrap().to_str().unwrap();
+        format!("{}_{}", context, orig_file_name)
+    };
+    path.set_file_name(new_file_name);
+    path
+}
+
+impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>
+    where BD: BitDenotation<Ctxt=MoveDataParamEnv<'tcx>>
+{
+    fn pre_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
+        where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug
+    {
+        if let Some(ref path_str) = self.print_preflow_to {
+            let path = dataflow_path(BD::name(), "preflow", path_str);
+            graphviz::print_borrowck_graph_to(self, &path, p)
+        } else {
+            Ok(())
+        }
+    }
+
+    fn post_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
+        where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug
+    {
+        if let Some(ref path_str) = self.print_postflow_to {
+            let path = dataflow_path(BD::name(), "postflow", path_str);
+            graphviz::print_borrowck_graph_to(self, &path, p)
+        } else{
+            Ok(())
+        }
+    }
+}
+
+/// Maps each block to a set of bits
+#[derive(Debug)]
+struct Bits<E:Idx> {
+    bits: IdxSetBuf<E>,
+}
+
+impl<E:Idx> Clone for Bits<E> {
+    fn clone(&self) -> Self { Bits { bits: self.bits.clone() } }
+}
+
+impl<E:Idx> Bits<E> {
+    fn new(bits: IdxSetBuf<E>) -> Self {
+        Bits { bits: bits }
+    }
+}
+
+pub struct DataflowAnalysis<'a, 'tcx: 'a, O>
+    where O: BitDenotation, O::Ctxt: 'a
+{
+    flow_state: DataflowState<O>,
+    mir: &'a Mir<'tcx>,
+    ctxt: &'a O::Ctxt,
+}
+
+impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O>
+    where O: BitDenotation
+{
+    pub fn results(self) -> DataflowResults<O> {
+        DataflowResults(self.flow_state)
+    }
+
+    pub fn mir(&self) -> &'a Mir<'tcx> { self.mir }
+}
+
+pub struct DataflowResults<O>(DataflowState<O>) where O: BitDenotation;
+
+impl<O: BitDenotation> DataflowResults<O> {
+    pub fn sets(&self) -> &AllSets<O::Idx> {
+        &self.0.sets
+    }
+}
+
+// FIXME: This type shouldn't be public, but the graphviz::MirWithFlowState trait
+// references it in a method signature. Look into using `pub(crate)` to address this.
+pub struct DataflowState<O: BitDenotation>
+{
+    /// All the sets for the analysis. (Factored into its
+    /// own structure so that we can borrow it mutably
+    /// on its own separate from other fields.)
+    pub sets: AllSets<O::Idx>,
+
+    /// operator used to initialize, combine, and interpret bits.
+    operator: O,
+}
+
+#[derive(Debug)]
+pub struct AllSets<E: Idx> {
+    /// Analysis bitwidth for each block.
+    bits_per_block: usize,
+
+    /// Number of words associated with each block entry
+    /// equal to bits_per_block / usize::BITS, rounded up.
+    words_per_block: usize,
+
+    /// For each block, bits generated by executing the statements in
+    /// the block. (For comparison, the Terminator for each block is
+    /// handled in a flow-specific manner during propagation.)
+    gen_sets: Bits<E>,
+
+    /// For each block, bits killed by executing the statements in the
+    /// block. (For comparison, the Terminator for each block is
+    /// handled in a flow-specific manner during propagation.)
+    kill_sets: Bits<E>,
+
+    /// For each block, bits valid on entry to the block.
+    on_entry_sets: Bits<E>,
+}
+
+pub struct BlockSets<'a, E: Idx> {
+    on_entry: &'a mut IdxSet<E>,
+    gen_set: &'a mut IdxSet<E>,
+    kill_set: &'a mut IdxSet<E>,
+}
+
+impl<'a, E:Idx> BlockSets<'a, E> {
+    fn gen(&mut self, e: &E) {
+        self.gen_set.add(e);
+        self.kill_set.remove(e);
+    }
+    fn kill(&mut self, e: &E) {
+        self.gen_set.remove(e);
+        self.kill_set.add(e);
+    }
+}
+
+impl<E:Idx> AllSets<E> {
+    pub fn bits_per_block(&self) -> usize { self.bits_per_block }
+    pub fn for_block(&mut self, block_idx: usize) -> BlockSets<E> {
+        let offset = self.words_per_block * block_idx;
+        let range = E::new(offset)..E::new(offset + self.words_per_block);
+        BlockSets {
+            on_entry: self.on_entry_sets.bits.range_mut(&range),
+            gen_set: self.gen_sets.bits.range_mut(&range),
+            kill_set: self.kill_sets.bits.range_mut(&range),
+        }
+    }
+
+    fn lookup_set_for<'a>(&self, sets: &'a Bits<E>, block_idx: usize) -> &'a IdxSet<E> {
+        let offset = self.words_per_block * block_idx;
+        let range = E::new(offset)..E::new(offset + self.words_per_block);
+        sets.bits.range(&range)
+    }
+    pub fn gen_set_for(&self, block_idx: usize) -> &IdxSet<E> {
+        self.lookup_set_for(&self.gen_sets, block_idx)
+    }
+    pub fn kill_set_for(&self, block_idx: usize) -> &IdxSet<E> {
+        self.lookup_set_for(&self.kill_sets, block_idx)
+    }
+    pub fn on_entry_set_for(&self, block_idx: usize) -> &IdxSet<E> {
+        self.lookup_set_for(&self.on_entry_sets, block_idx)
+    }
+}
+
+/// Parameterization for the precise form of data flow that is used.
+pub trait DataflowOperator: BitwiseOperator {
+    /// Specifies the initial value for each bit in the `on_entry` set
+    fn bottom_value() -> bool;
+}
+
+pub trait BitDenotation {
+    /// Specifies what index type is used to access the bitvector.
+    type Idx: Idx;
+
+    /// Specifies what, if any, separate context needs to be supplied for methods below.
+    type Ctxt;
+
+    /// A name describing the dataflow analysis that this
+    /// BitDenotation is supporting.  The name should be something
+    /// suitable for plugging in as part of a filename e.g. avoid
+    /// space-characters or other things that tend to look bad on a
+    /// file system, like slashes or periods. It is also better for
+    /// the name to be reasonably short, again because it will be
+    /// plugged into a filename.
+    fn name() -> &'static str;
+
+    /// Size of each bitvector allocated for each block in the analysis.
+    fn bits_per_block(&self, &Self::Ctxt) -> usize;
+
+    /// Mutates the block-sets (the flow sets for the given
+    /// basic block) according to the effects that have been
+    /// established *prior* to entering the start block.
+    ///
+    /// (For example, establishing the call arguments.)
+    ///
+    /// (Typically this should only modify `sets.on_entry`, since the
+    /// gen and kill sets should reflect the effects of *executing*
+    /// the start block itself.)
+    fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets<Self::Idx>);
+
+    /// Mutates the block-sets (the flow sets for the given
+    /// basic block) according to the effects of evaluating statement.
+    ///
+    /// This is used, in particular, for building up the
+    /// "transfer-function" represnting the overall-effect of the
+    /// block, represented via GEN and KILL sets.
+    ///
+    /// The statement is identified as `bb_data[idx_stmt]`, where
+    /// `bb_data` is the sequence of statements identifed by `bb` in
+    /// the MIR.
+    fn statement_effect(&self,
+                        ctxt: &Self::Ctxt,
+                        sets: &mut BlockSets<Self::Idx>,
+                        bb: repr::BasicBlock,
+                        idx_stmt: usize);
+
+    /// Mutates the block-sets (the flow sets for the given
+    /// basic block) according to the effects of evaluating
+    /// the terminator.
+    ///
+    /// This is used, in particular, for building up the
+    /// "transfer-function" represnting the overall-effect of the
+    /// block, represented via GEN and KILL sets.
+    ///
+    /// The effects applied here cannot depend on which branch the
+    /// terminator took.
+    fn terminator_effect(&self,
+                         ctxt: &Self::Ctxt,
+                         sets: &mut BlockSets<Self::Idx>,
+                         bb: repr::BasicBlock,
+                         idx_term: usize);
+
+    /// Mutates the block-sets according to the (flow-dependent)
+    /// effect of a successful return from a Call terminator.
+    ///
+    /// If basic-block BB_x ends with a call-instruction that, upon
+    /// successful return, flows to BB_y, then this method will be
+    /// called on the exit flow-state of BB_x in order to set up the
+    /// entry flow-state of BB_y.
+    ///
+    /// This is used, in particular, as a special case during the
+    /// "propagate" loop where all of the basic blocks are repeatedly
+    /// visited. Since the effects of a Call terminator are
+    /// flow-dependent, the current MIR cannot encode them via just
+    /// GEN and KILL sets attached to the block, and so instead we add
+    /// this extra machinery to represent the flow-dependent effect.
+    ///
+    /// FIXME: Right now this is a bit of a wart in the API. It might
+    /// be better to represent this as an additional gen- and
+    /// kill-sets associated with each edge coming out of the basic
+    /// block.
+    fn propagate_call_return(&self,
+                             ctxt: &Self::Ctxt,
+                             in_out: &mut IdxSet<Self::Idx>,
+                             call_bb: repr::BasicBlock,
+                             dest_bb: repr::BasicBlock,
+                             dest_lval: &repr::Lvalue);
+}
+
+impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
+    where D: BitDenotation + DataflowOperator
+{
+    pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>,
+               mir: &'a Mir<'tcx>,
+               ctxt: &'a D::Ctxt,
+               denotation: D) -> Self {
+        let bits_per_block = denotation.bits_per_block(&ctxt);
+        let usize_bits = mem::size_of::<usize>() * 8;
+        let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits;
+
+        // (now rounded up to multiple of word size)
+        let bits_per_block = words_per_block * usize_bits;
+
+        let num_blocks = mir.basic_blocks().len();
+        let num_overall = num_blocks * bits_per_block;
+
+        let zeroes = Bits::new(IdxSetBuf::new_empty(num_overall));
+        let on_entry = Bits::new(if D::bottom_value() {
+            IdxSetBuf::new_filled(num_overall)
+        } else {
+            IdxSetBuf::new_empty(num_overall)
+        });
+
+        DataflowAnalysis {
+            ctxt: ctxt,
+            mir: mir,
+            flow_state: DataflowState {
+                sets: AllSets {
+                    bits_per_block: bits_per_block,
+                    words_per_block: words_per_block,
+                    gen_sets: zeroes.clone(),
+                    kill_sets: zeroes,
+                    on_entry_sets: on_entry,
+                },
+                operator: denotation,
+            },
+        }
+
+    }
+}
+
+impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
+    where D: BitDenotation + DataflowOperator
+{
+    /// Propagates the bits of `in_out` into all the successors of `bb`,
+    /// using bitwise operator denoted by `self.operator`.
+    ///
+    /// For most blocks, this is entirely uniform. However, for blocks
+    /// that end with a call terminator, the effect of the call on the
+    /// dataflow state may depend on whether the call returned
+    /// successfully or unwound.
+    ///
+    /// To reflect this, the `propagate_call_return` method of the
+    /// `BitDenotation` mutates `in_out` when propagating `in_out` via
+    /// a call terminator; such mutation is performed *last*, to
+    /// ensure its side-effects do not leak elsewhere (e.g. into
+    /// unwind target).
+    fn propagate_bits_into_graph_successors_of(
+        &mut self,
+        in_out: &mut IdxSet<D::Idx>,
+        changed: &mut bool,
+        (bb, bb_data): (repr::BasicBlock, &repr::BasicBlockData))
+    {
+        match bb_data.terminator().kind {
+            repr::TerminatorKind::Return |
+            repr::TerminatorKind::Resume |
+            repr::TerminatorKind::Unreachable => {}
+            repr::TerminatorKind::Goto { ref target } |
+            repr::TerminatorKind::Assert { ref target, cleanup: None, .. } |
+            repr::TerminatorKind::Drop { ref target, location: _, unwind: None } |
+            repr::TerminatorKind::DropAndReplace {
+                ref target, value: _, location: _, unwind: None
+            } => {
+                self.propagate_bits_into_entry_set_for(in_out, changed, target);
+            }
+            repr::TerminatorKind::Assert { ref target, cleanup: Some(ref unwind), .. } |
+            repr::TerminatorKind::Drop { ref target, location: _, unwind: Some(ref unwind) } |
+            repr::TerminatorKind::DropAndReplace {
+                ref target, value: _, location: _, unwind: Some(ref unwind)
+            } => {
+                self.propagate_bits_into_entry_set_for(in_out, changed, target);
+                self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
+            }
+            repr::TerminatorKind::If { ref targets, .. } => {
+                self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0);
+                self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1);
+            }
+            repr::TerminatorKind::Switch { ref targets, .. } |
+            repr::TerminatorKind::SwitchInt { ref targets, .. } => {
+                for target in targets {
+                    self.propagate_bits_into_entry_set_for(in_out, changed, target);
+                }
+            }
+            repr::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => {
+                if let Some(ref unwind) = *cleanup {
+                    self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
+                }
+                if let Some((ref dest_lval, ref dest_bb)) = *destination {
+                    // N.B.: This must be done *last*, after all other
+                    // propagation, as documented in comment above.
+                    self.flow_state.operator.propagate_call_return(
+                        &self.ctxt, in_out, bb, *dest_bb, dest_lval);
+                    self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb);
+                }
+            }
+        }
+    }
+
+    fn propagate_bits_into_entry_set_for(&mut self,
+                                         in_out: &IdxSet<D::Idx>,
+                                         changed: &mut bool,
+                                         bb: &repr::BasicBlock) {
+        let entry_set = self.flow_state.sets.for_block(bb.index()).on_entry;
+        let set_changed = bitwise(entry_set.words_mut(),
+                                  in_out.words(),
+                                  &self.flow_state.operator);
+        if set_changed {
+            *changed = true;
+        }
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
new file mode 100644 (file)
index 0000000..d59bdf9
--- /dev/null
@@ -0,0 +1,171 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use syntax::abi::{Abi};
+use syntax::ast;
+use syntax_pos::Span;
+
+use rustc::ty::{self, TyCtxt};
+use rustc::mir::repr::{self, Mir};
+use rustc_data_structures::indexed_vec::Idx;
+
+use super::super::gather_moves::{MovePathIndex};
+use super::super::MoveDataParamEnv;
+use super::BitDenotation;
+use super::DataflowResults;
+
+/// This function scans `mir` for all calls to the intrinsic
+/// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
+///
+/// For each such call, determines what the dataflow bit-state is for
+/// the L-value corresponding to `expr`; if the bit-state is a 1, then
+/// that call to `rustc_peek` is ignored by the sanity check. If the
+/// bit-state is a 0, then this pass emits a error message saying
+/// "rustc_peek: bit not set".
+///
+/// The intention is that one can write unit tests for dataflow by
+/// putting code into a compile-fail test and using `rustc_peek` to
+/// make observations about the results of dataflow static analyses.
+///
+/// (If there are any calls to `rustc_peek` that do not match the
+/// expression form above, then that emits an error as well, but those
+/// errors are not intended to be used for unit tests.)
+pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                                mir: &Mir<'tcx>,
+                                                id: ast::NodeId,
+                                                _attributes: &[ast::Attribute],
+                                                flow_ctxt: &O::Ctxt,
+                                                results: &DataflowResults<O>)
+    where O: BitDenotation<Ctxt=MoveDataParamEnv<'tcx>, Idx=MovePathIndex>
+{
+    debug!("sanity_check_via_rustc_peek id: {:?}", id);
+    // FIXME: this is not DRY. Figure out way to abstract this and
+    // `dataflow::build_sets`. (But note it is doing non-standard
+    // stuff, so such generalization may not be realistic.)
+
+    for bb in mir.basic_blocks().indices() {
+        each_block(tcx, mir, flow_ctxt, results, bb);
+    }
+}
+
+fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                           mir: &Mir<'tcx>,
+                           ctxt: &O::Ctxt,
+                           results: &DataflowResults<O>,
+                           bb: repr::BasicBlock) where
+    O: BitDenotation<Ctxt=MoveDataParamEnv<'tcx>, Idx=MovePathIndex>
+{
+    let move_data = &ctxt.move_data;
+    let repr::BasicBlockData { ref statements,
+                               ref terminator,
+                               is_cleanup: _ } = mir[bb];
+
+    let (args, span) = match is_rustc_peek(tcx, terminator) {
+        Some(args_and_span) => args_and_span,
+        None => return,
+    };
+    assert!(args.len() == 1);
+    let peek_arg_lval = match args[0] {
+        repr::Operand::Consume(ref lval @ repr::Lvalue::Temp(_)) => {
+            lval
+        }
+        repr::Operand::Consume(_) |
+        repr::Operand::Constant(_) => {
+            tcx.sess.diagnostic().span_err(
+                span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
+            return;
+        }
+    };
+
+    let mut entry = results.0.sets.on_entry_set_for(bb.index()).to_owned();
+    let mut gen = results.0.sets.gen_set_for(bb.index()).to_owned();
+    let mut kill = results.0.sets.kill_set_for(bb.index()).to_owned();
+
+    // Emulate effect of all statements in the block up to (but not
+    // including) the borrow within `peek_arg_lval`. Do *not* include
+    // call to `peek_arg_lval` itself (since we are peeking the state
+    // of the argument at time immediate preceding Call to
+    // `rustc_peek`).
+
+    let mut sets = super::BlockSets { on_entry: &mut entry,
+                                      gen_set: &mut gen,
+                                      kill_set: &mut kill };
+
+    for (j, stmt) in statements.iter().enumerate() {
+        debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
+        let (lvalue, rvalue) = match stmt.kind {
+            repr::StatementKind::Assign(ref lvalue, ref rvalue) => {
+                (lvalue, rvalue)
+            }
+        };
+
+        if lvalue == peek_arg_lval {
+            if let repr::Rvalue::Ref(_,
+                                     repr::BorrowKind::Shared,
+                                     ref peeking_at_lval) = *rvalue {
+                // Okay, our search is over.
+                let peek_mpi = move_data.rev_lookup.find(peeking_at_lval);
+                let bit_state = sets.on_entry.contains(&peek_mpi);
+                debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
+                       lvalue, peeking_at_lval, bit_state);
+                if !bit_state {
+                    tcx.sess.span_err(span, &format!("rustc_peek: bit not set"));
+                }
+                return;
+            } else {
+                // Our search should have been over, but the input
+                // does not match expectations of `rustc_peek` for
+                // this sanity_check.
+                let msg = &format!("rustc_peek: argument expression \
+                                    must be immediate borrow of form `&expr`");
+                tcx.sess.span_err(span, msg);
+            }
+        }
+
+        let lhs_mpi = move_data.rev_lookup.find(lvalue);
+
+        debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}",
+               lvalue, lhs_mpi, stmt);
+        // reset GEN and KILL sets before emulating their effect.
+        for e in sets.gen_set.words_mut() { *e = 0; }
+        for e in sets.kill_set.words_mut() { *e = 0; }
+        results.0.operator.statement_effect(ctxt, &mut sets, bb, j);
+        sets.on_entry.union(sets.gen_set);
+        sets.on_entry.subtract(sets.kill_set);
+    }
+
+    tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
+                                      anticipated pattern; note that \
+                                      rustc_peek expects input of \
+                                      form `&expr`"));
+}
+
+fn is_rustc_peek<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                           terminator: &'a Option<repr::Terminator<'tcx>>)
+                           -> Option<(&'a [repr::Operand<'tcx>], Span)> {
+    if let Some(repr::Terminator { ref kind, source_info, .. }) = *terminator {
+        if let repr::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind
+        {
+            if let repr::Operand::Constant(ref func) = *oper
+            {
+                if let ty::TyFnDef(def_id, _, &ty::BareFnTy { abi, .. }) = func.ty.sty
+                {
+                    let name = tcx.item_name(def_id);
+                    if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
+                        if name.as_str() == "rustc_peek" {
+                            return Some((args, source_info.span));
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return None;
+}
diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
new file mode 100644 (file)
index 0000000..623ea60
--- /dev/null
@@ -0,0 +1,1034 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use indexed_set::IdxSetBuf;
+use super::gather_moves::{MoveData, MovePathIndex, MovePathContent, Location};
+use super::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
+use super::dataflow::{DataflowResults};
+use super::{drop_flag_effects_for_location, on_all_children_bits};
+use super::{DropFlagState, MoveDataParamEnv};
+use super::patch::MirPatch;
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::subst::{Subst, Substs, VecPerParamSpace};
+use rustc::mir::repr::*;
+use rustc::mir::transform::{Pass, MirPass, MirSource};
+use rustc::middle::const_val::ConstVal;
+use rustc::middle::lang_items;
+use rustc::util::nodemap::FnvHashMap;
+use rustc_data_structures::indexed_vec::Idx;
+use syntax_pos::Span;
+
+use std::fmt;
+use std::u32;
+
+pub struct ElaborateDrops;
+
+impl<'tcx> MirPass<'tcx> for ElaborateDrops {
+    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                    src: MirSource, mir: &mut Mir<'tcx>)
+    {
+        debug!("elaborate_drops({:?} @ {:?})", src, mir.span);
+        match src {
+            MirSource::Fn(..) => {},
+            _ => return
+        }
+        let id = src.item_id();
+        let param_env = ty::ParameterEnvironment::for_item(tcx, id);
+        let move_data = MoveData::gather_moves(mir, tcx);
+        let elaborate_patch = {
+            let mir = &*mir;
+            let env = MoveDataParamEnv {
+                move_data: move_data,
+                param_env: param_env
+            };
+            let flow_inits =
+                super::do_dataflow(tcx, mir, id, &[], &env,
+                                   MaybeInitializedLvals::new(tcx, mir));
+            let flow_uninits =
+                super::do_dataflow(tcx, mir, id, &[], &env,
+                                   MaybeUninitializedLvals::new(tcx, mir));
+
+            ElaborateDropsCtxt {
+                tcx: tcx,
+                mir: mir,
+                env: &env,
+                flow_inits: flow_inits,
+                flow_uninits: flow_uninits,
+                drop_flags: FnvHashMap(),
+                patch: MirPatch::new(mir),
+            }.elaborate()
+        };
+        elaborate_patch.apply(mir);
+    }
+}
+
+impl Pass for ElaborateDrops {}
+
+struct InitializationData {
+    live: IdxSetBuf<MovePathIndex>,
+    dead: IdxSetBuf<MovePathIndex>
+}
+
+impl InitializationData {
+    fn apply_location<'a,'tcx>(&mut self,
+                               tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                               mir: &Mir<'tcx>,
+                               env: &MoveDataParamEnv<'tcx>,
+                               loc: Location)
+    {
+        drop_flag_effects_for_location(tcx, mir, env, loc, |path, df| {
+            debug!("at location {:?}: setting {:?} to {:?}",
+                   loc, path, df);
+            match df {
+                DropFlagState::Present => {
+                    self.live.add(&path);
+                    self.dead.remove(&path);
+                }
+                DropFlagState::Absent => {
+                    self.dead.add(&path);
+                    self.live.remove(&path);
+                }
+            }
+        });
+    }
+
+    fn state(&self, path: MovePathIndex) -> (bool, bool) {
+        (self.live.contains(&path), self.dead.contains(&path))
+    }
+}
+
+impl fmt::Debug for InitializationData {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        Ok(())
+    }
+}
+
+struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &'a Mir<'tcx>,
+    env: &'a MoveDataParamEnv<'tcx>,
+    flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx>>,
+    flow_uninits:  DataflowResults<MaybeUninitializedLvals<'a, 'tcx>>,
+    drop_flags: FnvHashMap<MovePathIndex, Temp>,
+    patch: MirPatch<'tcx>,
+}
+
+#[derive(Copy, Clone, Debug)]
+struct DropCtxt<'a, 'tcx: 'a> {
+    source_info: SourceInfo,
+    is_cleanup: bool,
+
+    init_data: &'a InitializationData,
+
+    lvalue: &'a Lvalue<'tcx>,
+    path: MovePathIndex,
+    succ: BasicBlock,
+    unwind: Option<BasicBlock>
+}
+
+impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
+    fn move_data(&self) -> &'b MoveData<'tcx> { &self.env.move_data }
+    fn param_env(&self) -> &'b ty::ParameterEnvironment<'tcx> {
+        &self.env.param_env
+    }
+
+    fn initialization_data_at(&self, loc: Location) -> InitializationData {
+        let mut data = InitializationData {
+            live: self.flow_inits.sets().on_entry_set_for(loc.block.index())
+                .to_owned(),
+            dead: self.flow_uninits.sets().on_entry_set_for(loc.block.index())
+                .to_owned(),
+        };
+        for stmt in 0..loc.index {
+            data.apply_location(self.tcx, self.mir, self.env,
+                                Location { block: loc.block, index: stmt });
+        }
+        data
+    }
+
+    fn create_drop_flag(&mut self, index: MovePathIndex) {
+        let tcx = self.tcx;
+        let patch = &mut self.patch;
+        self.drop_flags.entry(index).or_insert_with(|| {
+            patch.new_temp(tcx.types.bool)
+        });
+    }
+
+    fn drop_flag(&mut self, index: MovePathIndex) -> Option<Lvalue<'tcx>> {
+        self.drop_flags.get(&index).map(|t| Lvalue::Temp(*t))
+    }
+
+    /// create a patch that elaborates all drops in the input
+    /// MIR.
+    fn elaborate(mut self) -> MirPatch<'tcx>
+    {
+        self.collect_drop_flags();
+
+        self.elaborate_drops();
+
+        self.drop_flags_on_init();
+        self.drop_flags_for_fn_rets();
+        self.drop_flags_for_args();
+        self.drop_flags_for_locs();
+
+        self.patch
+    }
+
+    fn path_needs_drop(&self, path: MovePathIndex) -> bool
+    {
+        match self.move_data().move_paths[path].content {
+            MovePathContent::Lvalue(ref lvalue) => {
+                let ty = self.mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx);
+                debug!("path_needs_drop({:?}, {:?} : {:?})", path, lvalue, ty);
+
+                self.tcx.type_needs_drop_given_env(ty, self.param_env())
+            }
+            _ => false
+        }
+    }
+
+    /// Returns whether this lvalue is tracked by drop elaboration. This
+    /// includes all lvalues, except these (1.) behind references or arrays,
+    ///  or (2.) behind ADT's with a Drop impl.
+    fn lvalue_is_tracked(&self, lv: &Lvalue<'tcx>) -> bool
+    {
+        // `lvalue_contents_drop_state_cannot_differ` only compares
+        // the `lv` to its immediate contents, while this recursively
+        // follows parent chain formed by `base` of each projection.
+        if let &Lvalue::Projection(ref data) = lv {
+            !super::lvalue_contents_drop_state_cannot_differ(self.tcx, self.mir, &data.base) &&
+                self.lvalue_is_tracked(&data.base)
+        } else {
+            true
+        }
+    }
+
+    fn collect_drop_flags(&mut self)
+    {
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
+            let terminator = data.terminator();
+            let location = match terminator.kind {
+                TerminatorKind::Drop { ref location, .. } |
+                TerminatorKind::DropAndReplace { ref location, .. } => location,
+                _ => continue
+            };
+
+            if !self.lvalue_is_tracked(location) {
+                continue
+            }
+
+            let init_data = self.initialization_data_at(Location {
+                block: bb,
+                index: data.statements.len()
+            });
+
+            let path = self.move_data().rev_lookup.find(location);
+            debug!("collect_drop_flags: {:?}, lv {:?} (index {:?})",
+                   bb, location, path);
+
+            on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| {
+                if self.path_needs_drop(child) {
+                    let (maybe_live, maybe_dead) = init_data.state(child);
+                    debug!("collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}",
+                           child, location, path, (maybe_live, maybe_dead));
+                    if maybe_live && maybe_dead {
+                        self.create_drop_flag(child)
+                    }
+                }
+            });
+        }
+    }
+
+    fn elaborate_drops(&mut self)
+    {
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
+            let loc = Location { block: bb, index: data.statements.len() };
+            let terminator = data.terminator();
+
+            let resume_block = self.patch.resume_block();
+            match terminator.kind {
+                TerminatorKind::Drop { ref location, target, unwind } => {
+                    let init_data = self.initialization_data_at(loc);
+                    let path = self.move_data().rev_lookup.find(location);
+                    self.elaborate_drop(&DropCtxt {
+                        source_info: terminator.source_info,
+                        is_cleanup: data.is_cleanup,
+                        init_data: &init_data,
+                        lvalue: location,
+                        path: path,
+                        succ: target,
+                        unwind: if data.is_cleanup {
+                            None
+                        } else {
+                            Some(Option::unwrap_or(unwind, resume_block))
+                        }
+                    }, bb);
+                }
+                TerminatorKind::DropAndReplace { ref location, ref value,
+                                                 target, unwind } =>
+                {
+                    assert!(!data.is_cleanup);
+
+                    self.elaborate_replace(
+                        loc,
+                        location, value,
+                        target, unwind
+                    );
+                }
+                _ => continue
+            }
+        }
+    }
+
+    /// Elaborate a MIR `replace` terminator. This instruction
+    /// is not directly handled by translation, and therefore
+    /// must be desugared.
+    ///
+    /// The desugaring drops the location if needed, and then writes
+    /// the value (including setting the drop flag) over it in *both* arms.
+    ///
+    /// The `replace` terminator can also be called on lvalues that
+    /// are not tracked by elaboration (for example,
+    /// `replace x[i] <- tmp0`). The borrow checker requires that
+    /// these locations are initialized before the assignment,
+    /// so we just generate an unconditional drop.
+    fn elaborate_replace(
+        &mut self,
+        loc: Location,
+        location: &Lvalue<'tcx>,
+        value: &Operand<'tcx>,
+        target: BasicBlock,
+        unwind: Option<BasicBlock>)
+    {
+        let bb = loc.block;
+        let data = &self.mir[bb];
+        let terminator = data.terminator();
+
+        let assign = Statement {
+            kind: StatementKind::Assign(location.clone(), Rvalue::Use(value.clone())),
+            source_info: terminator.source_info
+        };
+
+        let unwind = unwind.unwrap_or(self.patch.resume_block());
+        let unwind = self.patch.new_block(BasicBlockData {
+            statements: vec![assign.clone()],
+            terminator: Some(Terminator {
+                kind: TerminatorKind::Goto { target: unwind },
+                ..*terminator
+            }),
+            is_cleanup: true
+        });
+
+        let target = self.patch.new_block(BasicBlockData {
+            statements: vec![assign],
+            terminator: Some(Terminator {
+                kind: TerminatorKind::Goto { target: target },
+                ..*terminator
+            }),
+            is_cleanup: data.is_cleanup,
+        });
+
+        if !self.lvalue_is_tracked(location) {
+            // drop and replace behind a pointer/array/whatever. The location
+            // must be initialized.
+            debug!("elaborate_drop_and_replace({:?}) - untracked", terminator);
+            self.patch.patch_terminator(bb, TerminatorKind::Drop {
+                location: location.clone(),
+                target: target,
+                unwind: Some(unwind)
+            });
+        } else {
+            debug!("elaborate_drop_and_replace({:?}) - tracked", terminator);
+            let init_data = self.initialization_data_at(loc);
+            let path = self.move_data().rev_lookup.find(location);
+
+            self.elaborate_drop(&DropCtxt {
+                source_info: terminator.source_info,
+                is_cleanup: data.is_cleanup,
+                init_data: &init_data,
+                lvalue: location,
+                path: path,
+                succ: target,
+                unwind: Some(unwind)
+            }, bb);
+            on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| {
+                self.set_drop_flag(Location { block: target, index: 0 },
+                                   child, DropFlagState::Present);
+                self.set_drop_flag(Location { block: unwind, index: 0 },
+                                   child, DropFlagState::Present);
+            });
+        }
+    }
+
+    /// This elaborates a single drop instruction, located at `bb`, and
+    /// patches over it.
+    ///
+    /// The elaborated drop checks the drop flags to only drop what
+    /// is initialized.
+    ///
+    /// In addition, the relevant drop flags also need to be cleared
+    /// to avoid double-drops. However, in the middle of a complex
+    /// drop, one must avoid clearing some of the flags before they
+    /// are read, as that would cause a memory leak.
+    ///
+    /// In particular, when dropping an ADT, multiple fields may be
+    /// joined together under the `rest` subpath. They are all controlled
+    /// by the primary drop flag, but only the last rest-field dropped
+    /// should clear it (and it must also not clear anything else).
+    ///
+    /// FIXME: I think we should just control the flags externally
+    /// and then we do not need this machinery.
+    fn elaborate_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, bb: BasicBlock) {
+        debug!("elaborate_drop({:?})", c);
+
+        let mut some_live = false;
+        let mut some_dead = false;
+        let mut children_count = 0;
+        on_all_children_bits(
+            self.tcx, self.mir, self.move_data(),
+            c.path, |child| {
+                if self.path_needs_drop(child) {
+                    let (live, dead) = c.init_data.state(child);
+                    debug!("elaborate_drop: state({:?}) = {:?}",
+                           child, (live, dead));
+                    some_live |= live;
+                    some_dead |= dead;
+                    children_count += 1;
+                }
+            });
+
+        debug!("elaborate_drop({:?}): live - {:?}", c,
+               (some_live, some_dead));
+        match (some_live, some_dead) {
+            (false, false) | (false, true) => {
+                // dead drop - patch it out
+                self.patch.patch_terminator(bb, TerminatorKind::Goto {
+                    target: c.succ
+                });
+            }
+            (true, false) => {
+                // static drop - just set the flag
+                self.patch.patch_terminator(bb, TerminatorKind::Drop {
+                    location: c.lvalue.clone(),
+                    target: c.succ,
+                    unwind: c.unwind
+                });
+                self.drop_flags_for_drop(c, bb);
+            }
+            (true, true) => {
+                // dynamic drop
+                let drop_bb = if children_count == 1 || self.must_complete_drop(c) {
+                    self.conditional_drop(c)
+                } else {
+                    self.open_drop(c)
+                };
+                self.patch.patch_terminator(bb, TerminatorKind::Goto {
+                    target: drop_bb
+                });
+            }
+        }
+    }
+
+    /// Return the lvalue and move path for each field of `variant`,
+    /// (the move path is `None` if the field is a rest field).
+    fn move_paths_for_fields(&self,
+                             base_lv: &Lvalue<'tcx>,
+                             variant_path: MovePathIndex,
+                             variant: ty::VariantDef<'tcx>,
+                             substs: &'tcx Substs<'tcx>)
+                             -> Vec<(Lvalue<'tcx>, Option<MovePathIndex>)>
+    {
+        let move_paths = &self.move_data().move_paths;
+        variant.fields.iter().enumerate().map(|(i, f)| {
+            let subpath =
+                super::move_path_children_matching(move_paths, variant_path, |p| {
+                    match p {
+                        &Projection {
+                            elem: ProjectionElem::Field(idx, _), ..
+                        } => idx.index() == i,
+                        _ => false
+                    }
+                });
+
+            let field_ty =
+                self.tcx.normalize_associated_type_in_env(
+                    &f.ty(self.tcx, substs),
+                    self.param_env()
+                );
+            (base_lv.clone().field(Field::new(i), field_ty), subpath)
+        }).collect()
+    }
+
+    /// Create one-half of the drop ladder for a list of fields, and return
+    /// the list of steps in it in reverse order.
+    ///
+    /// `unwind_ladder` is such a list of steps in reverse order,
+    /// which is called instead of the next step if the drop unwinds
+    /// (the first field is never reached). If it is `None`, all
+    /// unwind targets are left blank.
+    fn drop_halfladder<'a>(&mut self,
+                           c: &DropCtxt<'a, 'tcx>,
+                           unwind_ladder: Option<Vec<BasicBlock>>,
+                           succ: BasicBlock,
+                           fields: &[(Lvalue<'tcx>, Option<MovePathIndex>)],
+                           is_cleanup: bool)
+                           -> Vec<BasicBlock>
+    {
+        let mut succ = succ;
+        let mut unwind_succ = if is_cleanup {
+            None
+        } else {
+            c.unwind
+        };
+        let mut update_drop_flag = true;
+
+        fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| {
+            let drop_block = match path {
+                Some(path) => {
+                    debug!("drop_ladder: for std field {} ({:?})", i, lv);
+
+                    self.elaborated_drop_block(&DropCtxt {
+                        source_info: c.source_info,
+                        is_cleanup: is_cleanup,
+                        init_data: c.init_data,
+                        lvalue: lv,
+                        path: path,
+                        succ: succ,
+                        unwind: unwind_succ,
+                    })
+                }
+                None => {
+                    debug!("drop_ladder: for rest field {} ({:?})", i, lv);
+
+                    let blk = self.complete_drop(&DropCtxt {
+                        source_info: c.source_info,
+                        is_cleanup: is_cleanup,
+                        init_data: c.init_data,
+                        lvalue: lv,
+                        path: c.path,
+                        succ: succ,
+                        unwind: unwind_succ,
+                    }, update_drop_flag);
+
+                    // the drop flag has been updated - updating
+                    // it again would clobber it.
+                    update_drop_flag = false;
+
+                    blk
+                }
+            };
+
+            succ = drop_block;
+            unwind_succ = unwind_ladder.as_ref().map(|p| p[i]);
+
+            drop_block
+        }).collect()
+    }
+
+    /// Create a full drop ladder, consisting of 2 connected half-drop-ladders
+    ///
+    /// For example, with 3 fields, the drop ladder is
+    ///
+    /// .d0:
+    ///     ELAB(drop location.0 [target=.d1, unwind=.c1])
+    /// .d1:
+    ///     ELAB(drop location.1 [target=.d2, unwind=.c2])
+    /// .d2:
+    ///     ELAB(drop location.2 [target=`c.succ`, unwind=`c.unwind`])
+    /// .c1:
+    ///     ELAB(drop location.1 [target=.c2])
+    /// .c2:
+    ///     ELAB(drop location.2 [target=`c.unwind])
+    fn drop_ladder<'a>(&mut self,
+                       c: &DropCtxt<'a, 'tcx>,
+                       fields: Vec<(Lvalue<'tcx>, Option<MovePathIndex>)>)
+                       -> BasicBlock
+    {
+        debug!("drop_ladder({:?}, {:?})", c, fields);
+
+        let mut fields = fields;
+        fields.retain(|&(ref lvalue, _)| {
+            let ty = self.mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx);
+            self.tcx.type_needs_drop_given_env(ty, self.param_env())
+        });
+
+        debug!("drop_ladder - fields needing drop: {:?}", fields);
+
+        let unwind_ladder = if c.is_cleanup {
+            None
+        } else {
+            Some(self.drop_halfladder(c, None, c.unwind.unwrap(), &fields, true))
+        };
+
+        self.drop_halfladder(c, unwind_ladder, c.succ, &fields, c.is_cleanup)
+            .last().cloned().unwrap_or(c.succ)
+    }
+
+    fn open_drop_for_tuple<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, tys: &[Ty<'tcx>])
+                               -> BasicBlock
+    {
+        debug!("open_drop_for_tuple({:?}, {:?})", c, tys);
+
+        let fields = tys.iter().enumerate().map(|(i, &ty)| {
+            (c.lvalue.clone().field(Field::new(i), ty),
+             super::move_path_children_matching(
+                 &self.move_data().move_paths, c.path, |proj| match proj {
+                     &Projection {
+                         elem: ProjectionElem::Field(f, _), ..
+                     } => f.index() == i,
+                     _ => false
+                 }
+            ))
+        }).collect();
+
+        self.drop_ladder(c, fields)
+    }
+
+    fn open_drop_for_box<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, ty: Ty<'tcx>)
+                             -> BasicBlock
+    {
+        debug!("open_drop_for_box({:?}, {:?})", c, ty);
+
+        let interior_path = super::move_path_children_matching(
+            &self.move_data().move_paths, c.path, |proj| match proj {
+                &Projection { elem: ProjectionElem::Deref, .. } => true,
+                _ => false
+            }).unwrap();
+
+        let interior = c.lvalue.clone().deref();
+        let inner_c = DropCtxt {
+            lvalue: &interior,
+            unwind: c.unwind.map(|u| {
+                self.box_free_block(c, ty, u, true)
+            }),
+            succ: self.box_free_block(c, ty, c.succ, c.is_cleanup),
+            path: interior_path,
+            ..*c
+        };
+
+        self.elaborated_drop_block(&inner_c)
+    }
+
+    fn open_drop_for_variant<'a>(&mut self,
+                                 c: &DropCtxt<'a, 'tcx>,
+                                 drop_block: &mut Option<BasicBlock>,
+                                 adt: ty::AdtDef<'tcx>,
+                                 substs: &'tcx Substs<'tcx>,
+                                 variant_index: usize)
+                                 -> BasicBlock
+    {
+        let move_paths = &self.move_data().move_paths;
+
+        let subpath = super::move_path_children_matching(
+            move_paths, c.path, |proj| match proj {
+                &Projection {
+                    elem: ProjectionElem::Downcast(_, idx), ..
+                } => idx == variant_index,
+                _ => false
+            });
+
+        if let Some(variant_path) = subpath {
+            let base_lv = c.lvalue.clone().elem(
+                ProjectionElem::Downcast(adt, variant_index)
+            );
+            let fields = self.move_paths_for_fields(
+                &base_lv,
+                variant_path,
+                &adt.variants[variant_index],
+                substs);
+            self.drop_ladder(c, fields)
+        } else {
+            // variant not found - drop the entire enum
+            if let None = *drop_block {
+                *drop_block = Some(self.complete_drop(c, true));
+            }
+            return drop_block.unwrap();
+        }
+    }
+
+    fn open_drop_for_adt<'a>(&mut self, c: &DropCtxt<'a, 'tcx>,
+                             adt: ty::AdtDef<'tcx>, substs: &'tcx Substs<'tcx>)
+                             -> BasicBlock {
+        debug!("open_drop_for_adt({:?}, {:?}, {:?})", c, adt, substs);
+
+        let mut drop_block = None;
+
+        match adt.variants.len() {
+            1 => {
+                let fields = self.move_paths_for_fields(
+                    c.lvalue,
+                    c.path,
+                    &adt.variants[0],
+                    substs
+                );
+                self.drop_ladder(c, fields)
+            }
+            _ => {
+                let variant_drops : Vec<BasicBlock> =
+                    (0..adt.variants.len()).map(|i| {
+                        self.open_drop_for_variant(c, &mut drop_block,
+                                                   adt, substs, i)
+                    }).collect();
+
+                // If there are multiple variants, then if something
+                // is present within the enum the discriminant, tracked
+                // by the rest path, must be initialized.
+                //
+                // Additionally, we do not want to switch on the
+                // discriminant after it is free-ed, because that
+                // way lies only trouble.
+
+                let switch_block = self.new_block(
+                    c, c.is_cleanup, TerminatorKind::Switch {
+                        discr: c.lvalue.clone(),
+                        adt_def: adt,
+                        targets: variant_drops
+                    });
+
+                self.drop_flag_test_block(c, switch_block)
+            }
+        }
+    }
+
+    /// The slow-path - create an "open", elaborated drop for a type
+    /// which is moved-out-of only partially, and patch `bb` to a jump
+    /// to it. This must not be called on ADTs with a destructor,
+    /// as these can't be moved-out-of, except for `Box<T>`, which is
+    /// special-cased.
+    ///
+    /// This creates a "drop ladder" that drops the needed fields of the
+    /// ADT, both in the success case or if one of the destructors fail.
+    fn open_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
+        let ty = self.mir.lvalue_ty(self.tcx, c.lvalue).to_ty(self.tcx);
+        match ty.sty {
+            ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
+                self.open_drop_for_adt(c, def, substs)
+            }
+            ty::TyTuple(tys) | ty::TyClosure(_, ty::ClosureSubsts {
+                upvar_tys: tys, ..
+            }) => {
+                self.open_drop_for_tuple(c, tys)
+            }
+            ty::TyBox(ty) => {
+                self.open_drop_for_box(c, ty)
+            }
+            _ => bug!("open drop from non-ADT `{:?}`", ty)
+        }
+    }
+
+    /// Return a basic block that drop an lvalue using the context
+    /// and path in `c`. If `update_drop_flag` is true, also
+    /// clear `c`.
+    ///
+    /// if FLAG(c.path)
+    ///     if(update_drop_flag) FLAG(c.path) = false
+    ///     drop(c.lv)
+    fn complete_drop<'a>(
+        &mut self,
+        c: &DropCtxt<'a, 'tcx>,
+        update_drop_flag: bool)
+        -> BasicBlock
+    {
+        debug!("complete_drop({:?},{:?})", c, update_drop_flag);
+
+        let drop_block = self.drop_block(c);
+        if update_drop_flag {
+            self.set_drop_flag(
+                Location { block: drop_block, index: 0 },
+                c.path,
+                DropFlagState::Absent
+            );
+        }
+
+        self.drop_flag_test_block(c, drop_block)
+    }
+
+    /// Create a simple conditional drop.
+    ///
+    /// if FLAG(c.lv)
+    ///     FLAGS(c.lv) = false
+    ///     drop(c.lv)
+    fn conditional_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>)
+                            -> BasicBlock
+    {
+        debug!("conditional_drop({:?})", c);
+        let drop_bb = self.drop_block(c);
+        self.drop_flags_for_drop(c, drop_bb);
+
+        self.drop_flag_test_block(c, drop_bb)
+    }
+
+    fn new_block<'a>(&mut self,
+                     c: &DropCtxt<'a, 'tcx>,
+                     is_cleanup: bool,
+                     k: TerminatorKind<'tcx>)
+                     -> BasicBlock
+    {
+        self.patch.new_block(BasicBlockData {
+            statements: vec![],
+            terminator: Some(Terminator {
+                source_info: c.source_info, kind: k
+            }),
+            is_cleanup: is_cleanup
+        })
+    }
+
+    fn elaborated_drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
+        debug!("elaborated_drop_block({:?})", c);
+        let blk = self.drop_block(c);
+        self.elaborate_drop(c, blk);
+        blk
+    }
+
+    fn drop_flag_test_block<'a>(&mut self,
+                                c: &DropCtxt<'a, 'tcx>,
+                                on_set: BasicBlock)
+                                -> BasicBlock {
+        self.drop_flag_test_block_with_succ(c, c.is_cleanup, on_set, c.succ)
+    }
+
+    fn drop_flag_test_block_with_succ<'a>(&mut self,
+                                          c: &DropCtxt<'a, 'tcx>,
+                                          is_cleanup: bool,
+                                          on_set: BasicBlock,
+                                          on_unset: BasicBlock)
+                                          -> BasicBlock
+    {
+        let (maybe_live, maybe_dead) = c.init_data.state(c.path);
+        debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}",
+               c, is_cleanup, on_set, (maybe_live, maybe_dead));
+
+        match (maybe_live, maybe_dead) {
+            (false, _) => on_unset,
+            (true, false) => on_set,
+            (true, true) => {
+                let flag = self.drop_flag(c.path).unwrap();
+                self.new_block(c, is_cleanup, TerminatorKind::If {
+                    cond: Operand::Consume(flag),
+                    targets: (on_set, on_unset)
+                })
+            }
+        }
+    }
+
+    fn drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
+        self.new_block(c, c.is_cleanup, TerminatorKind::Drop {
+            location: c.lvalue.clone(),
+            target: c.succ,
+            unwind: c.unwind
+        })
+    }
+
+    fn box_free_block<'a>(
+        &mut self,
+        c: &DropCtxt<'a, 'tcx>,
+        ty: Ty<'tcx>,
+        target: BasicBlock,
+        is_cleanup: bool
+    ) -> BasicBlock {
+        let block = self.unelaborated_free_block(c, ty, target, is_cleanup);
+        self.drop_flag_test_block_with_succ(c, is_cleanup, block, target)
+    }
+
+    fn unelaborated_free_block<'a>(
+        &mut self,
+        c: &DropCtxt<'a, 'tcx>,
+        ty: Ty<'tcx>,
+        target: BasicBlock,
+        is_cleanup: bool
+    ) -> BasicBlock {
+        let mut statements = vec![];
+        if let Some(&flag) = self.drop_flags.get(&c.path) {
+            statements.push(Statement {
+                source_info: c.source_info,
+                kind: StatementKind::Assign(
+                    Lvalue::Temp(flag),
+                    self.constant_bool(c.source_info.span, false)
+                )
+            });
+        }
+
+        let tcx = self.tcx;
+        let unit_temp = Lvalue::Temp(self.patch.new_temp(tcx.mk_nil()));
+        let free_func = tcx.lang_items.require(lang_items::BoxFreeFnLangItem)
+            .unwrap_or_else(|e| tcx.sess.fatal(&e));
+        let substs = tcx.mk_substs(Substs::new(
+            VecPerParamSpace::new(vec![], vec![], vec![ty]),
+            VecPerParamSpace::new(vec![], vec![], vec![])
+        ));
+        let fty = tcx.lookup_item_type(free_func).ty.subst(tcx, substs);
+
+        self.patch.new_block(BasicBlockData {
+            statements: statements,
+            terminator: Some(Terminator {
+                source_info: c.source_info, kind: TerminatorKind::Call {
+                    func: Operand::Constant(Constant {
+                        span: c.source_info.span,
+                        ty: fty,
+                        literal: Literal::Item {
+                            def_id: free_func,
+                            substs: substs
+                        }
+                    }),
+                    args: vec![Operand::Consume(c.lvalue.clone())],
+                    destination: Some((unit_temp, target)),
+                    cleanup: None
+                }
+            }),
+            is_cleanup: is_cleanup
+        })
+    }
+
+    fn must_complete_drop<'a>(&self, c: &DropCtxt<'a, 'tcx>) -> bool {
+        // if we have a destuctor, we must *not* split the drop.
+
+        // dataflow can create unneeded children in some cases
+        // - be sure to ignore them.
+
+        let ty = self.mir.lvalue_ty(self.tcx, c.lvalue).to_ty(self.tcx);
+
+        match ty.sty {
+            ty::TyStruct(def, _) | ty::TyEnum(def, _) => {
+                if def.has_dtor() {
+                    self.tcx.sess.span_warn(
+                        c.source_info.span,
+                        &format!("dataflow bug??? moving out of type with dtor {:?}",
+                                 c));
+                    true
+                } else {
+                    false
+                }
+            }
+            _ => false
+        }
+    }
+
+    fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> {
+        Rvalue::Use(Operand::Constant(Constant {
+            span: span,
+            ty: self.tcx.types.bool,
+            literal: Literal::Value { value: ConstVal::Bool(val) }
+        }))
+    }
+
+    fn set_drop_flag(&mut self, loc: Location, path: MovePathIndex, val: DropFlagState) {
+        if let Some(&flag) = self.drop_flags.get(&path) {
+            let span = self.patch.source_info_for_location(self.mir, loc).span;
+            let val = self.constant_bool(span, val.value());
+            self.patch.add_assign(loc, Lvalue::Temp(flag), val);
+        }
+    }
+
+    fn drop_flags_on_init(&mut self) {
+        let loc = Location { block: START_BLOCK, index: 0 };
+        let span = self.patch.source_info_for_location(self.mir, loc).span;
+        let false_ = self.constant_bool(span, false);
+        for flag in self.drop_flags.values() {
+            self.patch.add_assign(loc, Lvalue::Temp(*flag), false_.clone());
+        }
+    }
+
+    fn drop_flags_for_fn_rets(&mut self) {
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
+            if let TerminatorKind::Call {
+                destination: Some((ref lv, tgt)), cleanup: Some(_), ..
+            } = data.terminator().kind {
+                assert!(!self.patch.is_patched(bb));
+
+                let loc = Location { block: tgt, index: 0 };
+                let path = self.move_data().rev_lookup.find(lv);
+                on_all_children_bits(
+                    self.tcx, self.mir, self.move_data(), path,
+                    |child| self.set_drop_flag(loc, child, DropFlagState::Present)
+                );
+            }
+        }
+    }
+
+    fn drop_flags_for_args(&mut self) {
+        let loc = Location { block: START_BLOCK, index: 0 };
+        super::drop_flag_effects_for_function_entry(
+            self.tcx, self.mir, self.env, |path, ds| {
+                self.set_drop_flag(loc, path, ds);
+            }
+        )
+    }
+
+    fn drop_flags_for_locs(&mut self) {
+        // We intentionally iterate only over the *old* basic blocks.
+        //
+        // Basic blocks created by drop elaboration update their
+        // drop flags by themselves, to avoid the drop flags being
+        // clobbered before they are read.
+
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
+            debug!("drop_flags_for_locs({:?})", data);
+            for i in 0..(data.statements.len()+1) {
+                debug!("drop_flag_for_locs: stmt {}", i);
+                let mut allow_initializations = true;
+                if i == data.statements.len() {
+                    match data.terminator().kind {
+                        TerminatorKind::Drop { .. } => {
+                            // drop elaboration should handle that by itself
+                            continue
+                        }
+                        TerminatorKind::DropAndReplace { .. } => {
+                            // this contains the move of the source and
+                            // the initialization of the destination. We
+                            // only want the former - the latter is handled
+                            // by the elaboration code and must be done
+                            // *after* the destination is dropped.
+                            assert!(self.patch.is_patched(bb));
+                            allow_initializations = false;
+                        }
+                        _ => {
+                            assert!(!self.patch.is_patched(bb));
+                        }
+                    }
+                }
+                let loc = Location { block: bb, index: i };
+                super::drop_flag_effects_for_location(
+                    self.tcx, self.mir, self.env, loc, |path, ds| {
+                        if ds == DropFlagState::Absent || allow_initializations {
+                            self.set_drop_flag(loc, path, ds)
+                        }
+                    }
+                )
+            }
+
+            // There may be a critical edge after this call,
+            // so mark the return as initialized *before* the
+            // call.
+            if let TerminatorKind::Call {
+                destination: Some((ref lv, _)), cleanup: None, ..
+            } = data.terminator().kind {
+                assert!(!self.patch.is_patched(bb));
+
+                let loc = Location { block: bb, index: data.statements.len() };
+                let path = self.move_data().rev_lookup.find(lv);
+                on_all_children_bits(
+                    self.tcx, self.mir, self.move_data(), path,
+                    |child| self.set_drop_flag(loc, child, DropFlagState::Present)
+                );
+            }
+        }
+    }
+
+    fn drop_flags_for_drop<'a>(&mut self,
+                               c: &DropCtxt<'a, 'tcx>,
+                               bb: BasicBlock)
+    {
+        let loc = self.patch.terminator_loc(self.mir, bb);
+        on_all_children_bits(
+            self.tcx, self.mir, self.move_data(), c.path,
+            |child| self.set_drop_flag(loc, child, DropFlagState::Absent)
+        );
+    }
+}
index bf3d671bdb5af5611e28d55e2d022ced7793cd98..05412216d487c9e7153b6b2ed9c05d1928cf9c53 100644 (file)
@@ -9,9 +9,10 @@
 // except according to those terms.
 
 
-use rustc::ty::TyCtxt;
+use rustc::ty::{FnOutput, TyCtxt};
 use rustc::mir::repr::*;
 use rustc::util::nodemap::FnvHashMap;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 
 use std::cell::{Cell};
 use std::collections::hash_map::Entry;
@@ -19,7 +20,6 @@ use std::fmt;
 use std::iter;
 use std::ops::Index;
 
-use super::dataflow::BitDenotation;
 use super::abs_domain::{AbstractElem, Lift};
 
 // This submodule holds some newtype'd Index wrappers that are using
@@ -29,17 +29,21 @@ use super::abs_domain::{AbstractElem, Lift};
 // (which is likely to yield a subtle off-by-one error).
 mod indexes {
     use core::nonzero::NonZero;
+    use rustc_data_structures::indexed_vec::Idx;
 
     macro_rules! new_index {
         ($Index:ident) => {
-            #[derive(Copy, Clone, PartialEq, Eq, Debug)]
+            #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
             pub struct $Index(NonZero<usize>);
 
             impl $Index {
-                pub fn new(idx: usize) -> Self {
+            }
+
+            impl Idx for $Index {
+                fn new(idx: usize) -> Self {
                     unsafe { $Index(NonZero::new(idx + 1)) }
                 }
-                pub fn idx(&self) -> usize {
+                fn index(self) -> usize {
                     *self.0 - 1
                 }
             }
@@ -56,6 +60,12 @@ mod indexes {
 pub use self::indexes::MovePathIndex;
 pub use self::indexes::MoveOutIndex;
 
+impl self::indexes::MoveOutIndex {
+    pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex {
+        move_data.moves[self.index()].path
+    }
+}
+
 /// `MovePath` is a canonicalized representation of a path that is
 /// moved or assigned to.
 ///
@@ -125,6 +135,7 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> {
     }
 }
 
+#[derive(Debug)]
 pub struct MoveData<'tcx> {
     pub move_paths: MovePathData<'tcx>,
     pub moves: Vec<MoveOut>,
@@ -133,6 +144,7 @@ pub struct MoveData<'tcx> {
     pub rev_lookup: MovePathLookup<'tcx>,
 }
 
+#[derive(Debug)]
 pub struct LocMap {
     /// Location-indexed (BasicBlock for outer index, index within BB
     /// for inner index) map to list of MoveOutIndex's.
@@ -153,6 +165,7 @@ impl Index<Location> for LocMap {
     }
 }
 
+#[derive(Debug)]
 pub struct PathMap {
     /// Path-indexed map to list of MoveOutIndex's.
     ///
@@ -163,7 +176,7 @@ pub struct PathMap {
 impl Index<MovePathIndex> for PathMap {
     type Output = [MoveOutIndex];
     fn index(&self, index: MovePathIndex) -> &Self::Output {
-        &self.map[index.idx()]
+        &self.map[index.index()]
     }
 }
 
@@ -183,11 +196,11 @@ pub struct MoveOut {
 
 impl fmt::Debug for MoveOut {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write!(fmt, "p{}@{:?}", self.path.idx(), self.source)
+        write!(fmt, "p{}@{:?}", self.path.index(), self.source)
     }
 }
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
 pub struct Location {
     /// block where action is located
     pub block: BasicBlock,
@@ -202,21 +215,22 @@ impl fmt::Debug for Location {
     }
 }
 
+#[derive(Debug)]
 pub struct MovePathData<'tcx> {
     move_paths: Vec<MovePath<'tcx>>,
 }
 
+impl<'tcx> MovePathData<'tcx> {
+    pub fn len(&self) -> usize { self.move_paths.len() }
+}
+
 impl<'tcx> Index<MovePathIndex> for MovePathData<'tcx> {
     type Output = MovePath<'tcx>;
     fn index(&self, i: MovePathIndex) -> &MovePath<'tcx> {
-        &self.move_paths[i.idx()]
+        &self.move_paths[i.index()]
     }
 }
 
-/// MovePathInverseMap maps from a uint in an lvalue-category to the
-/// MovePathIndex for the MovePath for that lvalue.
-type MovePathInverseMap = Vec<Option<MovePathIndex>>;
-
 struct MovePathDataBuilder<'a, 'tcx: 'a> {
     mir: &'a Mir<'tcx>,
     pre_move_paths: Vec<PreMovePath<'tcx>>,
@@ -224,10 +238,11 @@ struct MovePathDataBuilder<'a, 'tcx: 'a> {
 }
 
 /// Tables mapping from an l-value to its MovePathIndex.
+#[derive(Debug)]
 pub struct MovePathLookup<'tcx> {
-    vars: MovePathInverseMap,
-    temps: MovePathInverseMap,
-    args: MovePathInverseMap,
+    vars: IndexVec<Var, Option<MovePathIndex>>,
+    temps: IndexVec<Temp, Option<MovePathIndex>>,
+    args: IndexVec<Arg, Option<MovePathIndex>>,
 
     /// The move path representing the return value is constructed
     /// lazily when we first encounter it in the input MIR.
@@ -272,18 +287,19 @@ impl<T:Clone> FillTo for Vec<T> {
 
 #[derive(Clone, Debug)]
 enum LookupKind { Generate, Reuse }
+#[derive(Clone, Debug)]
 struct Lookup<T>(LookupKind, T);
 
 impl Lookup<MovePathIndex> {
-    fn idx(&self) -> usize { (self.1).idx() }
+    fn index(&self) -> usize { (self.1).index() }
 }
 
 impl<'tcx> MovePathLookup<'tcx> {
-    fn new() -> Self {
+    fn new(mir: &Mir) -> Self {
         MovePathLookup {
-            vars: vec![],
-            temps: vec![],
-            args: vec![],
+            vars: IndexVec::from_elem(None, &mir.var_decls),
+            temps: IndexVec::from_elem(None, &mir.temp_decls),
+            args: IndexVec::from_elem(None, &mir.arg_decls),
             statics: None,
             return_ptr: None,
             projections: vec![],
@@ -293,15 +309,14 @@ impl<'tcx> MovePathLookup<'tcx> {
 
     fn next_index(next: &mut MovePathIndex) -> MovePathIndex {
         let i = *next;
-        *next = MovePathIndex::new(i.idx() + 1);
+        *next = MovePathIndex::new(i.index() + 1);
         i
     }
 
-    fn lookup_or_generate(vec: &mut Vec<Option<MovePathIndex>>,
-                          idx: u32,
-                          next_index: &mut MovePathIndex) -> Lookup<MovePathIndex> {
-        let idx = idx as usize;
-        vec.fill_to_with(idx, None);
+    fn lookup_or_generate<I: Idx>(vec: &mut IndexVec<I, Option<MovePathIndex>>,
+                                  idx: I,
+                                  next_index: &mut MovePathIndex)
+                                  -> Lookup<MovePathIndex> {
         let entry = &mut vec[idx];
         match *entry {
             None => {
@@ -315,19 +330,19 @@ impl<'tcx> MovePathLookup<'tcx> {
         }
     }
 
-    fn lookup_var(&mut self, var_idx: u32) -> Lookup<MovePathIndex> {
+    fn lookup_var(&mut self, var_idx: Var) -> Lookup<MovePathIndex> {
         Self::lookup_or_generate(&mut self.vars,
                                  var_idx,
                                  &mut self.next_index)
     }
 
-    fn lookup_temp(&mut self, temp_idx: u32) -> Lookup<MovePathIndex> {
+    fn lookup_temp(&mut self, temp_idx: Temp) -> Lookup<MovePathIndex> {
         Self::lookup_or_generate(&mut self.temps,
                                  temp_idx,
                                  &mut self.next_index)
     }
 
-    fn lookup_arg(&mut self, arg_idx: u32) -> Lookup<MovePathIndex> {
+    fn lookup_arg(&mut self, arg_idx: Arg) -> Lookup<MovePathIndex> {
         Self::lookup_or_generate(&mut self.args,
                                  arg_idx,
                                  &mut self.next_index)
@@ -364,8 +379,8 @@ impl<'tcx> MovePathLookup<'tcx> {
                    base: MovePathIndex) -> Lookup<MovePathIndex> {
         let MovePathLookup { ref mut projections,
                              ref mut next_index, .. } = *self;
-        projections.fill_to(base.idx());
-        match projections[base.idx()].entry(proj.elem.lift()) {
+        projections.fill_to(base.index());
+        match projections[base.index()].entry(proj.elem.lift()) {
             Entry::Occupied(ent) => {
                 Lookup(LookupKind::Reuse, *ent.get())
             }
@@ -384,14 +399,14 @@ impl<'tcx> MovePathLookup<'tcx> {
     // unknown l-value; it will simply panic.
     pub fn find(&self, lval: &Lvalue<'tcx>) -> MovePathIndex {
         match *lval {
-            Lvalue::Var(var_idx) => self.vars[var_idx as usize].unwrap(),
-            Lvalue::Temp(temp_idx) => self.temps[temp_idx as usize].unwrap(),
-            Lvalue::Arg(arg_idx) => self.args[arg_idx as usize].unwrap(),
+            Lvalue::Var(var) => self.vars[var].unwrap(),
+            Lvalue::Temp(temp) => self.temps[temp].unwrap(),
+            Lvalue::Arg(arg) => self.args[arg].unwrap(),
             Lvalue::Static(ref _def_id) => self.statics.unwrap(),
             Lvalue::ReturnPointer => self.return_ptr.unwrap(),
             Lvalue::Projection(ref proj) => {
                 let base_index = self.find(&proj.base);
-                self.projections[base_index.idx()][&proj.elem.lift()]
+                self.projections[base_index.index()][&proj.elem.lift()]
             }
         }
     }
@@ -419,12 +434,19 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> {
         self.rev_lookup.lookup_proj(proj, base_index)
     }
 
+    fn create_move_path(&mut self, lval: &Lvalue<'tcx>) {
+        // Create MovePath for `lval`, discarding returned index.
+        self.move_path_for(lval);
+    }
+
     fn move_path_for(&mut self, lval: &Lvalue<'tcx>) -> MovePathIndex {
+        debug!("move_path_for({:?})", lval);
+
         let lookup: Lookup<MovePathIndex> = self.lookup(lval);
 
         // `lookup` is either the previously assigned index or a
         // newly-allocated one.
-        debug_assert!(lookup.idx() <= self.pre_move_paths.len());
+        debug_assert!(lookup.index() <= self.pre_move_paths.len());
 
         if let Lookup(LookupKind::Generate, mpi) = lookup {
             let parent;
@@ -455,7 +477,7 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> {
                     let idx = self.move_path_for(&proj.base);
                     parent = Some(idx);
 
-                    let parent_move_path = &mut self.pre_move_paths[idx.idx()];
+                    let parent_move_path = &mut self.pre_move_paths[idx.index()];
 
                     // At last: Swap in the new first_child.
                     sibling = parent_move_path.first_child.get();
@@ -491,15 +513,15 @@ impl<'a, 'tcx> MoveData<'tcx> {
 #[derive(Debug)]
 enum StmtKind {
     Use, Repeat, Cast, BinaryOp, UnaryOp, Box,
-    Aggregate, Drop, CallFn, CallArg, Return,
+    Aggregate, Drop, CallFn, CallArg, Return, If,
 }
 
 fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveData<'tcx> {
     use self::StmtKind as SK;
 
-    let bbs = mir.all_basic_blocks();
-    let mut moves = Vec::with_capacity(bbs.len());
-    let mut loc_map: Vec<_> = iter::repeat(Vec::new()).take(bbs.len()).collect();
+    let bb_count = mir.basic_blocks().len();
+    let mut moves = vec![];
+    let mut loc_map: Vec<_> = iter::repeat(Vec::new()).take(bb_count).collect();
     let mut path_map = Vec::new();
 
     // this is mutable only because we will move it to and fro' the
@@ -508,12 +530,32 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
     let mut builder = MovePathDataBuilder {
         mir: mir,
         pre_move_paths: Vec::new(),
-        rev_lookup: MovePathLookup::new(),
+        rev_lookup: MovePathLookup::new(mir),
     };
 
-    for bb in bbs {
+    // Before we analyze the program text, we create the MovePath's
+    // for all of the vars, args, and temps. (This enforces a basic
+    // property that even if the MIR body doesn't contain any
+    // references to a var/arg/temp, it will still be a valid
+    // operation to lookup the MovePath associated with it.)
+    assert!(mir.var_decls.len() <= ::std::u32::MAX as usize);
+    assert!(mir.arg_decls.len() <= ::std::u32::MAX as usize);
+    assert!(mir.temp_decls.len() <= ::std::u32::MAX as usize);
+    for var in mir.var_decls.indices() {
+        let path_idx = builder.move_path_for(&Lvalue::Var(var));
+        path_map.fill_to(path_idx.index());
+    }
+    for arg in mir.arg_decls.indices() {
+        let path_idx = builder.move_path_for(&Lvalue::Arg(arg));
+        path_map.fill_to(path_idx.index());
+    }
+    for temp in mir.temp_decls.indices() {
+        let path_idx = builder.move_path_for(&Lvalue::Temp(temp));
+        path_map.fill_to(path_idx.index());
+    }
+
+    for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
         let loc_map_bb = &mut loc_map[bb.index()];
-        let bb_data = mir.basic_block_data(bb);
 
         debug_assert!(loc_map_bb.len() == 0);
         let len = bb_data.statements.len();
@@ -521,7 +563,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
         debug_assert!(loc_map_bb.len() == len + 1);
 
         let mut bb_ctxt = BlockContext {
-            tcx: tcx,
+            _tcx: tcx,
             moves: &mut moves,
             builder: builder,
             path_map: &mut path_map,
@@ -532,8 +574,12 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
             let source = Location { block: bb, index: i };
             match stmt.kind {
                 StatementKind::Assign(ref lval, ref rval) => {
-                    // ensure MovePath created for `lval`.
-                    bb_ctxt.builder.move_path_for(lval);
+                    bb_ctxt.builder.create_move_path(lval);
+
+                    // Ensure that the path_map contains entries even
+                    // if the lvalue is assigned and never read.
+                    let assigned_path = bb_ctxt.builder.move_path_for(lval);
+                    bb_ctxt.path_map.fill_to(assigned_path.index());
 
                     match *rval {
                         Rvalue::Use(ref operand) => {
@@ -543,7 +589,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
                             bb_ctxt.on_operand(SK::Repeat, operand, source),
                         Rvalue::Cast(ref _kind, ref operand, ref _ty) =>
                             bb_ctxt.on_operand(SK::Cast, operand, source),
-                        Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) => {
+                        Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) |
+                        Rvalue::CheckedBinaryOp(ref _binop, ref operand1, ref operand2) => {
                             bb_ctxt.on_operand(SK::BinaryOp, operand1, source);
                             bb_ctxt.on_operand(SK::BinaryOp, operand2, source);
                         }
@@ -567,29 +614,49 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
                         Rvalue::Ref(..) |
                         Rvalue::Len(..) |
                         Rvalue::InlineAsm { .. } => {}
-
-                        Rvalue::Slice {..} => {
-                            bug!("cannot move out of slice");
-                        }
                     }
                 }
             }
         }
 
+        debug!("gather_moves({:?})", bb_data.terminator());
         match bb_data.terminator().kind {
-            TerminatorKind::Goto { target: _ } | TerminatorKind::Resume => { }
+            TerminatorKind::Goto { target: _ } |
+            TerminatorKind::Resume |
+            TerminatorKind::Unreachable => { }
 
             TerminatorKind::Return => {
                 let source = Location { block: bb,
                                         index: bb_data.statements.len() };
-                let lval = &Lvalue::ReturnPointer.deref();
-                bb_ctxt.on_move_out_lval(SK::Return, lval, source);
+                if let FnOutput::FnConverging(_) = bb_ctxt.builder.mir.return_ty {
+                    debug!("gather_moves Return on_move_out_lval return {:?}", source);
+                    bb_ctxt.on_move_out_lval(SK::Return, &Lvalue::ReturnPointer, source);
+                } else {
+                    debug!("gather_moves Return on_move_out_lval \
+                            assuming unreachable return {:?}", source);
+                }
             }
 
             TerminatorKind::If { ref cond, targets: _ } => {
+                let source = Location { block: bb,
+                                        index: bb_data.statements.len() };
+                bb_ctxt.on_operand(SK::If, cond, source);
+            }
+
+            TerminatorKind::Assert {
+                ref cond, expected: _,
+                ref msg, target: _, cleanup: _
+            } => {
                 // The `cond` is always of (copyable) type `bool`,
                 // so there will never be anything to move.
                 let _ = cond;
+                match *msg {
+                    AssertMessage:: BoundsCheck { ref len, ref index } => {
+                        // Same for the usize length and index in bounds-checking.
+                        let _ = (len, index);
+                    }
+                    AssertMessage::Math(_) => {}
+                }
             }
 
             TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } |
@@ -601,23 +668,36 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
                 let _ = discr;
             }
 
-            TerminatorKind::Drop { value: ref lval, target: _, unwind: _ } => {
+            TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
                 let source = Location { block: bb,
                                         index: bb_data.statements.len() };
-                bb_ctxt.on_move_out_lval(SK::Drop, lval, source);
+                bb_ctxt.on_move_out_lval(SK::Drop, location, source);
             }
+            TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
+                let assigned_path = bb_ctxt.builder.move_path_for(location);
+                bb_ctxt.path_map.fill_to(assigned_path.index());
 
+                let source = Location { block: bb,
+                                        index: bb_data.statements.len() };
+                bb_ctxt.on_operand(SK::Use, value, source);
+            }
             TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
                 let source = Location { block: bb,
                                         index: bb_data.statements.len() };
                 bb_ctxt.on_operand(SK::CallFn, func, source);
                 for arg in args {
+                    debug!("gather_moves Call on_operand {:?} {:?}", arg, source);
                     bb_ctxt.on_operand(SK::CallArg, arg, source);
                 }
                 if let Some((ref destination, _bb)) = *destination {
-                    // Create MovePath for `destination`, then
-                    // discard returned index.
-                    bb_ctxt.builder.move_path_for(destination);
+                    debug!("gather_moves Call create_move_path {:?} {:?}", destination, source);
+
+                    // Ensure that the path_map contains entries even
+                    // if the lvalue is assigned and never read.
+                    let assigned_path = bb_ctxt.builder.move_path_for(destination);
+                    bb_ctxt.path_map.fill_to(assigned_path.index());
+
+                    bb_ctxt.builder.create_move_path(destination);
                 }
             }
         }
@@ -635,7 +715,6 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
     //
     // well you know, lets actually try just asserting that the path map *is* complete.
     assert_eq!(path_map.len(), builder.pre_move_paths.len());
-    path_map.fill_to(builder.pre_move_paths.len() - 1);
 
     let pre_move_paths = builder.pre_move_paths;
     let move_paths: Vec<_> = pre_move_paths.into_iter()
@@ -646,8 +725,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
         let mut seen: Vec<_> = move_paths.iter().map(|_| false).collect();
         for (j, &MoveOut { ref path, ref source }) in moves.iter().enumerate() {
             debug!("MovePathData moves[{}]: MoveOut {{ path: {:?} = {:?}, source: {:?} }}",
-                   j, path, move_paths[path.idx()], source);
-            seen[path.idx()] = true;
+                   j, path, move_paths[path.index()], source);
+            seen[path.index()] = true;
         }
         for (j, path) in move_paths.iter().enumerate() {
             if !seen[j] {
@@ -667,7 +746,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
 }
 
 struct BlockContext<'b, 'a: 'b, 'tcx: 'a> {
-    tcx: TyCtxt<'b, 'tcx, 'tcx>,
+    _tcx: TyCtxt<'b, 'tcx, 'tcx>,
     moves: &'b mut Vec<MoveOut>,
     builder: MovePathDataBuilder<'a, 'tcx>,
     path_map: &'b mut Vec<Vec<MoveOutIndex>>,
@@ -679,31 +758,12 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
                         stmt_kind: StmtKind,
                         lval: &Lvalue<'tcx>,
                         source: Location) {
-        let tcx = self.tcx;
-        let lval_ty = self.builder.mir.lvalue_ty(tcx, lval);
-
-        // FIXME: does lvalue_ty ever return TyError, or is it
-        // guaranteed to always return non-Infer/non-Error values?
-
-        // This code is just trying to avoid creating a MoveOut
-        // entry for values that do not need move semantics.
-        //
-        // type_contents is imprecise (may claim needs drop for
-        // types that in fact have no destructor). But that is
-        // still usable for our purposes here.
-        let consumed = lval_ty.to_ty(tcx).type_contents(tcx).needs_drop(tcx);
-
-        if !consumed {
-            debug!("ctxt: {:?} no consume of lval: {:?} of type {:?}",
-                   stmt_kind, lval, lval_ty);
-            return;
-        }
         let i = source.index;
         let index = MoveOutIndex::new(self.moves.len());
 
         let path = self.builder.move_path_for(lval);
         self.moves.push(MoveOut { path: path, source: source.clone() });
-        self.path_map.fill_to(path.idx());
+        self.path_map.fill_to(path.index());
 
         debug!("ctxt: {:?} add consume of lval: {:?} \
                 at index: {:?} \
@@ -711,12 +771,12 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
                 to loc_map for loc: {:?}",
                stmt_kind, lval, index, path, source);
 
-        debug_assert!(path.idx() < self.path_map.len());
+        debug_assert!(path.index() < self.path_map.len());
         // this is actually a questionable assert; at the very
         // least, incorrect input code can probably cause it to
         // fire.
-        assert!(self.path_map[path.idx()].iter().find(|idx| **idx == index).is_none());
-        self.path_map[path.idx()].push(index);
+        assert!(self.path_map[path.index()].iter().find(|idx| **idx == index).is_none());
+        self.path_map[path.index()].push(index);
 
         debug_assert!(i < self.loc_map_bb.len());
         debug_assert!(self.loc_map_bb[i].iter().find(|idx| **idx == index).is_none());
@@ -732,13 +792,3 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
         }
     }
 }
-
-impl<'tcx> BitDenotation for MoveData<'tcx>{
-    type Bit = MoveOut;
-    fn bits_per_block(&self) -> usize {
-        self.moves.len()
-    }
-    fn interpret(&self, idx: usize) -> &Self::Bit {
-        &self.moves[idx]
-    }
-}
diff --git a/src/librustc_borrowck/borrowck/mir/graphviz.rs b/src/librustc_borrowck/borrowck/mir/graphviz.rs
deleted file mode 100644 (file)
index 460c71d..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Hook into libgraphviz for rendering dataflow graphs for MIR.
-
-use rustc::mir::repr::{BasicBlock, Mir};
-
-use dot;
-use dot::IntoCow;
-
-use std::fs::File;
-use std::io;
-use std::io::prelude::*;
-
-use super::MirBorrowckCtxt;
-use bitslice::bits_to_string;
-use super::gather_moves::MoveOut;
-
-struct Graph<'c, 'b:'c, 'a:'b, 'tcx:'a> { mbcx: &'c MirBorrowckCtxt<'b, 'a, 'tcx>,
-                                          context: &'b str }
-
-pub fn print_borrowck_graph_to(mbcx: &MirBorrowckCtxt,
-                               context: &str,
-                               path: &str) -> io::Result<()> {
-    let g = Graph { mbcx: mbcx, context: context };
-    let mut v = Vec::new();
-    dot::render(&g, &mut v)?;
-    println!("print_borrowck_graph_to path: {} context: {} node_id: {}",
-             path, context, mbcx.node_id);
-    File::create(path).and_then(|mut f| f.write_all(&v))
-}
-
-pub type Node = BasicBlock;
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct Edge { source: BasicBlock, index: usize }
-
-fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec<Edge> {
-    let succ_len = mir.basic_block_data(bb).terminator().successors().len();
-    (0..succ_len).map(|index| Edge { source: bb, index: index}).collect()
-}
-
-impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> {
-    type Node = Node;
-    type Edge = Edge;
-    fn graph_id(&self) -> dot::Id {
-        dot::Id::new(format!("graph_for_node_{}_{}",
-                             self.mbcx.node_id,
-                             self.context))
-            .unwrap()
-    }
-
-    fn node_id(&self, n: &Node) -> dot::Id {
-        dot::Id::new(format!("bb_{}", n.index()))
-            .unwrap()
-    }
-
-    fn node_label(&self, n: &Node) -> dot::LabelText {
-        // A standard MIR label, as generated by write_node_label, is
-        // presented in a single column in a table.
-        //
-        // The code below does a bunch of formatting work to format a
-        // node (i.e. MIR basic-block) label with extra
-        // dataflow-enriched information.  In particular, the goal is
-        // to add extra columns that present the three dataflow
-        // bitvectors, and the data those bitvectors represent.
-        //
-        // It presents it in the following format (where I am
-        // presenting the table rendering via ASCII art, one line per
-        // row of the table, and a chunk size of 3 rather than 5):
-        //
-        // ------  -----------------------  ------------  --------------------
-        //                    [e1, e3, e4]
-        //             [e8, e9] "= ENTRY:"  <ENTRY-BITS>
-        // ------  -----------------------  ------------  --------------------
-        // Left
-        // Most
-        // Column
-        // Is
-        // Just
-        // Normal
-        // Series
-        // Of
-        // MIR
-        // Stmts
-        // ------  -----------------------  ------------  --------------------
-        //           [g1, g4, g5] "= GEN:"  <GEN-BITS>
-        // ------  -----------------------  ------------  --------------------
-        //                         "KILL:"  <KILL-BITS>   "=" [k1, k3, k8]
-        //                                                [k9]
-        // ------  -----------------------  ------------  --------------------
-        //
-        // (In addition, the added dataflow is rendered with a colored
-        // background just so it will stand out compared to the
-        // statements.)
-        let mut v = Vec::new();
-        let i = n.index();
-        let chunk_size = 5;
-        const BG_FLOWCONTENT: &'static str = r#"bgcolor="pink""#;
-        const ALIGN_RIGHT: &'static str = r#"align="right""#;
-        const FACE_MONOSPACE: &'static str = r#"FACE="Courier""#;
-        fn chunked_present_left<W:io::Write>(w: &mut W,
-                                             interpreted: &[&MoveOut],
-                                             chunk_size: usize)
-                                             -> io::Result<()>
-        {
-            // This function may emit a sequence of <tr>'s, but it
-            // always finishes with an (unfinished)
-            // <tr><td></td><td>
-            //
-            // Thus, after being called, one should finish both the
-            // pending <td> as well as the <tr> itself.
-            let mut seen_one = false;
-            for c in interpreted.chunks(chunk_size) {
-                if seen_one {
-                    // if not the first row, finish off the previous row
-                    write!(w, "</td><td></td><td></td></tr>")?;
-                }
-                write!(w, "<tr><td></td><td {bg} {align}>{objs:?}",
-                       bg = BG_FLOWCONTENT,
-                       align = ALIGN_RIGHT,
-                       objs = c)?;
-                seen_one = true;
-            }
-            if !seen_one {
-                write!(w, "<tr><td></td><td {bg} {align}>[]",
-                       bg = BG_FLOWCONTENT,
-                       align = ALIGN_RIGHT)?;
-            }
-            Ok(())
-        }
-        ::rustc_mir::graphviz::write_node_label(
-            *n, self.mbcx.mir, &mut v, 4,
-            |w| {
-                let flow = &self.mbcx.flow_state;
-                let entry = flow.interpret_set(flow.sets.on_entry_set_for(i));
-                chunked_present_left(w, &entry[..], chunk_size)?;
-                write!(w, "= ENTRY:</td><td {bg}><FONT {face}>{entrybits:?}</FONT></td>\
-                                        <td></td></tr>",
-                       bg = BG_FLOWCONTENT,
-                       face = FACE_MONOSPACE,
-                       entrybits=bits_to_string(flow.sets.on_entry_set_for(i),
-                                                flow.sets.bytes_per_block()))
-            },
-            |w| {
-                let flow = &self.mbcx.flow_state;
-                let gen = flow.interpret_set( flow.sets.gen_set_for(i));
-                let kill = flow.interpret_set(flow.sets.kill_set_for(i));
-                chunked_present_left(w, &gen[..], chunk_size)?;
-                write!(w, " = GEN:</td><td {bg}><FONT {face}>{genbits:?}</FONT></td>\
-                                       <td></td></tr>",
-                       bg = BG_FLOWCONTENT,
-                       face = FACE_MONOSPACE,
-                       genbits=bits_to_string( flow.sets.gen_set_for(i),
-                                               flow.sets.bytes_per_block()))?;
-                write!(w, "<tr><td></td><td {bg} {align}>KILL:</td>\
-                                        <td {bg}><FONT {face}>{killbits:?}</FONT></td>",
-                       bg = BG_FLOWCONTENT,
-                       align = ALIGN_RIGHT,
-                       face = FACE_MONOSPACE,
-                       killbits=bits_to_string(flow.sets.kill_set_for(i),
-                                               flow.sets.bytes_per_block()))?;
-
-                // (chunked_present_right)
-                let mut seen_one = false;
-                for k in kill.chunks(chunk_size) {
-                    if !seen_one {
-                        // continuation of row; this is fourth <td>
-                        write!(w, "<td {bg}>= {kill:?}</td></tr>",
-                               bg = BG_FLOWCONTENT,
-                               kill=k)?;
-                    } else {
-                        // new row, with indent of three <td>'s
-                        write!(w, "<tr><td></td><td></td><td></td><td {bg}>{kill:?}</td></tr>",
-                               bg = BG_FLOWCONTENT,
-                               kill=k)?;
-                    }
-                    seen_one = true;
-                }
-                if !seen_one {
-                    write!(w, "<td {bg}>= []</td></tr>",
-                           bg = BG_FLOWCONTENT)?;
-                }
-
-                Ok(())
-            })
-            .unwrap();
-        dot::LabelText::html(String::from_utf8(v).unwrap())
-    }
-
-    fn node_shape(&self, _n: &Node) -> Option<dot::LabelText> {
-        Some(dot::LabelText::label("none"))
-    }
-}
-
-impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::GraphWalk<'c> for Graph<'c,'b,'a,'tcx> {
-    type Node = Node;
-    type Edge = Edge;
-    fn nodes(&self) -> dot::Nodes<Node> {
-        self.mbcx.mir.all_basic_blocks().into_cow()
-    }
-
-    fn edges(&self) -> dot::Edges<Edge> {
-        let mir = self.mbcx.mir;
-        let blocks = self.mbcx.mir.all_basic_blocks();
-        // base initial capacity on assumption every block has at
-        // least one outgoing edge (Which should be true for all
-        // blocks but one, the exit-block).
-        let mut edges = Vec::with_capacity(blocks.len());
-        for bb in blocks {
-            let outgoing = outgoing(mir, bb);
-            edges.extend(outgoing.into_iter());
-        }
-        edges.into_cow()
-    }
-
-    fn source(&self, edge: &Edge) -> Node {
-        edge.source
-    }
-
-    fn target(&self, edge: &Edge) -> Node {
-        let mir = self.mbcx.mir;
-        mir.basic_block_data(edge.source).terminator().successors()[edge.index]
-    }
-}
index bec5ae03d3d4de8ee80cb9835244f0c8f0273cdb..7481b15685e6991c62b185e00be446523a952026 100644 (file)
 
 use borrowck::BorrowckCtxt;
 
-use syntax::ast;
-use syntax::codemap::Span;
+use syntax::ast::{self, MetaItem};
+use syntax::attr::AttrMetaMethods;
+use syntax::ptr::P;
+use syntax_pos::{Span, DUMMY_SP};
 
 use rustc::hir;
 use rustc::hir::intravisit::{FnKind};
 
+use rustc::mir::repr;
 use rustc::mir::repr::{BasicBlock, BasicBlockData, Mir, Statement, Terminator};
+use rustc::session::Session;
+use rustc::ty::{self, TyCtxt};
 
 mod abs_domain;
+pub mod elaborate_drops;
 mod dataflow;
 mod gather_moves;
-mod graphviz;
+mod patch;
+// mod graphviz;
 
-use self::dataflow::{Dataflow, DataflowState};
-use self::gather_moves::{MoveData};
+use self::dataflow::{BitDenotation};
+use self::dataflow::{DataflowOperator};
+use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults};
+use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
+use self::dataflow::{DefinitelyInitializedLvals};
+use self::gather_moves::{MoveData, MovePathIndex, Location};
+use self::gather_moves::{MovePathContent, MovePathData};
 
-pub fn borrowck_mir<'b, 'a: 'b, 'tcx: 'a>(
-    bcx: &'b mut BorrowckCtxt<'a, 'tcx>,
+fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<P<MetaItem>> {
+    for attr in attrs {
+        if attr.check_name("rustc_mir") {
+            let items = attr.meta_item_list();
+            for item in items.iter().flat_map(|l| l.iter()) {
+                if item.check_name(name) {
+                    return Some(item.clone())
+                }
+            }
+        }
+    }
+    return None;
+}
+
+pub struct MoveDataParamEnv<'tcx> {
+    move_data: MoveData<'tcx>,
+    param_env: ty::ParameterEnvironment<'tcx>,
+}
+
+pub fn borrowck_mir<'a, 'tcx: 'a>(
+    bcx: &mut BorrowckCtxt<'a, 'tcx>,
     fk: FnKind,
     _decl: &hir::FnDecl,
     mir: &'a Mir<'tcx>,
@@ -45,35 +76,112 @@ pub fn borrowck_mir<'b, 'a: 'b, 'tcx: 'a>(
         }
     }
 
+    let tcx = bcx.tcx;
+
+    let move_data = MoveData::gather_moves(mir, tcx);
+    let param_env = ty::ParameterEnvironment::for_item(tcx, id);
+    let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
+    let flow_inits =
+        do_dataflow(tcx, mir, id, attributes, &mdpe, MaybeInitializedLvals::new(tcx, mir));
+    let flow_uninits =
+        do_dataflow(tcx, mir, id, attributes, &mdpe, MaybeUninitializedLvals::new(tcx, mir));
+    let flow_def_inits =
+        do_dataflow(tcx, mir, id, attributes, &mdpe, DefinitelyInitializedLvals::new(tcx, mir));
+
+    if has_rustc_mir_with(attributes, "rustc_peek_maybe_init").is_some() {
+        dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &mdpe, &flow_inits);
+    }
+    if has_rustc_mir_with(attributes, "rustc_peek_maybe_uninit").is_some() {
+        dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &mdpe, &flow_uninits);
+    }
+    if has_rustc_mir_with(attributes, "rustc_peek_definite_init").is_some() {
+        dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &mdpe, &flow_def_inits);
+    }
+
+    if has_rustc_mir_with(attributes, "stop_after_dataflow").is_some() {
+        bcx.tcx.sess.fatal("stop_after_dataflow ended compilation");
+    }
+
     let mut mbcx = MirBorrowckCtxt {
-        flow_state: DataflowState::new_move_analysis(mir, bcx.tcx),
         bcx: bcx,
         mir: mir,
         node_id: id,
-        attributes: attributes,
+        move_data: mdpe.move_data,
+        flow_inits: flow_inits,
+        flow_uninits: flow_uninits,
     };
 
-    for bb in mir.all_basic_blocks() {
+    for bb in mir.basic_blocks().indices() {
         mbcx.process_basic_block(bb);
     }
 
-    mbcx.dataflow();
-
     debug!("borrowck_mir done");
 }
 
+fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                             mir: &Mir<'tcx>,
+                             node_id: ast::NodeId,
+                             attributes: &[ast::Attribute],
+                             ctxt: &BD::Ctxt,
+                             bd: BD) -> DataflowResults<BD>
+    where BD: BitDenotation<Idx=MovePathIndex, Ctxt=MoveDataParamEnv<'tcx>> + DataflowOperator
+{
+    use syntax::attr::AttrMetaMethods;
+
+    let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
+        if let Some(item) = has_rustc_mir_with(attrs, name) {
+            if let Some(s) = item.value_str() {
+                return Some(s.to_string())
+            } else {
+                sess.span_err(
+                    item.span,
+                    &format!("{} attribute requires a path", item.name()));
+                return None;
+            }
+        }
+        return None;
+    };
+
+    let print_preflow_to =
+        name_found(tcx.sess, attributes, "borrowck_graphviz_preflow");
+    let print_postflow_to =
+        name_found(tcx.sess, attributes, "borrowck_graphviz_postflow");
+
+    let mut mbcx = MirBorrowckCtxtPreDataflow {
+        node_id: node_id,
+        print_preflow_to: print_preflow_to,
+        print_postflow_to: print_postflow_to,
+        flow_state: DataflowAnalysis::new(tcx, mir, ctxt, bd),
+    };
+
+    mbcx.dataflow(|ctxt, i| &ctxt.move_data.move_paths[i]);
+    mbcx.flow_state.results()
+}
+
+
+pub struct MirBorrowckCtxtPreDataflow<'a, 'tcx: 'a, BD>
+    where BD: BitDenotation, BD::Ctxt: 'a
+{
+    node_id: ast::NodeId,
+    flow_state: DataflowAnalysis<'a, 'tcx, BD>,
+    print_preflow_to: Option<String>,
+    print_postflow_to: Option<String>,
+}
+
+#[allow(dead_code)]
 pub struct MirBorrowckCtxt<'b, 'a: 'b, 'tcx: 'a> {
     bcx: &'b mut BorrowckCtxt<'a, 'tcx>,
     mir: &'b Mir<'tcx>,
     node_id: ast::NodeId,
-    attributes: &'b [ast::Attribute],
-    flow_state: DataflowState<MoveData<'tcx>>,
+    move_data: MoveData<'tcx>,
+    flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx>>,
+    flow_uninits: DataflowResults<MaybeUninitializedLvals<'a, 'tcx>>
 }
 
 impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
     fn process_basic_block(&mut self, bb: BasicBlock) {
-        let &BasicBlockData { ref statements, ref terminator, is_cleanup: _ } =
-            self.mir.basic_block_data(bb);
+        let BasicBlockData { ref statements, ref terminator, is_cleanup: _ } =
+            self.mir[bb];
         for stmt in statements {
             self.process_statement(bb, stmt);
         }
@@ -89,3 +197,197 @@ impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
         debug!("MirBorrowckCtxt::process_terminator({:?}, {:?})", bb, term);
     }
 }
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+enum DropFlagState {
+    Present, // i.e. initialized
+    Absent, // i.e. deinitialized or "moved"
+}
+
+impl DropFlagState {
+    fn value(self) -> bool {
+        match self {
+            DropFlagState::Present => true,
+            DropFlagState::Absent => false
+        }
+    }
+}
+
+fn move_path_children_matching<'tcx, F>(move_paths: &MovePathData<'tcx>,
+                                        path: MovePathIndex,
+                                        mut cond: F)
+                                        -> Option<MovePathIndex>
+    where F: FnMut(&repr::LvalueProjection<'tcx>) -> bool
+{
+    let mut next_child = move_paths[path].first_child;
+    while let Some(child_index) = next_child {
+        match move_paths[child_index].content {
+            MovePathContent::Lvalue(repr::Lvalue::Projection(ref proj)) => {
+                if cond(proj) {
+                    return Some(child_index)
+                }
+            }
+            _ => {}
+        }
+        next_child = move_paths[child_index].next_sibling;
+    }
+
+    None
+}
+
+/// When enumerating the child fragments of a path, don't recurse into
+/// paths (1.) past arrays, slices, and pointers, nor (2.) into a type
+/// that implements `Drop`.
+///
+/// Lvalues behind references or arrays are not tracked by elaboration
+/// and are always assumed to be initialized when accessible. As
+/// references and indexes can be reseated, trying to track them can
+/// only lead to trouble.
+///
+/// Lvalues behind ADT's with a Drop impl are not tracked by
+/// elaboration since they can never have a drop-flag state that
+/// differs from that of the parent with the Drop impl.
+///
+/// In both cases, the contents can only be accessed if and only if
+/// their parents are initialized. This implies for example that there
+/// is no need to maintain separate drop flags to track such state.
+///
+/// FIXME: we have to do something for moving slice patterns.
+fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                                      mir: &Mir<'tcx>,
+                                                      lv: &repr::Lvalue<'tcx>) -> bool {
+    let ty = mir.lvalue_ty(tcx, lv).to_ty(tcx);
+    match ty.sty {
+        ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => {
+            debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} refd => false",
+                   lv, ty);
+            true
+        }
+        ty::TyStruct(def, _) | ty::TyEnum(def, _) if def.has_dtor() => {
+            debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => false",
+                   lv, ty);
+            true
+        }
+        _ => {
+            false
+        }
+    }
+}
+
+fn on_all_children_bits<'a, 'tcx, F>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &Mir<'tcx>,
+    move_data: &MoveData<'tcx>,
+    move_path_index: MovePathIndex,
+    mut each_child: F)
+    where F: FnMut(MovePathIndex)
+{
+    fn is_terminal_path<'a, 'tcx>(
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        mir: &Mir<'tcx>,
+        move_data: &MoveData<'tcx>,
+        path: MovePathIndex) -> bool
+    {
+        match move_data.move_paths[path].content {
+            MovePathContent::Lvalue(ref lvalue) => {
+                lvalue_contents_drop_state_cannot_differ(tcx, mir, lvalue)
+            }
+            _ => true
+        }
+    }
+
+    fn on_all_children_bits<'a, 'tcx, F>(
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        mir: &Mir<'tcx>,
+        move_data: &MoveData<'tcx>,
+        move_path_index: MovePathIndex,
+        each_child: &mut F)
+        where F: FnMut(MovePathIndex)
+    {
+        each_child(move_path_index);
+
+        if is_terminal_path(tcx, mir, move_data, move_path_index) {
+            return
+        }
+
+        let mut next_child_index = move_data.move_paths[move_path_index].first_child;
+        while let Some(child_index) = next_child_index {
+            on_all_children_bits(tcx, mir, move_data, child_index, each_child);
+            next_child_index = move_data.move_paths[child_index].next_sibling;
+        }
+    }
+    on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child);
+}
+
+fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &Mir<'tcx>,
+    ctxt: &MoveDataParamEnv<'tcx>,
+    mut callback: F)
+    where F: FnMut(MovePathIndex, DropFlagState)
+{
+    let move_data = &ctxt.move_data;
+    for (arg, _) in mir.arg_decls.iter_enumerated() {
+        let lvalue = repr::Lvalue::Arg(arg);
+        let move_path_index = move_data.rev_lookup.find(&lvalue);
+        on_all_children_bits(tcx, mir, move_data,
+                             move_path_index,
+                             |moi| callback(moi, DropFlagState::Present));
+    }
+}
+
+fn drop_flag_effects_for_location<'a, 'tcx, F>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &Mir<'tcx>,
+    ctxt: &MoveDataParamEnv<'tcx>,
+    loc: Location,
+    mut callback: F)
+    where F: FnMut(MovePathIndex, DropFlagState)
+{
+    let move_data = &ctxt.move_data;
+    let param_env = &ctxt.param_env;
+    debug!("drop_flag_effects_for_location({:?})", loc);
+
+    // first, move out of the RHS
+    for mi in &move_data.loc_map[loc] {
+        let path = mi.move_path_index(move_data);
+        debug!("moving out of path {:?}", move_data.move_paths[path]);
+
+        // don't move out of non-Copy things
+        if let MovePathContent::Lvalue(ref lvalue) = move_data.move_paths[path].content {
+            let ty = mir.lvalue_ty(tcx, lvalue).to_ty(tcx);
+            if !ty.moves_by_default(tcx, param_env, DUMMY_SP) {
+                continue;
+            }
+        }
+
+        on_all_children_bits(tcx, mir, move_data,
+                             path,
+                             |moi| callback(moi, DropFlagState::Absent))
+    }
+
+    let block = &mir[loc.block];
+    match block.statements.get(loc.index) {
+        Some(stmt) => match stmt.kind {
+            repr::StatementKind::Assign(ref lvalue, _) => {
+                debug!("drop_flag_effects: assignment {:?}", stmt);
+                 on_all_children_bits(tcx, mir, move_data,
+                                     move_data.rev_lookup.find(lvalue),
+                                     |moi| callback(moi, DropFlagState::Present))
+            }
+        },
+        None => {
+            debug!("drop_flag_effects: replace {:?}", block.terminator());
+            match block.terminator().kind {
+                repr::TerminatorKind::DropAndReplace { ref location, .. } => {
+                    on_all_children_bits(tcx, mir, move_data,
+                                         move_data.rev_lookup.find(location),
+                                         |moi| callback(moi, DropFlagState::Present))
+                }
+                _ => {
+                    // other terminators do not contain move-ins
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc_borrowck/borrowck/mir/patch.rs b/src/librustc_borrowck/borrowck/mir/patch.rs
new file mode 100644 (file)
index 0000000..417e719
--- /dev/null
@@ -0,0 +1,179 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::gather_moves::Location;
+use rustc::ty::Ty;
+use rustc::mir::repr::*;
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+
+/// This struct represents a patch to MIR, which can add
+/// new statements and basic blocks and patch over block
+/// terminators.
+pub struct MirPatch<'tcx> {
+    patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>,
+    new_blocks: Vec<BasicBlockData<'tcx>>,
+    new_statements: Vec<(Location, StatementKind<'tcx>)>,
+    new_temps: Vec<TempDecl<'tcx>>,
+    resume_block: BasicBlock,
+    next_temp: usize,
+}
+
+impl<'tcx> MirPatch<'tcx> {
+    pub fn new(mir: &Mir<'tcx>) -> Self {
+        let mut result = MirPatch {
+            patch_map: IndexVec::from_elem(None, mir.basic_blocks()),
+            new_blocks: vec![],
+            new_temps: vec![],
+            new_statements: vec![],
+            next_temp: mir.temp_decls.len(),
+            resume_block: START_BLOCK
+        };
+
+        // make sure the MIR we create has a resume block. It is
+        // completely legal to convert jumps to the resume block
+        // to jumps to None, but we occasionally have to add
+        // instructions just before that.
+
+        let mut resume_block = None;
+        let mut resume_stmt_block = None;
+        for (bb, block) in mir.basic_blocks().iter_enumerated() {
+            if let TerminatorKind::Resume = block.terminator().kind {
+                if block.statements.len() > 0 {
+                    resume_stmt_block = Some(bb);
+                } else {
+                    resume_block = Some(bb);
+                }
+                break
+            }
+        }
+        let resume_block = resume_block.unwrap_or_else(|| {
+            result.new_block(BasicBlockData {
+                statements: vec![],
+                terminator: Some(Terminator {
+                    source_info: SourceInfo {
+                        span: mir.span,
+                        scope: ARGUMENT_VISIBILITY_SCOPE
+                    },
+                    kind: TerminatorKind::Resume
+                }),
+                is_cleanup: true
+            })});
+        result.resume_block = resume_block;
+        if let Some(resume_stmt_block) = resume_stmt_block {
+            result.patch_terminator(resume_stmt_block, TerminatorKind::Goto {
+                target: resume_block
+            });
+        }
+        result
+    }
+
+    pub fn resume_block(&self) -> BasicBlock {
+        self.resume_block
+    }
+
+    pub fn is_patched(&self, bb: BasicBlock) -> bool {
+        self.patch_map[bb].is_some()
+    }
+
+    pub fn terminator_loc(&self, mir: &Mir<'tcx>, bb: BasicBlock) -> Location {
+        let offset = match bb.index().checked_sub(mir.basic_blocks().len()) {
+            Some(index) => self.new_blocks[index].statements.len(),
+            None => mir[bb].statements.len()
+        };
+        Location {
+            block: bb,
+            index: offset
+        }
+    }
+
+    pub fn new_temp(&mut self, ty: Ty<'tcx>) -> Temp {
+        let index = self.next_temp;
+        self.next_temp += 1;
+        self.new_temps.push(TempDecl { ty: ty });
+        Temp::new(index as usize)
+    }
+
+    pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
+        let block = BasicBlock::new(self.patch_map.len());
+        debug!("MirPatch: new_block: {:?}: {:?}", block, data);
+        self.new_blocks.push(data);
+        self.patch_map.push(None);
+        block
+    }
+
+    pub fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) {
+        assert!(self.patch_map[block].is_none());
+        debug!("MirPatch: patch_terminator({:?}, {:?})", block, new);
+        self.patch_map[block] = Some(new);
+    }
+
+    pub fn add_statement(&mut self, loc: Location, stmt: StatementKind<'tcx>) {
+        debug!("MirPatch: add_statement({:?}, {:?})", loc, stmt);
+        self.new_statements.push((loc, stmt));
+    }
+
+    pub fn add_assign(&mut self, loc: Location, lv: Lvalue<'tcx>, rv: Rvalue<'tcx>) {
+        self.add_statement(loc, StatementKind::Assign(lv, rv));
+    }
+
+    pub fn apply(self, mir: &mut Mir<'tcx>) {
+        debug!("MirPatch: {:?} new temps, starting from index {}: {:?}",
+               self.new_temps.len(), mir.temp_decls.len(), self.new_temps);
+        debug!("MirPatch: {} new blocks, starting from index {}",
+               self.new_blocks.len(), mir.basic_blocks().len());
+        mir.basic_blocks_mut().extend(self.new_blocks);
+        mir.temp_decls.extend(self.new_temps);
+        for (src, patch) in self.patch_map.into_iter_enumerated() {
+            if let Some(patch) = patch {
+                debug!("MirPatch: patching block {:?}", src);
+                mir[src].terminator_mut().kind = patch;
+            }
+        }
+
+        let mut new_statements = self.new_statements;
+        new_statements.sort_by(|u,v| u.0.cmp(&v.0));
+
+        let mut delta = 0;
+        let mut last_bb = START_BLOCK;
+        for (mut loc, stmt) in new_statements {
+            if loc.block != last_bb {
+                delta = 0;
+                last_bb = loc.block;
+            }
+            debug!("MirPatch: adding statement {:?} at loc {:?}+{}",
+                   stmt, loc, delta);
+            loc.index += delta;
+            let source_info = Self::source_info_for_index(
+                &mir[loc.block], loc
+            );
+            mir[loc.block].statements.insert(
+                loc.index, Statement {
+                    source_info: source_info,
+                    kind: stmt
+                });
+            delta += 1;
+        }
+    }
+
+    pub fn source_info_for_index(data: &BasicBlockData, loc: Location) -> SourceInfo {
+        match data.statements.get(loc.index) {
+            Some(stmt) => stmt.source_info,
+            None => data.terminator().source_info
+        }
+    }
+
+    pub fn source_info_for_location(&self, mir: &Mir, loc: Location) -> SourceInfo {
+        let data = match loc.block.index().checked_sub(mir.basic_blocks().len()) {
+            Some(new) => &self.new_blocks[new],
+            None => &mir[loc.block]
+        };
+        Self::source_info_for_index(data, loc)
+    }
+}
index 36222e172b8d61d431745aff431853b6018984ef..e86120b73bf97670bf59c2b7496ae76740bf4c2f 100644 (file)
@@ -18,6 +18,8 @@ pub use self::bckerr_code::*;
 pub use self::AliasableViolationKind::*;
 pub use self::MovedValueUseKind::*;
 
+pub use self::mir::elaborate_drops::ElaborateDrops;
+
 use self::InteriorKind::*;
 
 use rustc::dep_graph::DepNode;
@@ -41,8 +43,8 @@ use std::mem;
 use std::rc::Rc;
 use syntax::ast;
 use syntax::attr::AttrMetaMethods;
-use syntax::codemap::{MultiSpan, Span};
-use syntax::errors::DiagnosticBuilder;
+use syntax_pos::{MultiSpan, Span};
+use errors::DiagnosticBuilder;
 
 use rustc::hir;
 use rustc::hir::{FnDecl, Block};
@@ -977,7 +979,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                         if let Categorization::Local(local_id) = err.cmt.cat {
                             let span = self.tcx.map.span(local_id);
                             if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
-                                if snippet != "self" {
+                                if snippet.starts_with("ref ") {
+                                    db.span_label(span,
+                                        &format!("use `{}` here to make mutable",
+                                            snippet.replace("ref ", "ref mut ")));
+                                } else if snippet != "self" {
                                     db.span_label(span,
                                         &format!("use `mut {}` here to make mutable", snippet));
                                 }
index a742260018676257766e8e8afa878bc514dc4f40..c9822a4fee7498b4da41c83a2bffe357df515eb5 100644 (file)
@@ -28,7 +28,7 @@ use std::cell::RefCell;
 use std::rc::Rc;
 use std::usize;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::hir;
 use rustc::hir::intravisit::IdRange;
 
@@ -274,11 +274,8 @@ impl<'a, 'tcx> MoveData<'tcx> {
     /// `lp` and any of its base paths that do not yet have an index.
     pub fn move_path(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
                      lp: Rc<LoanPath<'tcx>>) -> MovePathIndex {
-        match self.path_map.borrow().get(&lp) {
-            Some(&index) => {
-                return index;
-            }
-            None => {}
+        if let Some(&index) = self.path_map.borrow().get(&lp) {
+            return index;
         }
 
         let index = match lp.kind {
index 116e3476897ca543e3cb42485c49e115897815a6..400ae186010e3fb15d594615c6b5dc4705e9b395 100644 (file)
@@ -153,7 +153,7 @@ structure that is currently uninitialized.
 
 For example, this can happen when a drop has taken place:
 
-```compile_fail
+```ignore
 struct Foo {
     a: u32,
 }
@@ -516,8 +516,10 @@ fn foo(a: &mut i32) {
             //        as immutable
 }
 ```
+
 To fix this error, ensure that you don't have any other references to the
 variable before trying to access it mutably:
+
 ```
 fn bar(x: &mut i32) {}
 fn foo(a: &mut i32) {
@@ -525,10 +527,67 @@ fn foo(a: &mut i32) {
     let ref y = a; // ok!
 }
 ```
+
 For more information on the rust ownership system, take a look at
 https://doc.rust-lang.org/stable/book/references-and-borrowing.html.
 "##,
 
+E0503: r##"
+A value was used after it was mutably borrowed.
+
+Example of erroneous code:
+
+```compile_fail
+fn main() {
+    let mut value = 3;
+    // Create a mutable borrow of `value`. This borrow
+    // lives until the end of this function.
+    let _borrow = &mut value;
+    let _sum = value + 1; // error: cannot use `value` because
+                          //        it was mutably borrowed
+}
+```
+
+In this example, `value` is mutably borrowed by `borrow` and cannot be
+used to calculate `sum`. This is not possible because this would violate
+Rust's mutability rules.
+
+You can fix this error by limiting the scope of the borrow:
+
+```
+fn main() {
+    let mut value = 3;
+    // By creating a new block, you can limit the scope
+    // of the reference.
+    {
+        let _borrow = &mut value; // Use `_borrow` inside this block.
+    }
+    // The block has ended and with it the borrow.
+    // You can now use `value` again.
+    let _sum = value + 1;
+}
+```
+
+Or by cloning `value` before borrowing it:
+
+```
+fn main() {
+    let mut value = 3;
+    // We clone `value`, creating a copy.
+    let value_cloned = value.clone();
+    // The mutable borrow is a reference to `value` and
+    // not to `value_cloned`...
+    let _borrow = &mut value;
+    // ... which means we can still use `value_cloned`,
+    let _sum = value_cloned + 1;
+    // even though the borrow only ends here.
+}
+```
+
+You can find more information about borrowing in the rust-book:
+http://doc.rust-lang.org/stable/book/references-and-borrowing.html
+"##,
+
 E0504: r##"
 This error occurs when an attempt is made to move a borrowed variable into a
 closure.
@@ -911,6 +970,50 @@ You can find more information about borrowing in the rust-book:
 http://doc.rust-lang.org/stable/book/references-and-borrowing.html
 "##,
 
+E0508: r##"
+A value was moved out of a non-copy fixed-size array.
+
+Example of erroneous code:
+
+```compile_fail
+struct NonCopy;
+
+fn main() {
+    let array = [NonCopy; 1];
+    let _value = array[0]; // error: cannot move out of type `[NonCopy; 1]`,
+                           //        a non-copy fixed-size array
+}
+```
+
+The first element was moved out of the array, but this is not
+possible because `NonCopy` does not implement the `Copy` trait.
+
+Consider borrowing the element instead of moving it:
+
+```
+struct NonCopy;
+
+fn main() {
+    let array = [NonCopy; 1];
+    let _value = &array[0]; // Borrowing is allowed, unlike moving.
+}
+```
+
+Alternatively, if your type implements `Clone` and you need to own the value,
+consider borrowing and then cloning:
+
+```
+#[derive(Clone)]
+struct NonCopy;
+
+fn main() {
+    let array = [NonCopy; 1];
+    // Now you can clone the array element.
+    let _value = array[0].clone();
+}
+```
+"##,
+
 E0509: r##"
 This error occurs when an attempt is made to move out of a value whose type
 implements the `Drop` trait.
@@ -1011,7 +1114,5 @@ fn main() {
 register_diagnostics! {
     E0385, // {} in an aliasable location
     E0388, // {} in a static location
-    E0503, // cannot use `..` because it was mutably borrowed
-    E0508, // cannot move out of type `..`, a non-copy fixed-size array
     E0524, // two closures require unique access to `..` at the same time
 }
diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs
new file mode 100644 (file)
index 0000000..671aff9
--- /dev/null
@@ -0,0 +1,159 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// FIXME: move this to `rustc_data_structures`
+
+use std::fmt;
+use std::marker::PhantomData;
+use std::mem;
+use std::ops::{Deref, DerefMut, Range};
+use bitslice::{BitSlice, Word};
+use bitslice::{bitwise, Union, Subtract};
+
+use rustc_data_structures::indexed_vec::Idx;
+
+/// Represents a set (or packed family of sets), of some element type
+/// E, where each E is identified by some unique index type `T`.
+///
+/// In other words, `T` is the type used to index into the bitvector
+/// this type uses to represent the set of object it holds.
+pub struct IdxSetBuf<T: Idx> {
+    _pd: PhantomData<fn(&T)>,
+    bits: Vec<Word>,
+}
+
+impl<T: Idx> Clone for IdxSetBuf<T> {
+    fn clone(&self) -> Self {
+        IdxSetBuf { _pd: PhantomData, bits: self.bits.clone() }
+    }
+}
+
+// pnkfelix wants to have this be `IdxSet<T>([Word]) and then pass
+// around `&mut IdxSet<T>` or `&IdxSet<T>`.
+//
+// WARNING: Mapping a `&IdxSetBuf<T>` to `&IdxSet<T>` (at least today)
+// requires a transmute relying on representation guarantees that may
+// not hold in the future.
+
+/// Represents a set (or packed family of sets), of some element type
+/// E, where each E is identified by some unique index type `T`.
+///
+/// In other words, `T` is the type used to index into the bitslice
+/// this type uses to represent the set of object it holds.
+pub struct IdxSet<T: Idx> {
+    _pd: PhantomData<fn(&T)>,
+    bits: [Word],
+}
+
+impl<T: Idx> fmt::Debug for IdxSetBuf<T> {
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) }
+}
+
+impl<T: Idx> fmt::Debug for IdxSet<T> {
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) }
+}
+
+impl<T: Idx> IdxSetBuf<T> {
+    fn new(init: Word, universe_size: usize) -> Self {
+        let bits_per_word = mem::size_of::<Word>() * 8;
+        let num_words = (universe_size + (bits_per_word - 1)) / bits_per_word;
+        IdxSetBuf {
+            _pd: Default::default(),
+            bits: vec![init; num_words],
+        }
+    }
+
+    /// Creates set holding every element whose index falls in range 0..universe_size.
+    pub fn new_filled(universe_size: usize) -> Self {
+        Self::new(!0, universe_size)
+    }
+
+    /// Creates set holding no elements.
+    pub fn new_empty(universe_size: usize) -> Self {
+        Self::new(0, universe_size)
+    }
+}
+
+impl<T: Idx> IdxSet<T> {
+    unsafe fn from_slice(s: &[Word]) -> &Self {
+        mem::transmute(s) // (see above WARNING)
+    }
+
+    unsafe fn from_slice_mut(s: &mut [Word]) -> &mut Self {
+        mem::transmute(s) // (see above WARNING)
+    }
+}
+
+impl<T: Idx> Deref for IdxSetBuf<T> {
+    type Target = IdxSet<T>;
+    fn deref(&self) -> &IdxSet<T> {
+        unsafe { IdxSet::from_slice(&self.bits[..]) }
+    }
+}
+
+impl<T: Idx> DerefMut for IdxSetBuf<T> {
+    fn deref_mut(&mut self) -> &mut IdxSet<T> {
+        unsafe { IdxSet::from_slice_mut(&mut self.bits[..]) }
+    }
+}
+
+impl<T: Idx> IdxSet<T> {
+    pub fn to_owned(&self) -> IdxSetBuf<T> {
+        IdxSetBuf {
+            _pd: Default::default(),
+            bits: self.bits.to_owned(),
+        }
+    }
+
+    /// Removes `elem` from the set `self`; returns true iff this changed `self`.
+    pub fn remove(&mut self, elem: &T) -> bool {
+        self.bits.clear_bit(elem.index())
+    }
+
+    /// Adds `elem` to the set `self`; returns true iff this changed `self`.
+    pub fn add(&mut self, elem: &T) -> bool {
+        self.bits.set_bit(elem.index())
+    }
+
+    pub fn range(&self, elems: &Range<T>) -> &Self {
+        let elems = elems.start.index()..elems.end.index();
+        unsafe { Self::from_slice(&self.bits[elems]) }
+    }
+
+    pub fn range_mut(&mut self, elems: &Range<T>) -> &mut Self {
+        let elems = elems.start.index()..elems.end.index();
+        unsafe { Self::from_slice_mut(&mut self.bits[elems]) }
+    }
+
+    /// Returns true iff set `self` contains `elem`.
+    pub fn contains(&self, elem: &T) -> bool {
+        self.bits.get_bit(elem.index())
+    }
+
+    pub fn words(&self) -> &[Word] {
+        &self.bits[..]
+    }
+
+    pub fn words_mut(&mut self) -> &mut [Word] {
+        &mut self.bits[..]
+    }
+
+    pub fn clone_from(&mut self, other: &IdxSet<T>) {
+        self.words_mut().clone_from_slice(other.words());
+    }
+
+    pub fn union(&mut self, other: &IdxSet<T>) -> bool {
+        bitwise(self.words_mut(), other.words(), &Union)
+    }
+
+    pub fn subtract(&mut self, other: &IdxSet<T>) -> bool {
+        bitwise(self.words_mut(), other.words(), &Subtract)
+    }
+}
index e38677de6625fc46a97eb015d011f76967d8e0d7..16fefee347269318b59f96f326c0f74bf847cd11 100644 (file)
 #![feature(question_mark)]
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
+extern crate syntax_pos;
+extern crate rustc_errors as errors;
 
 // for "clarity", rename the graphviz crate to dot; graphviz within `borrowck`
 // refers to the borrowck-specific graphviz adapter traits.
 extern crate graphviz as dot;
 #[macro_use]
 extern crate rustc;
+extern crate rustc_data_structures;
 extern crate rustc_mir;
 extern crate core; // for NonZero
 
 pub use borrowck::check_crate;
 pub use borrowck::build_borrowck_dataflow_data_for_fn;
-pub use borrowck::{AnalysisData, BorrowckCtxt};
+pub use borrowck::{AnalysisData, BorrowckCtxt, ElaborateDrops};
 
 // NB: This module needs to be declared first so diagnostics are
 // registered before they are used.
@@ -47,6 +50,7 @@ pub mod diagnostics;
 
 mod borrowck;
 mod bitslice;
+mod indexed_set;
 
 pub mod graphviz;
 
index c572284a6bb376c2acd5311d38df8838c18edfb5..01872bbe3c04996a87089b757b494c253dd42c3d 100644 (file)
@@ -16,3 +16,4 @@ rustc_back = { path = "../librustc_back" }
 rustc_const_math = { path = "../librustc_const_math" }
 syntax = { path = "../libsyntax" }
 graphviz = { path = "../libgraphviz" }
+syntax_pos = { path = "../libsyntax_pos" }
\ No newline at end of file
index 2fb5d796589b29ae06615cbec1374c6ed63c9f11..e4a2988492d120dd9b8f926950e9946b3a112f77 100644 (file)
@@ -37,11 +37,13 @@ use rustc::hir::intravisit::{self, IdVisitor, IdVisitingOperation, Visitor, FnKi
 use rustc_back::slice;
 
 use syntax::ast::{self, DUMMY_NODE_ID, NodeId};
-use syntax::codemap::{Span, Spanned, DUMMY_SP};
+use syntax::codemap::Spanned;
+use syntax_pos::{Span, DUMMY_SP};
 use rustc::hir::fold::{Folder, noop_fold_pat};
 use rustc::hir::print::pat_to_string;
 use syntax::ptr::P;
 use rustc::util::nodemap::FnvHashMap;
+use rustc::util::common::slice_pat;
 
 pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
     id: DUMMY_NODE_ID,
@@ -49,7 +51,7 @@ pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
     span: DUMMY_SP
 };
 
-struct Matrix<'a>(Vec<Vec<&'a Pat>>);
+struct Matrix<'a, 'tcx>(Vec<Vec<(&'a Pat, Option<Ty<'tcx>>)>>);
 
 /// Pretty-printer for matrices of patterns, example:
 /// ++++++++++++++++++++++++++
@@ -63,14 +65,14 @@ struct Matrix<'a>(Vec<Vec<&'a Pat>>);
 /// ++++++++++++++++++++++++++
 /// + _     + [_, _, ..tail] +
 /// ++++++++++++++++++++++++++
-impl<'a> fmt::Debug for Matrix<'a> {
+impl<'a, 'tcx> fmt::Debug for Matrix<'a, 'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "\n")?;
 
         let &Matrix(ref m) = self;
         let pretty_printed_matrix: Vec<Vec<String>> = m.iter().map(|row| {
             row.iter()
-               .map(|&pat| pat_to_string(&pat))
+               .map(|&(pat,ty)| format!("{}: {:?}", pat_to_string(&pat), ty))
                .collect::<Vec<String>>()
         }).collect();
 
@@ -97,8 +99,10 @@ impl<'a> fmt::Debug for Matrix<'a> {
     }
 }
 
-impl<'a> FromIterator<Vec<&'a Pat>> for Matrix<'a> {
-    fn from_iter<T: IntoIterator<Item=Vec<&'a Pat>>>(iter: T) -> Matrix<'a> {
+impl<'a, 'tcx> FromIterator<Vec<(&'a Pat, Option<Ty<'tcx>>)>> for Matrix<'a, 'tcx> {
+    fn from_iter<T: IntoIterator<Item=Vec<(&'a Pat, Option<Ty<'tcx>>)>>>(iter: T)
+                                                                         -> Self
+    {
         Matrix(iter.into_iter().collect())
     }
 }
@@ -109,7 +113,7 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
     pub param_env: ParameterEnvironment<'tcx>,
 }
 
-#[derive(Clone, PartialEq)]
+#[derive(Clone, Debug, PartialEq)]
 pub enum Constructor {
     /// The constructor of all patterns that don't vary by constructor,
     /// e.g. struct patterns and fixed-length arrays.
@@ -172,9 +176,8 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
 
                 // Second, if there is a guard on each arm, make sure it isn't
                 // assigning or borrowing anything mutably.
-                match arm.guard {
-                    Some(ref guard) => check_for_mutation_in_guard(cx, &guard),
-                    None => {}
+                if let Some(ref guard) = arm.guard {
+                    check_for_mutation_in_guard(cx, &guard);
                 }
             }
 
@@ -229,7 +232,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
                 .iter()
                 .filter(|&&(_, guard)| guard.is_none())
                 .flat_map(|arm| &arm.0)
-                .map(|pat| vec![&**pat])
+                .map(|pat| vec![wrap_pat(cx, &pat)])
                 .collect();
             check_exhaustive(cx, ex.span, &matrix, source);
         },
@@ -239,31 +242,26 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
 
 fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat) {
     pat.walk(|p| {
-        match p.node {
-            PatKind::Ident(hir::BindByValue(hir::MutImmutable), name, None) => {
-                let pat_ty = cx.tcx.pat_ty(p);
-                if let ty::TyEnum(edef, _) = pat_ty.sty {
-                    let def = cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def());
-                    if let Some(Def::Local(..)) = def {
-                        if edef.variants.iter().any(|variant|
-                            variant.name == name.node.unhygienize()
-                                && variant.kind() == VariantKind::Unit
-                        ) {
-                            let ty_path = cx.tcx.item_path_str(edef.did);
-                            let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170,
-                                "pattern binding `{}` is named the same as one \
-                                 of the variants of the type `{}`",
-                                name.node, ty_path);
-                            help!(err,
-                                "if you meant to match on a variant, \
-                                 consider making the path in the pattern qualified: `{}::{}`",
-                                ty_path, name.node);
-                            err.emit();
-                        }
+        if let PatKind::Binding(hir::BindByValue(hir::MutImmutable), name, None) = p.node {
+            let pat_ty = cx.tcx.pat_ty(p);
+            if let ty::TyEnum(edef, _) = pat_ty.sty {
+                if let Def::Local(..) = cx.tcx.expect_def(p.id) {
+                    if edef.variants.iter().any(|variant|
+                        variant.name == name.node && variant.kind() == VariantKind::Unit
+                    ) {
+                        let ty_path = cx.tcx.item_path_str(edef.did);
+                        let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170,
+                            "pattern binding `{}` is named the same as one \
+                             of the variants of the type `{}`",
+                            name.node, ty_path);
+                        help!(err,
+                            "if you meant to match on a variant, \
+                             consider making the path in the pattern qualified: `{}::{}`",
+                            ty_path, name.node);
+                        err.emit();
                     }
                 }
             }
-            _ => ()
         }
         true
     });
@@ -304,7 +302,7 @@ fn check_arms(cx: &MatchCheckCtxt,
     let mut printed_if_let_err = false;
     for &(ref pats, guard) in arms {
         for pat in pats {
-            let v = vec![&**pat];
+            let v = vec![wrap_pat(cx, &pat)];
 
             match is_useful(cx, &seen, &v[..], LeaveOutWitness) {
                 NotUseful => {
@@ -344,8 +342,9 @@ fn check_arms(cx: &MatchCheckCtxt,
                                                            "unreachable pattern");
                             // if we had a catchall pattern, hint at that
                             for row in &seen.0 {
-                                if pat_is_catchall(&cx.tcx.def_map.borrow(), row[0]) {
-                                    span_note!(err, row[0].span, "this pattern matches any value");
+                                if pat_is_catchall(&cx.tcx.def_map.borrow(), row[0].0) {
+                                    span_note!(err, row[0].0.span,
+                                               "this pattern matches any value");
                                 }
                             }
                             err.emit();
@@ -371,35 +370,38 @@ fn check_arms(cx: &MatchCheckCtxt,
 /// Checks for common cases of "catchall" patterns that may not be intended as such.
 fn pat_is_catchall(dm: &DefMap, p: &Pat) -> bool {
     match p.node {
-        PatKind::Ident(_, _, None) => pat_is_binding(dm, p),
-        PatKind::Ident(_, _, Some(ref s)) => pat_is_catchall(dm, &s),
+        PatKind::Binding(_, _, None) => true,
+        PatKind::Binding(_, _, Some(ref s)) => pat_is_catchall(dm, &s),
         PatKind::Ref(ref s, _) => pat_is_catchall(dm, &s),
-        PatKind::Tup(ref v) => v.iter().all(|p| pat_is_catchall(dm, &p)),
+        PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(dm, &p)),
         _ => false
     }
 }
 
 fn raw_pat(p: &Pat) -> &Pat {
     match p.node {
-        PatKind::Ident(_, _, Some(ref s)) => raw_pat(&s),
+        PatKind::Binding(_, _, Some(ref s)) => raw_pat(&s),
         _ => p
     }
 }
 
-fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix, source: hir::MatchSource) {
-    match is_useful(cx, matrix, &[DUMMY_WILD_PAT], ConstructWitness) {
+fn check_exhaustive<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
+                              sp: Span,
+                              matrix: &Matrix<'a, 'tcx>,
+                              source: hir::MatchSource) {
+    match is_useful(cx, matrix, &[(DUMMY_WILD_PAT, None)], ConstructWitness) {
         UsefulWithWitness(pats) => {
             let witnesses = if pats.is_empty() {
                 vec![DUMMY_WILD_PAT]
             } else {
-                pats.iter().map(|w| &**w ).collect()
+                pats.iter().map(|w| &**w).collect()
             };
             match source {
                 hir::MatchSource::ForLoopDesugar => {
                     // `witnesses[0]` has the form `Some(<head>)`, peel off the `Some`
                     let witness = match witnesses[0].node {
-                        PatKind::TupleStruct(_, Some(ref pats)) => match &pats[..] {
-                            [ref pat] => &**pat,
+                        PatKind::TupleStruct(_, ref pats, _) => match slice_pat(&&pats[..]) {
+                            &[ref pat] => &**pat,
                             _ => bug!(),
                         },
                         _ => bug!(),
@@ -449,7 +451,7 @@ fn const_val_to_expr(value: &ConstVal) -> P<hir::Expr> {
         id: 0,
         node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })),
         span: DUMMY_SP,
-        attrs: None,
+        attrs: ast::ThinVec::new(),
     })
 }
 
@@ -487,11 +489,9 @@ impl<'map> IdVisitingOperation for RenamingRecorder<'map> {
 impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
     fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
         return match pat.node {
-            PatKind::Ident(..) | PatKind::Path(..) | PatKind::QPath(..) => {
-                let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def());
-                match def {
-                    Some(Def::AssociatedConst(did)) |
-                    Some(Def::Const(did)) => {
+            PatKind::Path(..) | PatKind::QPath(..) => {
+                match self.tcx.expect_def(pat.id) {
+                    Def::AssociatedConst(did) | Def::Const(did) => {
                         let substs = Some(self.tcx.node_id_item_substs(pat.id).substs);
                         if let Some((const_expr, _)) = lookup_const_by_id(self.tcx, did, substs) {
                             match const_expr_to_pat(self.tcx, const_expr, pat.id, pat.span) {
@@ -559,7 +559,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
     let pats_len = pats.len();
     let mut pats = pats.into_iter().map(|p| P((*p).clone()));
     let pat = match left_ty.sty {
-        ty::TyTuple(_) => PatKind::Tup(pats.collect()),
+        ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),
 
         ty::TyEnum(adt, _) | ty::TyStruct(adt, _)  => {
             let v = ctor.variant_for_adt(adt);
@@ -580,7 +580,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
                     PatKind::Struct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields)
                 }
                 VariantKind::Tuple => {
-                    PatKind::TupleStruct(def_to_path(cx.tcx, v.did), Some(pats.collect()))
+                    PatKind::TupleStruct(def_to_path(cx.tcx, v.did), pats.collect(), None)
                 }
                 VariantKind::Unit => {
                     PatKind::Path(def_to_path(cx.tcx, v.did))
@@ -588,31 +588,19 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
             }
         }
 
-        ty::TyRef(_, ty::TypeAndMut { ty, mutbl }) => {
-            match ty.sty {
-               ty::TyArray(_, n) => match ctor {
-                    &Single => {
-                        assert_eq!(pats_len, n);
-                        PatKind::Vec(pats.collect(), None, hir::HirVec::new())
-                    },
-                    _ => bug!()
-                },
-                ty::TySlice(_) => match ctor {
-                    &Slice(n) => {
-                        assert_eq!(pats_len, n);
-                        PatKind::Vec(pats.collect(), None, hir::HirVec::new())
-                    },
-                    _ => bug!()
-                },
-                ty::TyStr => PatKind::Wild,
-
-                _ => {
-                    assert_eq!(pats_len, 1);
-                    PatKind::Ref(pats.nth(0).unwrap(), mutbl)
-                }
-            }
+        ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => {
+            assert_eq!(pats_len, 1);
+            PatKind::Ref(pats.nth(0).unwrap(), mutbl)
         }
 
+        ty::TySlice(_) => match ctor {
+            &Slice(n) => {
+                assert_eq!(pats_len, n);
+                PatKind::Vec(pats.collect(), None, hir::HirVec::new())
+            },
+            _ => unreachable!()
+        },
+
         ty::TyArray(_, len) => {
             assert_eq!(pats_len, len);
             PatKind::Vec(pats.collect(), None, hir::HirVec::new())
@@ -647,7 +635,7 @@ impl Constructor {
 fn missing_constructors(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix,
                        left_ty: Ty, max_slice_length: usize) -> Vec<Constructor> {
     let used_constructors: Vec<Constructor> = rows.iter()
-        .flat_map(|row| pat_constructors(cx, row[0], left_ty, max_slice_length))
+        .flat_map(|row| pat_constructors(cx, row[0].0, left_ty, max_slice_length))
         .collect();
     all_constructors(cx, left_ty, max_slice_length)
         .into_iter()
@@ -664,13 +652,8 @@ fn all_constructors(_cx: &MatchCheckCtxt, left_ty: Ty,
     match left_ty.sty {
         ty::TyBool =>
             [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
-
-        ty::TyRef(_, ty::TypeAndMut { ty, .. }) => match ty.sty {
-            ty::TySlice(_) =>
-                (0..max_slice_length+1).map(|length| Slice(length)).collect(),
-            _ => vec![Single]
-        },
-
+        ty::TySlice(_) =>
+            (0..max_slice_length+1).map(|length| Slice(length)).collect(),
         ty::TyEnum(def, _) => def.variants.iter().map(|v| Variant(v.did)).collect(),
         _ => vec![Single]
     }
@@ -689,13 +672,13 @@ fn all_constructors(_cx: &MatchCheckCtxt, left_ty: Ty,
 
 // Note: is_useful doesn't work on empty types, as the paper notes.
 // So it assumes that v is non-empty.
-fn is_useful(cx: &MatchCheckCtxt,
-             matrix: &Matrix,
-             v: &[&Pat],
-             witness: WitnessPreference)
-             -> Usefulness {
+fn is_useful<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>,
+                       matrix: &Matrix<'a, 'tcx>,
+                       v: &[(&Pat, Option<Ty<'tcx>>)],
+                       witness: WitnessPreference)
+                       -> Usefulness {
     let &Matrix(ref rows) = matrix;
-    debug!("{:?}", matrix);
+    debug!("is_useful({:?}, {:?})", matrix, v);
     if rows.is_empty() {
         return match witness {
             ConstructWitness => UsefulWithWitness(vec!()),
@@ -706,33 +689,29 @@ fn is_useful(cx: &MatchCheckCtxt,
         return NotUseful;
     }
     assert!(rows.iter().all(|r| r.len() == v.len()));
-    let real_pat = match rows.iter().find(|r| (*r)[0].id != DUMMY_NODE_ID) {
-        Some(r) => raw_pat(r[0]),
-        None if v.is_empty() => return NotUseful,
-        None => v[0]
-    };
-    let left_ty = if real_pat.id == DUMMY_NODE_ID {
-        cx.tcx.mk_nil()
-    } else {
-        let left_ty = cx.tcx.pat_ty(&real_pat);
-
-        match real_pat.node {
-            PatKind::Ident(hir::BindByRef(..), _, _) => {
-                left_ty.builtin_deref(false, NoPreference).unwrap().ty
-            }
-            _ => left_ty,
+    let left_ty = match rows.iter().filter_map(|r| r[0].1).next().or_else(|| v[0].1) {
+        Some(ty) => ty,
+        None => {
+            // all patterns are wildcards - we can pick any type we want
+            cx.tcx.types.bool
         }
     };
 
-    let max_slice_length = rows.iter().filter_map(|row| match row[0].node {
+    let max_slice_length = rows.iter().filter_map(|row| match row[0].0.node {
         PatKind::Vec(ref before, _, ref after) => Some(before.len() + after.len()),
         _ => None
     }).max().map_or(0, |v| v + 1);
 
-    let constructors = pat_constructors(cx, v[0], left_ty, max_slice_length);
+    let constructors = pat_constructors(cx, v[0].0, left_ty, max_slice_length);
+    debug!("is_useful - pat_constructors = {:?} left_ty = {:?}", constructors,
+           left_ty);
     if constructors.is_empty() {
+        // v[0] is a wildcard pattern - `pat_constructors` should really be returning
+        // an Option.
         let constructors = missing_constructors(cx, matrix, left_ty, max_slice_length);
+        debug!("is_useful - missing_constructors = {:?}", constructors);
         if constructors.is_empty() {
+            // all constructors are covered - must check them all.
             all_constructors(cx, left_ty, max_slice_length).into_iter().map(|c| {
                 match is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness) {
                     UsefulWithWitness(pats) => UsefulWithWitness({
@@ -751,11 +730,11 @@ fn is_useful(cx: &MatchCheckCtxt,
                 }
             }).find(|result| result != &NotUseful).unwrap_or(NotUseful)
         } else {
+            // some constructor is only covered by wildcards - pick it.
             let matrix = rows.iter().filter_map(|r| {
-                if pat_is_binding_or_wild(&cx.tcx.def_map.borrow(), raw_pat(r[0])) {
-                    Some(r[1..].to_vec())
-                } else {
-                    None
+                match raw_pat(r[0].0).node {
+                    PatKind::Binding(..) | PatKind::Wild => Some(r[1..].to_vec()),
+                    _ => None,
                 }
             }).collect();
             match is_useful(cx, &matrix, &v[1..], witness) {
@@ -772,16 +751,54 @@ fn is_useful(cx: &MatchCheckCtxt,
             }
         }
     } else {
+        // `v` is not a wildcard
         constructors.into_iter().map(|c|
             is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness)
         ).find(|result| result != &NotUseful).unwrap_or(NotUseful)
     }
 }
 
-fn is_useful_specialized(cx: &MatchCheckCtxt, &Matrix(ref m): &Matrix,
-                         v: &[&Pat], ctor: Constructor, lty: Ty,
-                         witness: WitnessPreference) -> Usefulness {
+fn check_for_ref_splitting<'s, 'a, 'tcx, I>(cx: &MatchCheckCtxt<'a, 'tcx>, data: I)
+    where I: Iterator<Item=(&'s Pat, Option<Ty<'tcx>>)>
+{
+    let mut ref_pattern = None;
+    let mut lit_pattern = None;
+
+    for (pat, _ty) in data {
+        match pat.node {
+            PatKind::Lit(..) => {
+                lit_pattern = Some(pat);
+            }
+            PatKind::Ref(..) => {
+                ref_pattern = Some(pat);
+            }
+            _ => {}
+        };
+        if let (Some(lit_pattern), Some(ref_pattern)) = (lit_pattern, ref_pattern) {
+            cx.tcx.sess.struct_span_err(
+                ref_pattern.span,
+                &format!("as a temporary restriction, literal patterns can't be split \
+                - see issue #35044")
+            ).span_note(lit_pattern.span, &format!("split literal here"))
+            .emit();
+            cx.tcx.sess.abort_if_errors();
+        }
+    }
+}
+
+fn is_useful_specialized<'a, 'tcx>(
+    cx: &MatchCheckCtxt<'a, 'tcx>,
+    &Matrix(ref m): &Matrix<'a, 'tcx>,
+    v: &[(&Pat, Option<Ty<'tcx>>)],
+    ctor: Constructor,
+    lty: Ty<'tcx>,
+    witness: WitnessPreference) -> Usefulness
+{
     let arity = constructor_arity(cx, &ctor, lty);
+    check_for_ref_splitting(
+        cx,
+        m.iter().map(|r| r[0]).chain(Some(v[0]))
+    );
     let matrix = Matrix(m.iter().filter_map(|r| {
         specialize(cx, &r[..], &ctor, 0, arity)
     }).collect());
@@ -801,41 +818,42 @@ fn is_useful_specialized(cx: &MatchCheckCtxt, &Matrix(ref m): &Matrix,
 /// On the other hand, a wild pattern and an identifier pattern cannot be
 /// specialized in any way.
 fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
-                    left_ty: Ty, max_slice_length: usize) -> Vec<Constructor> {
+                    left_ty: Ty, max_slice_length: usize)
+                    -> Vec<Constructor> {
     let pat = raw_pat(p);
     match pat.node {
-        PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) | PatKind::Ident(..) =>
-            match cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def() {
+        PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) =>
+            match cx.tcx.expect_def(pat.id) {
                 Def::Const(..) | Def::AssociatedConst(..) =>
                     span_bug!(pat.span, "const pattern should've \
                                          been rewritten"),
                 Def::Struct(..) | Def::TyAlias(..) => vec![Single],
                 Def::Variant(_, id) => vec![Variant(id)],
-                Def::Local(..) => vec![],
                 def => span_bug!(pat.span, "pat_constructors: unexpected \
                                             definition {:?}", def),
             },
         PatKind::QPath(..) =>
             span_bug!(pat.span, "const pattern should've been rewritten"),
         PatKind::Lit(ref expr) =>
-            vec!(ConstantValue(eval_const_expr(cx.tcx, &expr))),
+            vec![ConstantValue(eval_const_expr(cx.tcx, &expr))],
         PatKind::Range(ref lo, ref hi) =>
-            vec!(ConstantRange(eval_const_expr(cx.tcx, &lo), eval_const_expr(cx.tcx, &hi))),
+            vec![ConstantRange(eval_const_expr(cx.tcx, &lo), eval_const_expr(cx.tcx, &hi))],
         PatKind::Vec(ref before, ref slice, ref after) =>
             match left_ty.sty {
-                ty::TyArray(_, _) => vec!(Single),
-                _                      => if slice.is_some() {
+                ty::TyArray(_, _) => vec![Single],
+                ty::TySlice(_) if slice.is_some() => {
                     (before.len() + after.len()..max_slice_length+1)
                         .map(|length| Slice(length))
                         .collect()
-                } else {
-                    vec!(Slice(before.len() + after.len()))
                 }
+                ty::TySlice(_) => vec!(Slice(before.len() + after.len())),
+                _ => span_bug!(pat.span, "pat_constructors: unexpected \
+                                          slice pattern type {:?}", left_ty)
             },
-        PatKind::Box(_) | PatKind::Tup(_) | PatKind::Ref(..) =>
-            vec!(Single),
-        PatKind::Wild =>
-            vec!(),
+        PatKind::Box(..) | PatKind::Tuple(..) | PatKind::Ref(..) =>
+            vec![Single],
+        PatKind::Binding(..) | PatKind::Wild =>
+            vec![],
     }
 }
 
@@ -845,18 +863,16 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
 /// For instance, a tuple pattern (_, 42, Some([])) has the arity of 3.
 /// A struct pattern's arity is the number of fields it contains, etc.
 pub fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize {
+    debug!("constructor_arity({:?}, {:?})", ctor, ty);
     match ty.sty {
         ty::TyTuple(ref fs) => fs.len(),
         ty::TyBox(_) => 1,
-        ty::TyRef(_, ty::TypeAndMut { ty, .. }) => match ty.sty {
-            ty::TySlice(_) => match *ctor {
-                Slice(length) => length,
-                ConstantValue(_) => 0,
-                _ => bug!()
-            },
-            ty::TyStr => 0,
-            _ => 1
+        ty::TySlice(_) => match *ctor {
+            Slice(length) => length,
+            ConstantValue(_) => 0,
+            _ => bug!()
         },
+        ty::TyRef(..) => 1,
         ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => {
             ctor.variant_for_adt(adt).fields.len()
         }
@@ -883,6 +899,19 @@ fn range_covered_by_constructor(ctor: &Constructor,
     }
 }
 
+fn wrap_pat<'a, 'b, 'tcx>(cx: &MatchCheckCtxt<'b, 'tcx>,
+                          pat: &'a Pat)
+                          -> (&'a Pat, Option<Ty<'tcx>>)
+{
+    let pat_ty = cx.tcx.pat_ty(pat);
+    (pat, Some(match pat.node {
+        PatKind::Binding(hir::BindByRef(..), _, _) => {
+            pat_ty.builtin_deref(false, NoPreference).unwrap().ty
+        }
+        _ => pat_ty
+    }))
+}
+
 /// This is the main specialization step. It expands the first pattern in the given row
 /// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
 /// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
@@ -891,41 +920,53 @@ fn range_covered_by_constructor(ctor: &Constructor,
 /// different patterns.
 /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
 /// fields filled with wild patterns.
-pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
-                      constructor: &Constructor, col: usize, arity: usize) -> Option<Vec<&'a Pat>> {
+pub fn specialize<'a, 'b, 'tcx>(
+    cx: &MatchCheckCtxt<'b, 'tcx>,
+    r: &[(&'a Pat, Option<Ty<'tcx>>)],
+    constructor: &Constructor, col: usize, arity: usize)
+    -> Option<Vec<(&'a Pat, Option<Ty<'tcx>>)>>
+{
+    let pat = raw_pat(r[col].0);
     let &Pat {
         id: pat_id, ref node, span: pat_span
-    } = raw_pat(r[col]);
-    let head: Option<Vec<&Pat>> = match *node {
-        PatKind::Wild =>
-            Some(vec![DUMMY_WILD_PAT; arity]),
-
-        PatKind::Path(..) | PatKind::Ident(..) => {
-            let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
-            match def {
+    } = pat;
+    let wpat = |pat: &'a Pat| wrap_pat(cx, pat);
+    let dummy_pat = (DUMMY_WILD_PAT, None);
+
+    let head: Option<Vec<(&Pat, Option<Ty>)>> = match *node {
+        PatKind::Binding(..) | PatKind::Wild =>
+            Some(vec![dummy_pat; arity]),
+
+        PatKind::Path(..) => {
+            match cx.tcx.expect_def(pat_id) {
                 Def::Const(..) | Def::AssociatedConst(..) =>
                     span_bug!(pat_span, "const pattern should've \
                                          been rewritten"),
                 Def::Variant(_, id) if *constructor != Variant(id) => None,
                 Def::Variant(..) | Def::Struct(..) => Some(Vec::new()),
-                Def::Local(..) => Some(vec![DUMMY_WILD_PAT; arity]),
-                _ => span_bug!(pat_span, "specialize: unexpected \
+                def => span_bug!(pat_span, "specialize: unexpected \
                                           definition {:?}", def),
             }
         }
 
-        PatKind::TupleStruct(_, ref args) => {
-            let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
-            match def {
+        PatKind::TupleStruct(_, ref args, ddpos) => {
+            match cx.tcx.expect_def(pat_id) {
                 Def::Const(..) | Def::AssociatedConst(..) =>
                     span_bug!(pat_span, "const pattern should've \
                                          been rewritten"),
                 Def::Variant(_, id) if *constructor != Variant(id) => None,
                 Def::Variant(..) | Def::Struct(..) => {
-                    Some(match args {
-                        &Some(ref args) => args.iter().map(|p| &**p).collect(),
-                        &None => vec![DUMMY_WILD_PAT; arity],
-                    })
+                    match ddpos {
+                        Some(ddpos) => {
+                            let mut pats: Vec<_> = args[..ddpos].iter().map(|p| {
+                                wpat(p)
+                            }).collect();
+                            pats.extend(repeat((DUMMY_WILD_PAT, None)).take(arity - args.len()));
+                            pats.extend(args[ddpos..].iter().map(|p| wpat(p)));
+                            Some(pats)
+                        }
+                        None => Some(args.iter().map(|p| wpat(p)).collect())
+                    }
                 }
                 _ => None
             }
@@ -936,15 +977,14 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
         }
 
         PatKind::Struct(_, ref pattern_fields, _) => {
-            let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
             let adt = cx.tcx.node_id_to_type(pat_id).ty_adt_def().unwrap();
             let variant = constructor.variant_for_adt(adt);
-            let def_variant = adt.variant_of_def(def);
+            let def_variant = adt.variant_of_def(cx.tcx.expect_def(pat_id));
             if variant.did == def_variant.did {
                 Some(variant.fields.iter().map(|sf| {
                     match pattern_fields.iter().find(|f| f.node.name == sf.name) {
-                        Some(ref f) => &*f.node.pat,
-                        _ => DUMMY_WILD_PAT
+                        Some(ref f) => wpat(&f.node.pat),
+                        _ => dummy_pat
                     }
                 }).collect())
             } else {
@@ -952,20 +992,33 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
             }
         }
 
-        PatKind::Tup(ref args) =>
-            Some(args.iter().map(|p| &**p).collect()),
+        PatKind::Tuple(ref args, Some(ddpos)) => {
+            let mut pats: Vec<_> = args[..ddpos].iter().map(|p| wpat(p)).collect();
+            pats.extend(repeat(dummy_pat).take(arity - args.len()));
+            pats.extend(args[ddpos..].iter().map(|p| wpat(p)));
+            Some(pats)
+        }
+        PatKind::Tuple(ref args, None) =>
+            Some(args.iter().map(|p| wpat(&**p)).collect()),
 
         PatKind::Box(ref inner) | PatKind::Ref(ref inner, _) =>
-            Some(vec![&**inner]),
+            Some(vec![wpat(&**inner)]),
 
         PatKind::Lit(ref expr) => {
-            let expr_value = eval_const_expr(cx.tcx, &expr);
-            match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
-                Some(true) => Some(vec![]),
-                Some(false) => None,
-                None => {
-                    span_err!(cx.tcx.sess, pat_span, E0298, "mismatched types between arms");
-                    None
+            if let Some(&ty::TyS { sty: ty::TyRef(_, mt), .. }) = r[col].1 {
+                // HACK: handle string literals. A string literal pattern
+                // serves both as an unary reference pattern and as a
+                // nullary value pattern, depending on the type.
+                Some(vec![(pat, Some(mt.ty))])
+            } else {
+                let expr_value = eval_const_expr(cx.tcx, &expr);
+                match range_covered_by_constructor(constructor, &expr_value, &expr_value) {
+                    Some(true) => Some(vec![]),
+                    Some(false) => None,
+                    None => {
+                        span_err!(cx.tcx.sess, pat_span, E0298, "mismatched types between arms");
+                        None
+                    }
                 }
             }
         }
@@ -984,37 +1037,45 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
         }
 
         PatKind::Vec(ref before, ref slice, ref after) => {
+            let pat_len = before.len() + after.len();
             match *constructor {
-                // Fixed-length vectors.
                 Single => {
-                    let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
-                    pats.extend(repeat(DUMMY_WILD_PAT).take(arity - before.len() - after.len()));
-                    pats.extend(after.iter().map(|p| &**p));
-                    Some(pats)
-                },
-                Slice(length) if before.len() + after.len() <= length && slice.is_some() => {
-                    let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
-                    pats.extend(repeat(DUMMY_WILD_PAT).take(arity - before.len() - after.len()));
-                    pats.extend(after.iter().map(|p| &**p));
-                    Some(pats)
-                },
-                Slice(length) if before.len() + after.len() == length => {
-                    let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
-                    pats.extend(after.iter().map(|p| &**p));
-                    Some(pats)
+                    // Fixed-length vectors.
+                    Some(
+                        before.iter().map(|p| wpat(p)).chain(
+                        repeat(dummy_pat).take(arity - pat_len).chain(
+                        after.iter().map(|p| wpat(p))
+                    )).collect())
                 },
+                Slice(length) if pat_len <= length && slice.is_some() => {
+                    Some(
+                        before.iter().map(|p| wpat(p)).chain(
+                        repeat(dummy_pat).take(arity - pat_len).chain(
+                        after.iter().map(|p| wpat(p))
+                    )).collect())
+                }
+                Slice(length) if pat_len == length => {
+                    Some(
+                        before.iter().map(|p| wpat(p)).chain(
+                        after.iter().map(|p| wpat(p))
+                    ).collect())
+                }
                 SliceWithSubslice(prefix, suffix)
                     if before.len() == prefix
                         && after.len() == suffix
                         && slice.is_some() => {
-                    let mut pats: Vec<&Pat> = before.iter().map(|p| &**p).collect();
-                    pats.extend(after.iter().map(|p| &**p));
+                    // this is used by trans::_match only
+                    let mut pats: Vec<_> = before.iter()
+                        .map(|p| (&**p, None)).collect();
+                    pats.extend(after.iter().map(|p| (&**p, None)));
                     Some(pats)
                 }
                 _ => None
             }
         }
     };
+    debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head);
+
     head.map(|mut head| {
         head.extend_from_slice(&r[..col]);
         head.extend_from_slice(&r[col + 1..]);
@@ -1072,8 +1133,8 @@ fn check_irrefutable(cx: &MatchCheckCtxt, pat: &Pat, is_fn_arg: bool) {
 fn is_refutable<A, F>(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) -> Option<A> where
     F: FnOnce(&Pat) -> A,
 {
-    let pats = Matrix(vec!(vec!(pat)));
-    match is_useful(cx, &pats, &[DUMMY_WILD_PAT], ConstructWitness) {
+    let pats = Matrix(vec!(vec!(wrap_pat(cx, pat))));
+    match is_useful(cx, &pats, &[(DUMMY_WILD_PAT, None)], ConstructWitness) {
         UsefulWithWitness(pats) => Some(refutable(&pats[0])),
         NotUseful => None,
         Useful => bug!()
@@ -1084,17 +1145,11 @@ fn is_refutable<A, F>(cx: &MatchCheckCtxt, pat: &Pat, refutable: F) -> Option<A>
 fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
                                    has_guard: bool,
                                    pats: &[P<Pat>]) {
-    let tcx = cx.tcx;
-    let def_map = &tcx.def_map;
     let mut by_ref_span = None;
     for pat in pats {
-        pat_bindings(def_map, &pat, |bm, _, span, _path| {
-            match bm {
-                hir::BindByRef(_) => {
-                    by_ref_span = Some(span);
-                }
-                hir::BindByValue(_) => {
-                }
+        pat_bindings(&pat, |bm, _, span, _path| {
+            if let hir::BindByRef(..) = bm {
+                by_ref_span = Some(span);
             }
         })
     }
@@ -1103,7 +1158,7 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
         // check legality of moving out of the enum
 
         // x @ Foo(..) is legal, but x @ Foo(y) isn't.
-        if sub.map_or(false, |p| pat_contains_bindings(&def_map.borrow(), &p)) {
+        if sub.map_or(false, |p| pat_contains_bindings(&p)) {
             span_err!(cx.tcx.sess, p.span, E0007, "cannot bind by-move with sub-bindings");
         } else if has_guard {
             span_err!(cx.tcx.sess, p.span, E0008, "cannot bind by-move into a pattern guard");
@@ -1117,28 +1172,15 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
 
     for pat in pats {
         pat.walk(|p| {
-            if pat_is_binding(&def_map.borrow(), &p) {
-                match p.node {
-                    PatKind::Ident(hir::BindByValue(_), _, ref sub) => {
-                        let pat_ty = tcx.node_id_to_type(p.id);
-                        //FIXME: (@jroesch) this code should be floated up as well
-                        cx.tcx.infer_ctxt(None, Some(cx.param_env.clone()),
-                                          ProjectionMode::AnyFinal).enter(|infcx| {
-                            if infcx.type_moves_by_default(pat_ty, pat.span) {
-                                check_move(p, sub.as_ref().map(|p| &**p));
-                            }
-                        });
+            if let PatKind::Binding(hir::BindByValue(..), _, ref sub) = p.node {
+                let pat_ty = cx.tcx.node_id_to_type(p.id);
+                //FIXME: (@jroesch) this code should be floated up as well
+                cx.tcx.infer_ctxt(None, Some(cx.param_env.clone()),
+                                  ProjectionMode::AnyFinal).enter(|infcx| {
+                    if infcx.type_moves_by_default(pat_ty, pat.span) {
+                        check_move(p, sub.as_ref().map(|p| &**p));
                     }
-                    PatKind::Ident(hir::BindByRef(_), _, _) => {
-                    }
-                    _ => {
-                        span_bug!(
-                            p.span,
-                            "binding pattern {} is not an identifier: {:?}",
-                            p.id,
-                            p.node);
-                    }
-                }
+                });
             }
             true
         });
@@ -1207,18 +1249,19 @@ struct AtBindingPatternVisitor<'a, 'b:'a, 'tcx:'b> {
 
 impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> {
     fn visit_pat(&mut self, pat: &Pat) {
-        if !self.bindings_allowed && pat_is_binding(&self.cx.tcx.def_map.borrow(), pat) {
-            span_err!(self.cx.tcx.sess, pat.span, E0303,
-                                      "pattern bindings are not allowed \
-                                       after an `@`");
-        }
-
         match pat.node {
-            PatKind::Ident(_, _, Some(_)) => {
-                let bindings_were_allowed = self.bindings_allowed;
-                self.bindings_allowed = false;
-                intravisit::walk_pat(self, pat);
-                self.bindings_allowed = bindings_were_allowed;
+            PatKind::Binding(_, _, ref subpat) => {
+                if !self.bindings_allowed {
+                    span_err!(self.cx.tcx.sess, pat.span, E0303,
+                              "pattern bindings are not allowed after an `@`");
+                }
+
+                if subpat.is_some() {
+                    let bindings_were_allowed = self.bindings_allowed;
+                    self.bindings_allowed = false;
+                    intravisit::walk_pat(self, pat);
+                    self.bindings_allowed = bindings_were_allowed;
+                }
             }
             _ => intravisit::walk_pat(self, pat),
         }
index 457d25923c611b21c56ba249b20267446344797e..f2abdf831a3b8fdbdae8c6033bbc17085e38b3cc 100644 (file)
@@ -76,6 +76,8 @@ Not-a-Number (NaN) values cannot be compared for equality and hence can never
 match the input to a match expression. So, the following will not compile:
 
 ```compile_fail
+#![deny(illegal_floating_point_constant_pattern)]
+
 const NAN: f32 = 0.0 / 0.0;
 
 let number = 0.1f32;
@@ -160,7 +162,7 @@ let Some(y) = x;
 If you encounter this error you probably need to use a `match` or `if let` to
 deal with the possibility of failure. Example:
 
-```compile_fail
+```
 let x = Some(1);
 
 match x {
@@ -382,18 +384,19 @@ let irr = Irrefutable(0);
 
 // This fails to compile because the match is irrefutable.
 while let Irrefutable(x) = irr {
-    ...
+    // ...
 }
+```
 
 Try this instead:
 
-```
+```no_run
 struct Irrefutable(i32);
 let irr = Irrefutable(0);
 
 loop {
     let Irrefutable(x) = irr;
-    ...
+    // ...
 }
 ```
 "##,
index d0be7e203fa40e2580b759e9e8546845dd14fe09..6c37662206ce252f5592eeeee86853fe43c4b1e6 100644 (file)
@@ -19,7 +19,7 @@ use rustc::hir::map as ast_map;
 use rustc::hir::map::blocks::FnLikeNode;
 use rustc::middle::cstore::{self, InlinedItem};
 use rustc::traits;
-use rustc::hir::def::Def;
+use rustc::hir::def::{Def, PathResolution};
 use rustc::hir::def_id::DefId;
 use rustc::hir::pat_util::def_to_path;
 use rustc::ty::{self, Ty, TyCtxt, subst};
@@ -33,10 +33,10 @@ use syntax::ast;
 use rustc::hir::{Expr, PatKind};
 use rustc::hir;
 use rustc::hir::intravisit::FnKind;
-use syntax::codemap::Span;
 use syntax::ptr::P;
 use syntax::codemap;
 use syntax::attr::IntType;
+use syntax_pos::{self, Span};
 
 use std::borrow::Cow;
 use std::cmp::Ordering;
@@ -271,17 +271,16 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
     let pat = match expr.node {
         hir::ExprTup(ref exprs) =>
-            PatKind::Tup(try!(exprs.iter()
-                                  .map(|expr| const_expr_to_pat(tcx, &expr,
-                                                                pat_id, span))
-                                  .collect())),
+            PatKind::Tuple(try!(exprs.iter()
+                                     .map(|expr| const_expr_to_pat(tcx, &expr, pat_id, span))
+                                     .collect()), None),
 
         hir::ExprCall(ref callee, ref args) => {
-            let def = *tcx.def_map.borrow().get(&callee.id).unwrap();
+            let def = tcx.expect_def(callee.id);
             if let Vacant(entry) = tcx.def_map.borrow_mut().entry(expr.id) {
-               entry.insert(def);
+               entry.insert(PathResolution::new(def));
             }
-            let path = match def.full_def() {
+            let path = match def {
                 Def::Struct(def_id) => def_to_path(tcx, def_id),
                 Def::Variant(_, variant_did) => def_to_path(tcx, variant_did),
                 Def::Fn(..) | Def::Method(..) => return Ok(P(hir::Pat {
@@ -295,14 +294,14 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                 .map(|expr| const_expr_to_pat(tcx, &**expr,
                                                               pat_id, span))
                                 .collect());
-            PatKind::TupleStruct(path, Some(pats))
+            PatKind::TupleStruct(path, pats, None)
         }
 
         hir::ExprStruct(ref path, ref fields, None) => {
             let field_pats =
                 try!(fields.iter()
                            .map(|field| Ok(codemap::Spanned {
-                               span: codemap::DUMMY_SP,
+                               span: syntax_pos::DUMMY_SP,
                                node: hir::FieldPat {
                                    name: field.name.node,
                                    pat: try!(const_expr_to_pat(tcx, &field.expr,
@@ -323,12 +322,9 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
 
         hir::ExprPath(_, ref path) => {
-            let opt_def = tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def());
-            match opt_def {
-                Some(Def::Struct(..)) | Some(Def::Variant(..)) =>
-                    PatKind::Path(path.clone()),
-                Some(Def::Const(def_id)) |
-                Some(Def::AssociatedConst(def_id)) => {
+            match tcx.expect_def(expr.id) {
+                Def::Struct(..) | Def::Variant(..) => PatKind::Path(path.clone()),
+                Def::Const(def_id) | Def::AssociatedConst(def_id) => {
                     let substs = Some(tcx.node_id_item_substs(expr.id).substs);
                     let (expr, _ty) = lookup_const_by_id(tcx, def_id, substs).unwrap();
                     return const_expr_to_pat(tcx, expr, pat_id, span);
@@ -381,12 +377,6 @@ pub enum ErrKind {
     NotOn(ConstVal),
     CallOn(ConstVal),
 
-    DivideByZero,
-    DivideWithOverflow,
-    ModuloByZero,
-    ModuloWithOverflow,
-    ShiftLeftWithOverflow,
-    ShiftRightWithOverflow,
     MissingStructField,
     NonConstPath,
     UnimplementedConstVal(&'static str),
@@ -397,7 +387,7 @@ pub enum ErrKind {
     IndexedNonVec,
     IndexNegative,
     IndexNotInt,
-    IndexOutOfBounds,
+    IndexOutOfBounds { len: u64, index: u64 },
     RepeatCountNotNatural,
     RepeatCountNotInt,
 
@@ -437,12 +427,6 @@ impl ConstEvalErr {
             NotOn(ref const_val) => format!("not on {}", const_val.description()).into_cow(),
             CallOn(ref const_val) => format!("call on {}", const_val.description()).into_cow(),
 
-            DivideByZero         => "attempted to divide by zero".into_cow(),
-            DivideWithOverflow   => "attempted to divide with overflow".into_cow(),
-            ModuloByZero         => "attempted remainder with a divisor of zero".into_cow(),
-            ModuloWithOverflow   => "attempted remainder with overflow".into_cow(),
-            ShiftLeftWithOverflow => "attempted left shift with overflow".into_cow(),
-            ShiftRightWithOverflow => "attempted right shift with overflow".into_cow(),
             MissingStructField  => "nonexistent struct field".into_cow(),
             NonConstPath        => "non-constant path in constant expression".into_cow(),
             UnimplementedConstVal(what) =>
@@ -454,7 +438,10 @@ impl ConstEvalErr {
             IndexedNonVec => "indexing is only supported for arrays".into_cow(),
             IndexNegative => "indices must be non-negative integers".into_cow(),
             IndexNotInt => "indices must be integers".into_cow(),
-            IndexOutOfBounds => "array index out of bounds".into_cow(),
+            IndexOutOfBounds { len, index } => {
+                format!("index out of bounds: the len is {} but the index is {}",
+                        len, index).into_cow()
+            }
             RepeatCountNotNatural => "repeat count must be a natural number".into_cow(),
             RepeatCountNotInt => "repeat count must be integers".into_cow(),
 
@@ -583,6 +570,9 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 (&LitKind::Int(n, Unsuffixed), Some(&ty::TyInt(IntTy::Is))) |
                 (&LitKind::Int(n, Signed(IntTy::Is)), _) => {
                     match tcx.sess.target.int_type {
+                        IntTy::I16 => if n == I16_OVERFLOW {
+                            return Ok(Integral(Isize(Is16(::std::i16::MIN))));
+                        },
                         IntTy::I32 => if n == I32_OVERFLOW {
                             return Ok(Integral(Isize(Is32(::std::i32::MIN))));
                         },
@@ -621,18 +611,19 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         match (eval_const_expr_partial(tcx, &a, ty_hint, fn_args)?,
                eval_const_expr_partial(tcx, &b, b_ty, fn_args)?) {
           (Float(a), Float(b)) => {
+            use std::cmp::Ordering::*;
             match op.node {
-              hir::BiAdd => Float(a + b),
-              hir::BiSub => Float(a - b),
-              hir::BiMul => Float(a * b),
-              hir::BiDiv => Float(a / b),
-              hir::BiRem => Float(a % b),
-              hir::BiEq => Bool(a == b),
-              hir::BiLt => Bool(a < b),
-              hir::BiLe => Bool(a <= b),
-              hir::BiNe => Bool(a != b),
-              hir::BiGe => Bool(a >= b),
-              hir::BiGt => Bool(a > b),
+              hir::BiAdd => Float(math!(e, a + b)),
+              hir::BiSub => Float(math!(e, a - b)),
+              hir::BiMul => Float(math!(e, a * b)),
+              hir::BiDiv => Float(math!(e, a / b)),
+              hir::BiRem => Float(math!(e, a % b)),
+              hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal),
+              hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less),
+              hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater),
+              hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
+              hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
+              hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
               _ => signal!(e, InvalidOpForFloats(op.node)),
             }
           }
@@ -713,21 +704,13 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
       }
       hir::ExprPath(..) => {
-          let opt_def = if let Some(def) = tcx.def_map.borrow().get(&e.id) {
-              // After type-checking, def_map contains definition of the
-              // item referred to by the path. During type-checking, it
-              // can contain the raw output of path resolution, which
-              // might be a partially resolved path.
-              // FIXME: There's probably a better way to make sure we don't
-              // panic here.
-              if def.depth != 0 {
-                  signal!(e, UnresolvedPath);
-              }
-              def.full_def()
-          } else {
-              signal!(e, NonConstPath);
-          };
-          match opt_def {
+          // This function can be used before type checking when not all paths are fully resolved.
+          // FIXME: There's probably a better way to make sure we don't panic here.
+          let resolution = tcx.expect_resolution(e.id);
+          if resolution.depth != 0 {
+              signal!(e, UnresolvedPath);
+          }
+          match resolution.base_def {
               Def::Const(def_id) |
               Def::AssociatedConst(def_id) => {
                   let substs = if let ExprTypeChecked = ty_hint {
@@ -838,7 +821,9 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         };
         assert_eq!(idx as usize as u64, idx);
         match arr {
-            Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
+            Array(_, n) if idx >= n => {
+                signal!(e, IndexOutOfBounds { len: n, index: idx })
+            }
             Array(v, n) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node {
                 assert_eq!(n as usize as u64, n);
                 eval_const_expr_partial(tcx, &v[idx as usize], ty_hint, fn_args)?
@@ -846,7 +831,9 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 bug!()
             },
 
-            Repeat(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
+            Repeat(_, n) if idx >= n => {
+                signal!(e, IndexOutOfBounds { len: n, index: idx })
+            }
             Repeat(elem, _) => eval_const_expr_partial(
                 tcx,
                 &tcx.map.expect_expr(elem),
@@ -854,14 +841,13 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 fn_args,
             )?,
 
-            ByteStr(ref data) if idx >= data.len() as u64 => signal!(e, IndexOutOfBounds),
+            ByteStr(ref data) if idx >= data.len() as u64 => {
+                signal!(e, IndexOutOfBounds { len: data.len() as u64, index: idx })
+            }
             ByteStr(data) => {
                 Integral(U8(data[idx as usize]))
             },
 
-            Str(ref s) if idx as usize >= s.len() => signal!(e, IndexOutOfBounds),
-            // FIXME: return a const char
-            Str(_) => signal!(e, UnimplementedConstVal("indexing into str")),
             _ => signal!(e, IndexedNonVec),
         }
       }
@@ -952,10 +938,7 @@ fn infer<'a, 'tcx>(i: ConstInt,
         (&ty::TyInt(IntTy::I32), Infer(i)) => Ok(I32(i as i64 as i32)),
         (&ty::TyInt(IntTy::I64), Infer(i)) => Ok(I64(i as i64)),
         (&ty::TyInt(IntTy::Is), Infer(i)) => {
-            match ConstIsize::new(i as i64, tcx.sess.target.int_type) {
-                Ok(val) => Ok(Isize(val)),
-                Err(_) => Ok(Isize(ConstIsize::Is32(i as i64 as i32))),
-            }
+            Ok(Isize(ConstIsize::new_truncating(i as i64, tcx.sess.target.int_type)))
         },
 
         (&ty::TyInt(IntTy::I8), InferSigned(i)) => Ok(I8(i as i8)),
@@ -963,10 +946,7 @@ fn infer<'a, 'tcx>(i: ConstInt,
         (&ty::TyInt(IntTy::I32), InferSigned(i)) => Ok(I32(i as i32)),
         (&ty::TyInt(IntTy::I64), InferSigned(i)) => Ok(I64(i)),
         (&ty::TyInt(IntTy::Is), InferSigned(i)) => {
-            match ConstIsize::new(i, tcx.sess.target.int_type) {
-                Ok(val) => Ok(Isize(val)),
-                Err(_) => Ok(Isize(ConstIsize::Is32(i as i32))),
-            }
+            Ok(Isize(ConstIsize::new_truncating(i, tcx.sess.target.int_type)))
         },
 
         (&ty::TyUint(UintTy::U8), Infer(i)) => Ok(U8(i as u8)),
@@ -974,10 +954,7 @@ fn infer<'a, 'tcx>(i: ConstInt,
         (&ty::TyUint(UintTy::U32), Infer(i)) => Ok(U32(i as u32)),
         (&ty::TyUint(UintTy::U64), Infer(i)) => Ok(U64(i)),
         (&ty::TyUint(UintTy::Us), Infer(i)) => {
-            match ConstUsize::new(i, tcx.sess.target.uint_type) {
-                Ok(val) => Ok(Usize(val)),
-                Err(_) => Ok(Usize(ConstUsize::Us32(i as u32))),
-            }
+            Ok(Usize(ConstUsize::new_truncating(i, tcx.sess.target.uint_type)))
         },
         (&ty::TyUint(_), InferSigned(_)) => Err(IntermediateUnsignedNegative),
 
@@ -1059,29 +1036,23 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::
         ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i64 as i32))),
         ty::TyInt(ast::IntTy::I64) => Ok(Integral(I64(v as i64))),
         ty::TyInt(ast::IntTy::Is) => {
-            match ConstIsize::new(v as i64, tcx.sess.target.int_type) {
-                Ok(val) => Ok(Integral(Isize(val))),
-                Err(_) => Ok(Integral(Isize(ConstIsize::Is32(v as i64 as i32)))),
-            }
+            Ok(Integral(Isize(ConstIsize::new_truncating(v as i64, tcx.sess.target.int_type))))
         },
         ty::TyUint(ast::UintTy::U8) => Ok(Integral(U8(v as u8))),
         ty::TyUint(ast::UintTy::U16) => Ok(Integral(U16(v as u16))),
         ty::TyUint(ast::UintTy::U32) => Ok(Integral(U32(v as u32))),
         ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v))),
         ty::TyUint(ast::UintTy::Us) => {
-            match ConstUsize::new(v, tcx.sess.target.uint_type) {
-                Ok(val) => Ok(Integral(Usize(val))),
-                Err(_) => Ok(Integral(Usize(ConstUsize::Us32(v as u32)))),
-            }
+            Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.uint_type))))
         },
         ty::TyFloat(ast::FloatTy::F64) => match val.erase_type() {
-            Infer(u) => Ok(Float(u as f64)),
-            InferSigned(i) => Ok(Float(i as f64)),
+            Infer(u) => Ok(Float(F64(u as f64))),
+            InferSigned(i) => Ok(Float(F64(i as f64))),
             _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"),
         },
         ty::TyFloat(ast::FloatTy::F32) => match val.erase_type() {
-            Infer(u) => Ok(Float(u as f32 as f64)),
-            InferSigned(i) => Ok(Float(i as f32 as f64)),
+            Infer(u) => Ok(Float(F32(u as f32))),
+            InferSigned(i) => Ok(Float(F32(i as f32))),
             _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"),
         },
         ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")),
@@ -1094,13 +1065,35 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::
     }
 }
 
-fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: f64, ty: ty::Ty) -> CastResult {
+fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                              val: ConstFloat,
+                              ty: ty::Ty) -> CastResult {
     match ty.sty {
-        ty::TyInt(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty),
-        ty::TyInt(_) => cast_const_int(tcx, InferSigned(f as i64), ty),
-        ty::TyUint(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty),
-        ty::TyFloat(ast::FloatTy::F64) => Ok(Float(f)),
-        ty::TyFloat(ast::FloatTy::F32) => Ok(Float(f as f32 as f64)),
+        ty::TyInt(_) | ty::TyUint(_) => {
+            let i = match val {
+                F32(f) if f >= 0.0 => Infer(f as u64),
+                FInfer { f64: f, .. } |
+                F64(f) if f >= 0.0 => Infer(f as u64),
+
+                F32(f) => InferSigned(f as i64),
+                FInfer { f64: f, .. } |
+                F64(f) => InferSigned(f as i64)
+            };
+
+            if let (InferSigned(_), &ty::TyUint(_)) = (i, &ty.sty) {
+                return Err(CannotCast);
+            }
+
+            cast_const_int(tcx, i, ty)
+        }
+        ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val {
+            F32(f) => f as f64,
+            FInfer { f64: f, .. } | F64(f) => f
+        }))),
+        ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val {
+            F64(f) => f as f32,
+            FInfer { f32: f, .. } | F32(f) => f
+        }))),
         _ => Err(CannotCast),
     }
 }
@@ -1116,6 +1109,7 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstVal, ty: ty::Ty)
             ty::TyRawPtr(_) => {
                 Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr"))
             },
+            ty::TyRef(..) => Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice")),
             _ => Err(CannotCast),
         },
         _ => Err(CannotCast),
@@ -1158,33 +1152,43 @@ fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind,
             infer(Infer(n), tcx, &ty::TyUint(ity)).map(Integral)
         },
 
-        LitKind::Float(ref n, _) |
+        LitKind::Float(ref n, fty) => {
+            Ok(Float(parse_float(n, Some(fty), span)))
+        }
         LitKind::FloatUnsuffixed(ref n) => {
-            if let Ok(x) = n.parse::<f64>() {
-                Ok(Float(x))
-            } else {
-                // FIXME(#31407) this is only necessary because float parsing is buggy
-                span_bug!(span, "could not evaluate float literal (see issue #31407)");
-            }
+            let fty_hint = match ty_hint.map(|t| &t.sty) {
+                Some(&ty::TyFloat(fty)) => Some(fty),
+                _ => None
+            };
+            Ok(Float(parse_float(n, fty_hint, span)))
         }
         LitKind::Bool(b) => Ok(Bool(b)),
         LitKind::Char(c) => Ok(Char(c)),
     }
 }
 
+fn parse_float(num: &str, fty_hint: Option<ast::FloatTy>, span: Span) -> ConstFloat {
+    let val = match fty_hint {
+        Some(ast::FloatTy::F32) => num.parse::<f32>().map(F32),
+        Some(ast::FloatTy::F64) => num.parse::<f64>().map(F64),
+        None => {
+            num.parse::<f32>().and_then(|f32| {
+                num.parse::<f64>().map(|f64| {
+                    FInfer { f32: f32, f64: f64 }
+                })
+            })
+        }
+    };
+    val.unwrap_or_else(|_| {
+        // FIXME(#31407) this is only necessary because float parsing is buggy
+        span_bug!(span, "could not evaluate float literal (see issue #31407)");
+    })
+}
+
 pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
     match (a, b) {
         (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(),
-        (&Float(a), &Float(b)) => {
-            // This is pretty bad but it is the existing behavior.
-            Some(if a == b {
-                Ordering::Equal
-            } else if a < b {
-                Ordering::Less
-            } else {
-                Ordering::Greater
-            })
-        }
+        (&Float(a), &Float(b)) => a.try_cmp(b).ok(),
         (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)),
         (&Bool(a), &Bool(b)) => Some(a.cmp(&b)),
         (&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)),
index 9ab6a437a5ab24418dfcac4a3071c5b59580581a..726ba4fc1924fd48f3b3fc25b7c0d38df381c0f3 100644 (file)
@@ -27,7 +27,6 @@
 #![feature(staged_api)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(slice_patterns)]
-#![feature(iter_arith)]
 #![feature(question_mark)]
 #![feature(box_patterns)]
 #![feature(box_syntax)]
@@ -38,7 +37,7 @@
 extern crate rustc_back;
 extern crate rustc_const_math;
 extern crate graphviz;
-
+extern crate syntax_pos;
 extern crate serialize as rustc_serialize; // used by deriving
 
 // NB: This module needs to be declared first so diagnostics are
index 126b3824efec6940c4cd6c0949765bcbdd822a65..e4eb0f2c97eb9d5bf77f276155e17804d934ed82 100644 (file)
@@ -10,7 +10,7 @@
 
 use syntax::ast;
 
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, RustcEncodable, RustcDecodable)]
 pub enum ConstMathErr {
     NotInRange,
     CmpBetweenUnequalTypes,
@@ -25,7 +25,7 @@ pub enum ConstMathErr {
 }
 pub use self::ConstMathErr::*;
 
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, RustcEncodable, RustcDecodable)]
 pub enum Op {
     Add,
     Sub,
@@ -45,17 +45,17 @@ impl ConstMathErr {
         use self::Op::*;
         match *self {
             NotInRange => "inferred value out of range",
-            CmpBetweenUnequalTypes => "compared two integrals of different types",
-            UnequalTypes(Add) => "tried to add two integrals of different types",
-            UnequalTypes(Sub) => "tried to subtract two integrals of different types",
-            UnequalTypes(Mul) => "tried to multiply two integrals of different types",
-            UnequalTypes(Div) => "tried to divide two integrals of different types",
+            CmpBetweenUnequalTypes => "compared two values of different types",
+            UnequalTypes(Add) => "tried to add two values of different types",
+            UnequalTypes(Sub) => "tried to subtract two values of different types",
+            UnequalTypes(Mul) => "tried to multiply two values of different types",
+            UnequalTypes(Div) => "tried to divide two values of different types",
             UnequalTypes(Rem) => {
-                "tried to calculate the remainder of two integrals of different types"
+                "tried to calculate the remainder of two values of different types"
             },
-            UnequalTypes(BitAnd) => "tried to bitand two integrals of different types",
-            UnequalTypes(BitOr) => "tried to bitor two integrals of different types",
-            UnequalTypes(BitXor) => "tried to xor two integrals of different types",
+            UnequalTypes(BitAnd) => "tried to bitand two values of different types",
+            UnequalTypes(BitOr) => "tried to bitor two values of different types",
+            UnequalTypes(BitXor) => "tried to xor two values of different types",
             UnequalTypes(_) => unreachable!(),
             Overflow(Add) => "attempted to add with overflow",
             Overflow(Sub) => "attempted to subtract with overflow",
diff --git a/src/librustc_const_math/float.rs b/src/librustc_const_math/float.rs
new file mode 100644 (file)
index 0000000..4610c18
--- /dev/null
@@ -0,0 +1,173 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::cmp::Ordering;
+use std::hash;
+use std::mem::transmute;
+
+use super::err::*;
+
+#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
+pub enum ConstFloat {
+    F32(f32),
+    F64(f64),
+
+    // When the type isn't known, we have to operate on both possibilities.
+    FInfer {
+        f32: f32,
+        f64: f64
+    }
+}
+pub use self::ConstFloat::*;
+
+impl ConstFloat {
+    /// Description of the type, not the value
+    pub fn description(&self) -> &'static str {
+        match *self {
+            FInfer {..} => "float",
+            F32(_) => "f32",
+            F64(_) => "f64",
+        }
+    }
+
+    pub fn is_nan(&self) -> bool {
+        match *self {
+            F32(f) => f.is_nan(),
+            F64(f) => f.is_nan(),
+            FInfer { f32, f64 } => f32.is_nan() || f64.is_nan()
+        }
+    }
+
+    /// Compares the values if they are of the same type
+    pub fn try_cmp(self, rhs: Self) -> Result<Ordering, ConstMathErr> {
+        match (self, rhs) {
+            (F64(a), F64(b)) |
+            (F64(a), FInfer { f64: b, .. }) |
+            (FInfer { f64: a, .. }, F64(b)) |
+            (FInfer { f64: a, .. }, FInfer { f64: b, .. })  => {
+                // This is pretty bad but it is the existing behavior.
+                Ok(if a == b {
+                    Ordering::Equal
+                } else if a < b {
+                    Ordering::Less
+                } else {
+                    Ordering::Greater
+                })
+            }
+
+            (F32(a), F32(b)) |
+            (F32(a), FInfer { f32: b, .. }) |
+            (FInfer { f32: a, .. }, F32(b)) => {
+                Ok(if a == b {
+                    Ordering::Equal
+                } else if a < b {
+                    Ordering::Less
+                } else {
+                    Ordering::Greater
+                })
+            }
+
+            _ => Err(CmpBetweenUnequalTypes),
+        }
+    }
+}
+
+/// Note that equality for `ConstFloat` means that the it is the same
+/// constant, not that the rust values are equal. In particular, `NaN
+/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
+/// are considering unequal).
+impl PartialEq for ConstFloat {
+    fn eq(&self, other: &Self) -> bool {
+        match (*self, *other) {
+            (F64(a), F64(b)) |
+            (F64(a), FInfer { f64: b, .. }) |
+            (FInfer { f64: a, .. }, F64(b)) |
+            (FInfer { f64: a, .. }, FInfer { f64: b, .. }) => {
+                unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)}
+            }
+            (F32(a), F32(b)) => {
+                unsafe{transmute::<_,u32>(a) == transmute::<_,u32>(b)}
+            }
+            _ => false
+        }
+    }
+}
+
+impl Eq for ConstFloat {}
+
+impl hash::Hash for ConstFloat {
+    fn hash<H: hash::Hasher>(&self, state: &mut H) {
+        match *self {
+            F64(a) | FInfer { f64: a, .. } => {
+                unsafe { transmute::<_,u64>(a) }.hash(state)
+            }
+            F32(a) => {
+                unsafe { transmute::<_,u32>(a) }.hash(state)
+            }
+        }
+    }
+}
+
+impl ::std::fmt::Display for ConstFloat {
+    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
+        match *self {
+            FInfer { f64, .. } => write!(fmt, "{}", f64),
+            F32(f) => write!(fmt, "{}f32", f),
+            F64(f) => write!(fmt, "{}f64", f),
+        }
+    }
+}
+
+macro_rules! derive_binop {
+    ($op:ident, $func:ident) => {
+        impl ::std::ops::$op for ConstFloat {
+            type Output = Result<Self, ConstMathErr>;
+            fn $func(self, rhs: Self) -> Result<Self, ConstMathErr> {
+                match (self, rhs) {
+                    (F32(a), F32(b)) |
+                    (F32(a), FInfer { f32: b, .. }) |
+                    (FInfer { f32: a, .. }, F32(b)) => Ok(F32(a.$func(b))),
+
+                    (F64(a), F64(b)) |
+                    (FInfer { f64: a, .. }, F64(b)) |
+                    (F64(a), FInfer { f64: b, .. }) => Ok(F64(a.$func(b))),
+
+                    (FInfer { f32: a32, f64: a64 },
+                     FInfer { f32: b32, f64: b64 }) => Ok(FInfer {
+                        f32: a32.$func(b32),
+                        f64: a64.$func(b64)
+                    }),
+
+                    _ => Err(UnequalTypes(Op::$op)),
+                }
+            }
+        }
+    }
+}
+
+derive_binop!(Add, add);
+derive_binop!(Sub, sub);
+derive_binop!(Mul, mul);
+derive_binop!(Div, div);
+derive_binop!(Rem, rem);
+
+impl ::std::ops::Neg for ConstFloat {
+    type Output = Self;
+    fn neg(self) -> Self {
+        match self {
+            F32(f) => F32(-f),
+            F64(f) => F64(-f),
+            FInfer { f32, f64 } => FInfer {
+                f32: -f32,
+                f64: -f64
+            }
+        }
+    }
+}
index 64f03be3b5f07454ba0c50e7ef106554d96bb2a4..28a5887847252cb871f988768a0659de68c9fcda 100644 (file)
@@ -77,12 +77,14 @@ impl ConstInt {
             (Infer(a @ 0...as_u64::I16MAX), I16(_)) => I16(a as i64 as i16),
             (Infer(a @ 0...as_u64::I32MAX), I32(_)) => I32(a as i64 as i32),
             (Infer(a @ 0...as_u64::I64MAX), I64(_)) => I64(a as i64),
+            (Infer(a @ 0...as_u64::I16MAX), Isize(Is16(_))) => Isize(Is16(a as i64 as i16)),
             (Infer(a @ 0...as_u64::I32MAX), Isize(Is32(_))) => Isize(Is32(a as i64 as i32)),
             (Infer(a @ 0...as_u64::I64MAX), Isize(Is64(_))) => Isize(Is64(a as i64)),
             (Infer(a @ 0...as_u64::U8MAX), U8(_)) => U8(a as u8),
             (Infer(a @ 0...as_u64::U16MAX), U16(_)) => U16(a as u16),
             (Infer(a @ 0...as_u64::U32MAX), U32(_)) => U32(a as u32),
             (Infer(a), U64(_)) => U64(a),
+            (Infer(a @ 0...as_u64::U16MAX), Usize(Us16(_))) => Usize(Us16(a as u16)),
             (Infer(a @ 0...as_u64::U32MAX), Usize(Us32(_))) => Usize(Us32(a as u32)),
             (Infer(a), Usize(Us64(_))) => Usize(Us64(a)),
 
@@ -92,6 +94,9 @@ impl ConstInt {
             (InferSigned(a @ as_i64::I16MIN...as_i64::I16MAX), I16(_)) => I16(a as i16),
             (InferSigned(a @ as_i64::I32MIN...as_i64::I32MAX), I32(_)) => I32(a as i32),
             (InferSigned(a), I64(_)) => I64(a),
+            (InferSigned(a @ as_i64::I16MIN...as_i64::I16MAX), Isize(Is16(_))) => {
+                Isize(Is16(a as i16))
+            },
             (InferSigned(a @ as_i64::I32MIN...as_i64::I32MAX), Isize(Is32(_))) => {
                 Isize(Is32(a as i32))
             },
@@ -100,6 +105,7 @@ impl ConstInt {
             (InferSigned(a @ 0...as_i64::U16MAX), U16(_)) => U16(a as u16),
             (InferSigned(a @ 0...as_i64::U32MAX), U32(_)) => U32(a as u32),
             (InferSigned(a @ 0...as_i64::I64MAX), U64(_)) => U64(a as u64),
+            (InferSigned(a @ 0...as_i64::U16MAX), Usize(Us16(_))) => Usize(Us16(a as u16)),
             (InferSigned(a @ 0...as_i64::U32MAX), Usize(Us32(_))) => Usize(Us32(a as u32)),
             (InferSigned(a @ 0...as_i64::I64MAX), Usize(Us64(_))) => Usize(Us64(a as u64)),
             (InferSigned(_), _) => return Err(ConstMathErr::NotInRange),
@@ -117,6 +123,7 @@ impl ConstInt {
             I16(i) if i < 0 => InferSigned(i as i64),
             I32(i) if i < 0 => InferSigned(i as i64),
             I64(i) if i < 0 => InferSigned(i as i64),
+            Isize(Is16(i)) if i < 0 => InferSigned(i as i64),
             Isize(Is32(i)) if i < 0 => InferSigned(i as i64),
             Isize(Is64(i)) if i < 0 => InferSigned(i as i64),
             InferSigned(i) => Infer(i as u64),
@@ -124,12 +131,14 @@ impl ConstInt {
             I16(i) => Infer(i as u64),
             I32(i) => Infer(i as u64),
             I64(i) => Infer(i as u64),
+            Isize(Is16(i)) => Infer(i as u64),
             Isize(Is32(i)) => Infer(i as u64),
             Isize(Is64(i)) => Infer(i as u64),
             U8(i) => Infer(i as u64),
             U16(i) => Infer(i as u64),
             U32(i) => Infer(i as u64),
             U64(i) => Infer(i as u64),
+            Usize(Us16(i)) => Infer(i as u64),
             Usize(Us32(i)) => Infer(i as u64),
             Usize(Us64(i)) => Infer(i),
         }
@@ -173,6 +182,7 @@ impl ConstInt {
             | Isize(Is64(v))
             | I64(v) if v >= 0 && v <= ::std::u32::MAX as i64 => Some(v as u32),
             Isize(Is32(v)) if v >= 0 => Some(v as u32),
+            Isize(Is16(v)) if v >= 0 => Some(v as u32),
             U8(v) => Some(v as u32),
             U16(v) => Some(v as u32),
             U32(v) => Some(v),
@@ -180,6 +190,7 @@ impl ConstInt {
             | Usize(Us64(v))
             | U64(v) if v <= ::std::u32::MAX as u64 => Some(v as u32),
             Usize(Us32(v)) => Some(v),
+            Usize(Us16(v)) => Some(v as u32),
             _ => None,
         }
     }
@@ -193,12 +204,14 @@ impl ConstInt {
             I16(v) if v >= 0 => Some(v as u64),
             I32(v) if v >= 0 => Some(v as u64),
             I64(v) if v >= 0 => Some(v as u64),
+            Isize(Is16(v)) if v >= 0 => Some(v as u64),
             Isize(Is32(v)) if v >= 0 => Some(v as u64),
             Isize(Is64(v)) if v >= 0 => Some(v as u64),
             U8(v) => Some(v as u64),
             U16(v) => Some(v as u64),
             U32(v) => Some(v as u64),
             U64(v) => Some(v),
+            Usize(Us16(v)) => Some(v as u64),
             Usize(Us32(v)) => Some(v as u64),
             Usize(Us64(v)) => Some(v),
             _ => None,
@@ -211,6 +224,7 @@ impl ConstInt {
             I16(v) => v < 0,
             I32(v) => v < 0,
             I64(v) => v < 0,
+            Isize(Is16(v)) => v < 0,
             Isize(Is32(v)) => v < 0,
             Isize(Is64(v)) => v < 0,
             InferSigned(v) => v < 0,
@@ -225,12 +239,14 @@ impl ConstInt {
             (I16(a), I16(b)) => Ok(a.cmp(&b)),
             (I32(a), I32(b)) => Ok(a.cmp(&b)),
             (I64(a), I64(b)) => Ok(a.cmp(&b)),
+            (Isize(Is16(a)), Isize(Is16(b))) => Ok(a.cmp(&b)),
             (Isize(Is32(a)), Isize(Is32(b))) => Ok(a.cmp(&b)),
             (Isize(Is64(a)), Isize(Is64(b))) => Ok(a.cmp(&b)),
             (U8(a), U8(b)) => Ok(a.cmp(&b)),
             (U16(a), U16(b)) => Ok(a.cmp(&b)),
             (U32(a), U32(b)) => Ok(a.cmp(&b)),
             (U64(a), U64(b)) => Ok(a.cmp(&b)),
+            (Usize(Us16(a)), Usize(Us16(b))) => Ok(a.cmp(&b)),
             (Usize(Us32(a)), Usize(Us32(b))) => Ok(a.cmp(&b)),
             (Usize(Us64(a)), Usize(Us64(b))) => Ok(a.cmp(&b)),
             (Infer(a), Infer(b)) => Ok(a.cmp(&b)),
@@ -249,12 +265,14 @@ impl ConstInt {
             ConstInt::I16(i) => ConstInt::I16(add1!(i)),
             ConstInt::I32(i) => ConstInt::I32(add1!(i)),
             ConstInt::I64(i) => ConstInt::I64(add1!(i)),
+            ConstInt::Isize(ConstIsize::Is16(i)) => ConstInt::Isize(ConstIsize::Is16(add1!(i))),
             ConstInt::Isize(ConstIsize::Is32(i)) => ConstInt::Isize(ConstIsize::Is32(add1!(i))),
             ConstInt::Isize(ConstIsize::Is64(i)) => ConstInt::Isize(ConstIsize::Is64(add1!(i))),
             ConstInt::U8(i) => ConstInt::U8(add1!(i)),
             ConstInt::U16(i) => ConstInt::U16(add1!(i)),
             ConstInt::U32(i) => ConstInt::U32(add1!(i)),
             ConstInt::U64(i) => ConstInt::U64(add1!(i)),
+            ConstInt::Usize(ConstUsize::Us16(i)) => ConstInt::Usize(ConstUsize::Us16(add1!(i))),
             ConstInt::Usize(ConstUsize::Us32(i)) => ConstInt::Usize(ConstUsize::Us32(add1!(i))),
             ConstInt::Usize(ConstUsize::Us64(i)) => ConstInt::Usize(ConstUsize::Us64(add1!(i))),
             ConstInt::Infer(_) | ConstInt::InferSigned(_) => panic!("no type info for const int"),
@@ -301,12 +319,14 @@ impl ::std::fmt::Display for ConstInt {
             I64(i) => write!(fmt, "{}i64", i),
             Isize(ConstIsize::Is64(i)) => write!(fmt, "{}isize", i),
             Isize(ConstIsize::Is32(i)) => write!(fmt, "{}isize", i),
+            Isize(ConstIsize::Is16(i)) => write!(fmt, "{}isize", i),
             U8(i) => write!(fmt, "{}u8", i),
             U16(i) => write!(fmt, "{}u16", i),
             U32(i) => write!(fmt, "{}u32", i),
             U64(i) => write!(fmt, "{}u64", i),
             Usize(ConstUsize::Us64(i)) => write!(fmt, "{}usize", i),
             Usize(ConstUsize::Us32(i)) => write!(fmt, "{}usize", i),
+            Usize(ConstUsize::Us16(i)) => write!(fmt, "{}usize", i),
         }
     }
 }
@@ -331,12 +351,14 @@ macro_rules! impl_binop {
                     (I16(a), I16(b)) => a.$checked_func(b).map(I16),
                     (I32(a), I32(b)) => a.$checked_func(b).map(I32),
                     (I64(a), I64(b)) => a.$checked_func(b).map(I64),
+                    (Isize(Is16(a)), Isize(Is16(b))) => a.$checked_func(b).map(Is16).map(Isize),
                     (Isize(Is32(a)), Isize(Is32(b))) => a.$checked_func(b).map(Is32).map(Isize),
                     (Isize(Is64(a)), Isize(Is64(b))) => a.$checked_func(b).map(Is64).map(Isize),
                     (U8(a), U8(b)) => a.$checked_func(b).map(U8),
                     (U16(a), U16(b)) => a.$checked_func(b).map(U16),
                     (U32(a), U32(b)) => a.$checked_func(b).map(U32),
                     (U64(a), U64(b)) => a.$checked_func(b).map(U64),
+                    (Usize(Us16(a)), Usize(Us16(b))) => a.$checked_func(b).map(Us16).map(Usize),
                     (Usize(Us32(a)), Usize(Us32(b))) => a.$checked_func(b).map(Us32).map(Usize),
                     (Usize(Us64(a)), Usize(Us64(b))) => a.$checked_func(b).map(Us64).map(Usize),
                     (Infer(a), Infer(b)) => a.$checked_func(b).map(Infer),
@@ -358,12 +380,14 @@ macro_rules! derive_binop {
                     (I16(a), I16(b)) => Ok(I16(a.$func(b))),
                     (I32(a), I32(b)) => Ok(I32(a.$func(b))),
                     (I64(a), I64(b)) => Ok(I64(a.$func(b))),
+                    (Isize(Is16(a)), Isize(Is16(b))) => Ok(Isize(Is16(a.$func(b)))),
                     (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a.$func(b)))),
                     (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a.$func(b)))),
                     (U8(a), U8(b)) => Ok(U8(a.$func(b))),
                     (U16(a), U16(b)) => Ok(U16(a.$func(b))),
                     (U32(a), U32(b)) => Ok(U32(a.$func(b))),
                     (U64(a), U64(b)) => Ok(U64(a.$func(b))),
+                    (Usize(Us16(a)), Usize(Us16(b))) => Ok(Usize(Us16(a.$func(b)))),
                     (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a.$func(b)))),
                     (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a.$func(b)))),
                     (Infer(a), Infer(b)) => Ok(Infer(a.$func(b))),
@@ -393,6 +417,7 @@ fn check_division(
         (I16(_), I16(0)) => Err(zerr),
         (I32(_), I32(0)) => Err(zerr),
         (I64(_), I64(0)) => Err(zerr),
+        (Isize(_), Isize(Is16(0))) => Err(zerr),
         (Isize(_), Isize(Is32(0))) => Err(zerr),
         (Isize(_), Isize(Is64(0))) => Err(zerr),
         (InferSigned(_), InferSigned(0)) => Err(zerr),
@@ -401,6 +426,7 @@ fn check_division(
         (U16(_), U16(0)) => Err(zerr),
         (U32(_), U32(0)) => Err(zerr),
         (U64(_), U64(0)) => Err(zerr),
+        (Usize(_), Usize(Us16(0))) => Err(zerr),
         (Usize(_), Usize(Us32(0))) => Err(zerr),
         (Usize(_), Usize(Us64(0))) => Err(zerr),
         (Infer(_), Infer(0)) => Err(zerr),
@@ -409,6 +435,7 @@ fn check_division(
         (I16(::std::i16::MIN), I16(-1)) => Err(Overflow(op)),
         (I32(::std::i32::MIN), I32(-1)) => Err(Overflow(op)),
         (I64(::std::i64::MIN), I64(-1)) => Err(Overflow(op)),
+        (Isize(Is16(::std::i16::MIN)), Isize(Is16(-1))) => Err(Overflow(op)),
         (Isize(Is32(::std::i32::MIN)), Isize(Is32(-1))) => Err(Overflow(op)),
         (Isize(Is64(::std::i64::MIN)), Isize(Is64(-1))) => Err(Overflow(op)),
         (InferSigned(::std::i64::MIN), InferSigned(-1)) => Err(Overflow(op)),
@@ -427,6 +454,7 @@ impl ::std::ops::Div for ConstInt {
             (I16(a), I16(b)) => Ok(I16(a/b)),
             (I32(a), I32(b)) => Ok(I32(a/b)),
             (I64(a), I64(b)) => Ok(I64(a/b)),
+            (Isize(Is16(a)), Isize(Is16(b))) => Ok(Isize(Is16(a/b))),
             (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a/b))),
             (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a/b))),
             (InferSigned(a), InferSigned(b)) => Ok(InferSigned(a/b)),
@@ -435,6 +463,7 @@ impl ::std::ops::Div for ConstInt {
             (U16(a), U16(b)) => Ok(U16(a/b)),
             (U32(a), U32(b)) => Ok(U32(a/b)),
             (U64(a), U64(b)) => Ok(U64(a/b)),
+            (Usize(Us16(a)), Usize(Us16(b))) => Ok(Usize(Us16(a/b))),
             (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a/b))),
             (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a/b))),
             (Infer(a), Infer(b)) => Ok(Infer(a/b)),
@@ -455,6 +484,7 @@ impl ::std::ops::Rem for ConstInt {
             (I16(a), I16(b)) => Ok(I16(a%b)),
             (I32(a), I32(b)) => Ok(I32(a%b)),
             (I64(a), I64(b)) => Ok(I64(a%b)),
+            (Isize(Is16(a)), Isize(Is16(b))) => Ok(Isize(Is16(a%b))),
             (Isize(Is32(a)), Isize(Is32(b))) => Ok(Isize(Is32(a%b))),
             (Isize(Is64(a)), Isize(Is64(b))) => Ok(Isize(Is64(a%b))),
             (InferSigned(a), InferSigned(b)) => Ok(InferSigned(a%b)),
@@ -463,6 +493,7 @@ impl ::std::ops::Rem for ConstInt {
             (U16(a), U16(b)) => Ok(U16(a%b)),
             (U32(a), U32(b)) => Ok(U32(a%b)),
             (U64(a), U64(b)) => Ok(U64(a%b)),
+            (Usize(Us16(a)), Usize(Us16(b))) => Ok(Usize(Us16(a%b))),
             (Usize(Us32(a)), Usize(Us32(b))) => Ok(Usize(Us32(a%b))),
             (Usize(Us64(a)), Usize(Us64(b))) => Ok(Usize(Us64(a%b))),
             (Infer(a), Infer(b)) => Ok(Infer(a%b)),
@@ -481,12 +512,14 @@ impl ::std::ops::Shl<ConstInt> for ConstInt {
             I16(a) => Ok(I16(overflowing!(a.overflowing_shl(b), Op::Shl))),
             I32(a) => Ok(I32(overflowing!(a.overflowing_shl(b), Op::Shl))),
             I64(a) => Ok(I64(overflowing!(a.overflowing_shl(b), Op::Shl))),
+            Isize(Is16(a)) => Ok(Isize(Is16(overflowing!(a.overflowing_shl(b), Op::Shl)))),
             Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shl(b), Op::Shl)))),
             Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shl(b), Op::Shl)))),
             U8(a) => Ok(U8(overflowing!(a.overflowing_shl(b), Op::Shl))),
             U16(a) => Ok(U16(overflowing!(a.overflowing_shl(b), Op::Shl))),
             U32(a) => Ok(U32(overflowing!(a.overflowing_shl(b), Op::Shl))),
             U64(a) => Ok(U64(overflowing!(a.overflowing_shl(b), Op::Shl))),
+            Usize(Us16(a)) => Ok(Usize(Us16(overflowing!(a.overflowing_shl(b), Op::Shl)))),
             Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shl(b), Op::Shl)))),
             Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shl(b), Op::Shl)))),
             Infer(a) => Ok(Infer(overflowing!(a.overflowing_shl(b), Op::Shl))),
@@ -504,12 +537,14 @@ impl ::std::ops::Shr<ConstInt> for ConstInt {
             I16(a) => Ok(I16(overflowing!(a.overflowing_shr(b), Op::Shr))),
             I32(a) => Ok(I32(overflowing!(a.overflowing_shr(b), Op::Shr))),
             I64(a) => Ok(I64(overflowing!(a.overflowing_shr(b), Op::Shr))),
+            Isize(Is16(a)) => Ok(Isize(Is16(overflowing!(a.overflowing_shr(b), Op::Shr)))),
             Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_shr(b), Op::Shr)))),
             Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_shr(b), Op::Shr)))),
             U8(a) => Ok(U8(overflowing!(a.overflowing_shr(b), Op::Shr))),
             U16(a) => Ok(U16(overflowing!(a.overflowing_shr(b), Op::Shr))),
             U32(a) => Ok(U32(overflowing!(a.overflowing_shr(b), Op::Shr))),
             U64(a) => Ok(U64(overflowing!(a.overflowing_shr(b), Op::Shr))),
+            Usize(Us16(a)) => Ok(Usize(Us16(overflowing!(a.overflowing_shr(b), Op::Shr)))),
             Usize(Us32(a)) => Ok(Usize(Us32(overflowing!(a.overflowing_shr(b), Op::Shr)))),
             Usize(Us64(a)) => Ok(Usize(Us64(overflowing!(a.overflowing_shr(b), Op::Shr)))),
             Infer(a) => Ok(Infer(overflowing!(a.overflowing_shr(b), Op::Shr))),
@@ -526,12 +561,14 @@ impl ::std::ops::Neg for ConstInt {
             I16(a) => Ok(I16(overflowing!(a.overflowing_neg(), Op::Neg))),
             I32(a) => Ok(I32(overflowing!(a.overflowing_neg(), Op::Neg))),
             I64(a) => Ok(I64(overflowing!(a.overflowing_neg(), Op::Neg))),
+            Isize(Is16(a)) => Ok(Isize(Is16(overflowing!(a.overflowing_neg(), Op::Neg)))),
             Isize(Is32(a)) => Ok(Isize(Is32(overflowing!(a.overflowing_neg(), Op::Neg)))),
             Isize(Is64(a)) => Ok(Isize(Is64(overflowing!(a.overflowing_neg(), Op::Neg)))),
             U8(0) => Ok(U8(0)),
             U16(0) => Ok(U16(0)),
             U32(0) => Ok(U32(0)),
             U64(0) => Ok(U64(0)),
+            Usize(Us16(0)) => Ok(Usize(Us16(0))),
             Usize(Us32(0)) => Ok(Usize(Us32(0))),
             Usize(Us64(0)) => Ok(Usize(Us64(0))),
             U8(_) => Err(UnsignedNegation),
@@ -554,12 +591,14 @@ impl ::std::ops::Not for ConstInt {
             I16(a) => Ok(I16(!a)),
             I32(a) => Ok(I32(!a)),
             I64(a) => Ok(I64(!a)),
+            Isize(Is16(a)) => Ok(Isize(Is16(!a))),
             Isize(Is32(a)) => Ok(Isize(Is32(!a))),
             Isize(Is64(a)) => Ok(Isize(Is64(!a))),
             U8(a) => Ok(U8(!a)),
             U16(a) => Ok(U16(!a)),
             U32(a) => Ok(U32(!a)),
             U64(a) => Ok(U64(!a)),
+            Usize(Us16(a)) => Ok(Usize(Us16(!a))),
             Usize(Us32(a)) => Ok(Usize(Us32(!a))),
             Usize(Us64(a)) => Ok(Usize(Us64(!a))),
             Infer(a) => Ok(Infer(!a)),
index 082c6510f8bc961308387571b40c2e2c71ffc5ce..ef92b628523e7b278c0fdba2372bbcb6d166eb96 100644 (file)
@@ -15,6 +15,7 @@ use super::err::*;
 /// Anything else is an error. This invariant is checked at several locations
 #[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)]
 pub enum ConstIsize {
+    Is16(i16),
     Is32(i32),
     Is64(i64),
 }
@@ -23,17 +24,29 @@ pub use self::ConstIsize::*;
 impl ConstIsize {
     pub fn as_i64(self, target_int_ty: ast::IntTy) -> i64 {
         match (self, target_int_ty) {
+            (Is16(i), ast::IntTy::I16) => i as i64,
             (Is32(i), ast::IntTy::I32) => i as i64,
             (Is64(i), ast::IntTy::I64) => i,
-            _ => panic!("got invalid isize size for target"),
+            _ => panic!("unable to convert self ({:?}) to target isize ({:?})",
+                        self, target_int_ty),
         }
     }
     pub fn new(i: i64, target_int_ty: ast::IntTy) -> Result<Self, ConstMathErr> {
         match target_int_ty {
+            ast::IntTy::I16 if i as i16 as i64 == i => Ok(Is16(i as i16)),
+            ast::IntTy::I16 => Err(LitOutOfRange(ast::IntTy::Is)),
             ast::IntTy::I32 if i as i32 as i64 == i => Ok(Is32(i as i32)),
             ast::IntTy::I32 => Err(LitOutOfRange(ast::IntTy::Is)),
             ast::IntTy::I64 => Ok(Is64(i)),
             _ => unreachable!(),
         }
     }
+    pub fn new_truncating(i: i64, target_int_ty: ast::IntTy) -> Self {
+        match target_int_ty {
+            ast::IntTy::I16 => Is16(i as i16),
+            ast::IntTy::I32 => Is32(i as i32),
+            ast::IntTy::I64 => Is64(i),
+            _ => unreachable!(),
+        }
+    }
 }
index 59792d16e8bb6a484c1793de319d5d2965709ac6..741dd4107e001e0886d8484fb744e01445357ad9 100644 (file)
 
 extern crate serialize as rustc_serialize; // used by deriving
 
+mod float;
 mod int;
 mod us;
 mod is;
 mod err;
 
+pub use float::*;
 pub use int::*;
 pub use us::*;
 pub use is::*;
index e5a7086d43663c1ba93b889c5891ee0b8228bac1..bf73ff03c98959e025fe3ae04fb6f8065f5464b1 100644 (file)
@@ -15,6 +15,7 @@ use super::err::*;
 /// Anything else is an error. This invariant is checked at several locations
 #[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, Hash, Eq, PartialEq)]
 pub enum ConstUsize {
+    Us16(u16),
     Us32(u32),
     Us64(u64),
 }
@@ -23,17 +24,29 @@ pub use self::ConstUsize::*;
 impl ConstUsize {
     pub fn as_u64(self, target_uint_ty: ast::UintTy) -> u64 {
         match (self, target_uint_ty) {
+            (Us16(i), ast::UintTy::U16) => i as u64,
             (Us32(i), ast::UintTy::U32) => i as u64,
             (Us64(i), ast::UintTy::U64) => i,
-            _ => panic!("got invalid usize size for target"),
+            _ => panic!("unable to convert self ({:?}) to target usize ({:?})",
+                        self, target_uint_ty),
         }
     }
     pub fn new(i: u64, target_uint_ty: ast::UintTy) -> Result<Self, ConstMathErr> {
         match target_uint_ty {
+            ast::UintTy::U16 if i as u16 as u64 == i => Ok(Us16(i as u16)),
+            ast::UintTy::U16 => Err(ULitOutOfRange(ast::UintTy::Us)),
             ast::UintTy::U32 if i as u32 as u64 == i => Ok(Us32(i as u32)),
             ast::UintTy::U32 => Err(ULitOutOfRange(ast::UintTy::Us)),
             ast::UintTy::U64 => Ok(Us64(i)),
             _ => unreachable!(),
         }
     }
+    pub fn new_truncating(i: u64, target_uint_ty: ast::UintTy) -> Self {
+        match target_uint_ty {
+            ast::UintTy::U16 => Us16(i as u16),
+            ast::UintTy::U32 => Us32(i as u32),
+            ast::UintTy::U64 => Us64(i),
+            _ => unreachable!(),
+        }
+    }
 }
index cb648038c343643737ed3b7d327837edf4cb4dfe..2c3a2e8ef6c3b97c9514bdf0cca99b850e3fdedd 100644 (file)
@@ -11,7 +11,7 @@
 use std::iter::FromIterator;
 
 /// A very simple BitVector type.
-#[derive(Clone)]
+#[derive(Clone, Debug, PartialEq)]
 pub struct BitVector {
     data: Vec<u64>,
 }
@@ -237,23 +237,9 @@ fn bitvec_iter_works() {
                [1, 10, 19, 62, 63, 64, 65, 66, 99]);
 }
 
-#[test]
-fn bitvec_iter_works_2() {
-    let mut bitvec = BitVector::new(300);
-    bitvec.insert(1);
-    bitvec.insert(10);
-    bitvec.insert(19);
-    bitvec.insert(62);
-    bitvec.insert(66);
-    bitvec.insert(99);
-    bitvec.insert(299);
-    assert_eq!(bitvec.iter().collect::<Vec<_>>(),
-               [1, 10, 19, 62, 66, 99, 299]);
-
-}
 
 #[test]
-fn bitvec_iter_works_3() {
+fn bitvec_iter_works_2() {
     let mut bitvec = BitVector::new(319);
     bitvec.insert(0);
     bitvec.insert(127);
diff --git a/src/librustc_data_structures/control_flow_graph/dominators/mod.rs b/src/librustc_data_structures/control_flow_graph/dominators/mod.rs
new file mode 100644 (file)
index 0000000..250b89d
--- /dev/null
@@ -0,0 +1,284 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Algorithm citation:
+//! A Simple, Fast Dominance Algorithm.
+//! Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
+//! Rice Computer Science TS-06-33870
+//! https://www.cs.rice.edu/~keith/EMBED/dom.pdf
+
+use super::ControlFlowGraph;
+use super::iterate::reverse_post_order;
+use super::super::indexed_vec::{IndexVec, Idx};
+
+use std::fmt;
+
+#[cfg(test)]
+mod test;
+
+pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
+    let start_node = graph.start_node();
+    let rpo = reverse_post_order(graph, start_node);
+    dominators_given_rpo(graph, &rpo)
+}
+
+pub fn dominators_given_rpo<G: ControlFlowGraph>(graph: &G,
+                                                 rpo: &[G::Node])
+                                                 -> Dominators<G::Node> {
+    let start_node = graph.start_node();
+    assert_eq!(rpo[0], start_node);
+
+    // compute the post order index (rank) for each node
+    let mut post_order_rank: IndexVec<G::Node, usize> = IndexVec::from_elem_n(usize::default(),
+                                                                              graph.num_nodes());
+    for (index, node) in rpo.iter().rev().cloned().enumerate() {
+        post_order_rank[node] = index;
+    }
+
+    let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
+        IndexVec::from_elem_n(Option::default(), graph.num_nodes());
+    immediate_dominators[start_node] = Some(start_node);
+
+    let mut changed = true;
+    while changed {
+        changed = false;
+
+        for &node in &rpo[1..] {
+            let mut new_idom = None;
+            for pred in graph.predecessors(node) {
+                if immediate_dominators[pred].is_some() {
+                    // (*)
+                    // (*) dominators for `pred` have been calculated
+                    new_idom = intersect_opt(&post_order_rank,
+                                                  &immediate_dominators,
+                                                  new_idom,
+                                                  Some(pred));
+                }
+            }
+
+            if new_idom != immediate_dominators[node] {
+                immediate_dominators[node] = new_idom;
+                changed = true;
+            }
+        }
+    }
+
+    Dominators {
+        post_order_rank: post_order_rank,
+        immediate_dominators: immediate_dominators,
+    }
+}
+
+fn intersect_opt<Node: Idx>(post_order_rank: &IndexVec<Node, usize>,
+                                      immediate_dominators: &IndexVec<Node, Option<Node>>,
+                                      node1: Option<Node>,
+                                      node2: Option<Node>)
+                                      -> Option<Node> {
+    match (node1, node2) {
+        (None, None) => None,
+        (Some(n), None) | (None, Some(n)) => Some(n),
+        (Some(n1), Some(n2)) => Some(intersect(post_order_rank, immediate_dominators, n1, n2)),
+    }
+}
+
+fn intersect<Node: Idx>(post_order_rank: &IndexVec<Node, usize>,
+                                  immediate_dominators: &IndexVec<Node, Option<Node>>,
+                                  mut node1: Node,
+                                  mut node2: Node)
+                                  -> Node {
+    while node1 != node2 {
+        while post_order_rank[node1] < post_order_rank[node2] {
+            node1 = immediate_dominators[node1].unwrap();
+        }
+
+        while post_order_rank[node2] < post_order_rank[node1] {
+            node2 = immediate_dominators[node2].unwrap();
+        }
+    }
+    return node1;
+}
+
+#[derive(Clone, Debug)]
+pub struct Dominators<N: Idx> {
+    post_order_rank: IndexVec<N, usize>,
+    immediate_dominators: IndexVec<N, Option<N>>,
+}
+
+impl<Node: Idx> Dominators<Node> {
+    pub fn is_reachable(&self, node: Node) -> bool {
+        self.immediate_dominators[node].is_some()
+    }
+
+    pub fn immediate_dominator(&self, node: Node) -> Node {
+        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
+        self.immediate_dominators[node].unwrap()
+    }
+
+    pub fn dominators(&self, node: Node) -> Iter<Node> {
+        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
+        Iter {
+            dominators: self,
+            node: Some(node),
+        }
+    }
+
+    pub fn is_dominated_by(&self, node: Node, dom: Node) -> bool {
+        // FIXME -- could be optimized by using post-order-rank
+        self.dominators(node).any(|n| n == dom)
+    }
+
+    pub fn mutual_dominator_node(&self, node1: Node, node2: Node) -> Node {
+        assert!(self.is_reachable(node1),
+                "node {:?} is not reachable",
+                node1);
+        assert!(self.is_reachable(node2),
+                "node {:?} is not reachable",
+                node2);
+        intersect::<Node>(&self.post_order_rank,
+                  &self.immediate_dominators,
+                  node1,
+                  node2)
+    }
+
+    pub fn mutual_dominator<I>(&self, iter: I) -> Option<Node>
+        where I: IntoIterator<Item = Node>
+    {
+        let mut iter = iter.into_iter();
+        iter.next()
+            .map(|dom| iter.fold(dom, |dom, node| self.mutual_dominator_node(dom, node)))
+    }
+
+    pub fn all_immediate_dominators(&self) -> &IndexVec<Node, Option<Node>> {
+        &self.immediate_dominators
+    }
+
+    pub fn dominator_tree(&self) -> DominatorTree<Node> {
+        let elem: Vec<Node> = Vec::new();
+        let mut children: IndexVec<Node, Vec<Node>> =
+            IndexVec::from_elem_n(elem, self.immediate_dominators.len());
+        let mut root = None;
+        for (index, immed_dom) in self.immediate_dominators.iter().enumerate() {
+            let node = Node::new(index);
+            match *immed_dom {
+                None => {
+                    // node not reachable
+                }
+                Some(immed_dom) => {
+                    if node == immed_dom {
+                        root = Some(node);
+                    } else {
+                        children[immed_dom].push(node);
+                    }
+                }
+            }
+        }
+        DominatorTree {
+            root: root.unwrap(),
+            children: children,
+        }
+    }
+}
+
+pub struct Iter<'dom, Node: Idx + 'dom> {
+    dominators: &'dom Dominators<Node>,
+    node: Option<Node>,
+}
+
+impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
+    type Item = Node;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(node) = self.node {
+            let dom = self.dominators.immediate_dominator(node);
+            if dom == node {
+                self.node = None; // reached the root
+            } else {
+                self.node = Some(dom);
+            }
+            return Some(node);
+        } else {
+            return None;
+        }
+    }
+}
+
+pub struct DominatorTree<N: Idx> {
+    root: N,
+    children: IndexVec<N, Vec<N>>,
+}
+
+impl<Node: Idx> DominatorTree<Node> {
+    pub fn root(&self) -> Node {
+        self.root
+    }
+
+    pub fn children(&self, node: Node) -> &[Node] {
+        &self.children[node]
+    }
+
+    pub fn iter_children_of(&self, node: Node) -> IterChildrenOf<Node> {
+        IterChildrenOf {
+            tree: self,
+            stack: vec![node],
+        }
+    }
+}
+
+pub struct IterChildrenOf<'iter, Node: Idx + 'iter> {
+    tree: &'iter DominatorTree<Node>,
+    stack: Vec<Node>,
+}
+
+impl<'iter, Node: Idx> Iterator for IterChildrenOf<'iter, Node> {
+    type Item = Node;
+
+    fn next(&mut self) -> Option<Node> {
+        if let Some(node) = self.stack.pop() {
+            self.stack.extend(self.tree.children(node));
+            Some(node)
+        } else {
+            None
+        }
+    }
+}
+
+impl<Node: Idx> fmt::Debug for DominatorTree<Node> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        fmt::Debug::fmt(&DominatorTreeNode {
+                            tree: self,
+                            node: self.root,
+                        },
+                        fmt)
+    }
+}
+
+struct DominatorTreeNode<'tree, Node: Idx> {
+    tree: &'tree DominatorTree<Node>,
+    node: Node,
+}
+
+impl<'tree, Node: Idx> fmt::Debug for DominatorTreeNode<'tree, Node> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        let subtrees: Vec<_> = self.tree
+            .children(self.node)
+            .iter()
+            .map(|&child| {
+                DominatorTreeNode {
+                    tree: self.tree,
+                    node: child,
+                }
+            })
+            .collect();
+        fmt.debug_tuple("")
+            .field(&self.node)
+            .field(&subtrees)
+            .finish()
+    }
+}
diff --git a/src/librustc_data_structures/control_flow_graph/dominators/test.rs b/src/librustc_data_structures/control_flow_graph/dominators/test.rs
new file mode 100644 (file)
index 0000000..a6db5f2
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::super::test::TestGraph;
+
+use super::*;
+
+#[test]
+fn diamond() {
+    let graph = TestGraph::new(0, &[
+        (0, 1),
+        (0, 2),
+        (1, 3),
+        (2, 3),
+    ]);
+
+    let dominators = dominators(&graph);
+    let immediate_dominators = dominators.all_immediate_dominators();
+    assert_eq!(immediate_dominators[0], Some(0));
+    assert_eq!(immediate_dominators[1], Some(0));
+    assert_eq!(immediate_dominators[2], Some(0));
+    assert_eq!(immediate_dominators[3], Some(0));
+}
+
+#[test]
+fn paper() {
+    // example from the paper:
+    let graph = TestGraph::new(6, &[
+        (6, 5),
+        (6, 4),
+        (5, 1),
+        (4, 2),
+        (4, 3),
+        (1, 2),
+        (2, 3),
+        (3, 2),
+        (2, 1),
+    ]);
+
+    let dominators = dominators(&graph);
+    let immediate_dominators = dominators.all_immediate_dominators();
+    assert_eq!(immediate_dominators[0], None); // <-- note that 0 is not in graph
+    assert_eq!(immediate_dominators[1], Some(6));
+    assert_eq!(immediate_dominators[2], Some(6));
+    assert_eq!(immediate_dominators[3], Some(6));
+    assert_eq!(immediate_dominators[4], Some(6));
+    assert_eq!(immediate_dominators[5], Some(6));
+    assert_eq!(immediate_dominators[6], Some(6));
+}
+
diff --git a/src/librustc_data_structures/control_flow_graph/iterate/mod.rs b/src/librustc_data_structures/control_flow_graph/iterate/mod.rs
new file mode 100644 (file)
index 0000000..11b557c
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::ControlFlowGraph;
+use super::super::indexed_vec::IndexVec;
+
+#[cfg(test)]
+mod test;
+
+pub fn post_order_from<G: ControlFlowGraph>(graph: &G, start_node: G::Node) -> Vec<G::Node> {
+    post_order_from_to(graph, start_node, None)
+}
+
+pub fn post_order_from_to<G: ControlFlowGraph>(graph: &G,
+                                               start_node: G::Node,
+                                               end_node: Option<G::Node>)
+                                               -> Vec<G::Node> {
+    let mut visited: IndexVec<G::Node, bool> = IndexVec::from_elem_n(false, graph.num_nodes());
+    let mut result: Vec<G::Node> = Vec::with_capacity(graph.num_nodes());
+    if let Some(end_node) = end_node {
+        visited[end_node] = true;
+    }
+    post_order_walk(graph, start_node, &mut result, &mut visited);
+    result
+}
+
+fn post_order_walk<G: ControlFlowGraph>(graph: &G,
+                                        node: G::Node,
+                                        result: &mut Vec<G::Node>,
+                                        visited: &mut IndexVec<G::Node, bool>) {
+    if visited[node] {
+        return;
+    }
+    visited[node] = true;
+
+    for successor in graph.successors(node) {
+        post_order_walk(graph, successor, result, visited);
+    }
+
+    result.push(node);
+}
+
+pub fn pre_order_walk<G: ControlFlowGraph>(graph: &G,
+                                           node: G::Node,
+                                           result: &mut Vec<G::Node>,
+                                           visited: &mut IndexVec<G::Node, bool>) {
+    if visited[node] {
+        return;
+    }
+    visited[node] = true;
+
+    result.push(node);
+
+    for successor in graph.successors(node) {
+        pre_order_walk(graph, successor, result, visited);
+    }
+}
+
+pub fn reverse_post_order<G: ControlFlowGraph>(graph: &G, start_node: G::Node) -> Vec<G::Node> {
+    let mut vec = post_order_from(graph, start_node);
+    vec.reverse();
+    vec
+}
diff --git a/src/librustc_data_structures/control_flow_graph/iterate/test.rs b/src/librustc_data_structures/control_flow_graph/iterate/test.rs
new file mode 100644 (file)
index 0000000..28297d5
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::super::test::TestGraph;
+use super::super::transpose::TransposedGraph;
+
+use super::*;
+
+#[test]
+fn diamond_post_order() {
+    let graph = TestGraph::new(0, &[
+        (0, 1),
+        (0, 2),
+        (1, 3),
+        (2, 3),
+    ]);
+
+    let result = post_order_from(&graph, 0);
+    assert_eq!(result, vec![3, 1, 2, 0]);
+}
+
+
+#[test]
+fn rev_post_order_inner_loop() {
+    // 0 -> 1 ->     2     -> 3 -> 5
+    //      ^     ^    v      |
+    //      |     6 <- 4      |
+    //      +-----------------+
+    let graph = TestGraph::new(0, &[
+        (0, 1),
+        (1, 2),
+        (2, 3),
+        (3, 5),
+        (3, 1),
+        (2, 4),
+        (4, 6),
+        (6, 2),
+    ]);
+
+    let rev_graph = TransposedGraph::new(&graph);
+
+    let result = post_order_from_to(&rev_graph, 6, Some(2));
+    assert_eq!(result, vec![4, 6]);
+
+    let result = post_order_from_to(&rev_graph, 3, Some(1));
+    assert_eq!(result, vec![4, 6, 2, 3]);
+}
+
diff --git a/src/librustc_data_structures/control_flow_graph/mod.rs b/src/librustc_data_structures/control_flow_graph/mod.rs
new file mode 100644 (file)
index 0000000..f9e75b1
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::indexed_vec::Idx;
+pub use std::slice::Iter;
+
+pub mod dominators;
+pub mod iterate;
+pub mod reachable;
+mod reference;
+pub mod transpose;
+
+#[cfg(test)]
+mod test;
+
+pub trait ControlFlowGraph
+    where Self: for<'graph> GraphPredecessors<'graph, Item=<Self as ControlFlowGraph>::Node>,
+          Self: for<'graph> GraphSuccessors<'graph, Item=<Self as ControlFlowGraph>::Node>
+{
+    type Node: Idx;
+
+    fn num_nodes(&self) -> usize;
+    fn start_node(&self) -> Self::Node;
+    fn predecessors<'graph>(&'graph self, node: Self::Node)
+                            -> <Self as GraphPredecessors<'graph>>::Iter;
+    fn successors<'graph>(&'graph self, node: Self::Node)
+                            -> <Self as GraphSuccessors<'graph>>::Iter;
+}
+
+pub trait GraphPredecessors<'graph> {
+    type Item;
+    type Iter: Iterator<Item=Self::Item>;
+}
+
+pub trait GraphSuccessors<'graph> {
+    type Item;
+    type Iter: Iterator<Item=Self::Item>;
+}
\ No newline at end of file
diff --git a/src/librustc_data_structures/control_flow_graph/reachable/mod.rs b/src/librustc_data_structures/control_flow_graph/reachable/mod.rs
new file mode 100644 (file)
index 0000000..e520e23
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Compute reachability using a simple dataflow propagation.
+//! Store end-result in a big NxN bit matrix.
+
+use super::ControlFlowGraph;
+use super::super::bitvec::BitVector;
+use super::iterate::reverse_post_order;
+use super::super::indexed_vec::{IndexVec, Idx};
+
+#[cfg(test)]
+mod test;
+
+pub fn reachable<G: ControlFlowGraph>(graph: &G)
+                                      -> Reachability<G::Node> {
+    let reverse_post_order = reverse_post_order(graph, graph.start_node());
+    reachable_given_rpo(graph, &reverse_post_order)
+}
+
+pub fn reachable_given_rpo<G: ControlFlowGraph>(graph: &G,
+                                                reverse_post_order: &[G::Node])
+                                                -> Reachability<G::Node> {
+    let mut reachability = Reachability::new(graph);
+    let mut changed = true;
+    while changed {
+        changed = false;
+        for &node in reverse_post_order.iter().rev() {
+            // every node can reach itself
+            changed |= reachability.bits[node].insert(node.index());
+
+            // and every pred can reach everything node can reach
+            for pred in graph.predecessors(node) {
+                let nodes_bits = reachability.bits[node].clone();
+                changed |= reachability.bits[pred].insert_all(&nodes_bits);
+            }
+        }
+    }
+    reachability
+}
+
+pub struct Reachability<Node: Idx> {
+    bits: IndexVec<Node, BitVector>,
+}
+
+impl<Node: Idx> Reachability<Node> {
+    fn new<G: ControlFlowGraph>(graph: &G) -> Self {
+        let num_nodes = graph.num_nodes();
+        Reachability {
+            bits: IndexVec::from_elem_n(BitVector::new(num_nodes), num_nodes),
+        }
+    }
+
+    pub fn can_reach(&self, source: Node, target: Node)-> bool {
+        let bit: usize = target.index();
+        self.bits[source].contains(bit)
+    }
+}
diff --git a/src/librustc_data_structures/control_flow_graph/reachable/test.rs b/src/librustc_data_structures/control_flow_graph/reachable/test.rs
new file mode 100644 (file)
index 0000000..6aa906a
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::super::test::TestGraph;
+
+use super::*;
+
+#[test]
+fn test1() {
+    // 0 -> 1 -> 2 -> 3
+    //      ^    v
+    //      6 <- 4 -> 5
+    let graph = TestGraph::new(0, &[
+        (0, 1),
+        (1, 2),
+        (2, 3),
+        (2, 4),
+        (4, 5),
+        (4, 6),
+        (6, 1),
+    ]);
+    let reachable = reachable(&graph);
+    assert!((0..6).all(|i| reachable.can_reach(0, i)));
+    assert!((1..6).all(|i| reachable.can_reach(1, i)));
+    assert!((1..6).all(|i| reachable.can_reach(2, i)));
+    assert!((1..6).all(|i| reachable.can_reach(4, i)));
+    assert!((1..6).all(|i| reachable.can_reach(6, i)));
+    assert!(reachable.can_reach(3, 3));
+    assert!(!reachable.can_reach(3, 5));
+    assert!(!reachable.can_reach(5, 3));
+}
+
+/// use bigger indices to cross between words in the bit set
+#[test]
+fn test2() {
+    // 30 -> 31 -> 32 -> 33
+    //       ^      v
+    //       36 <- 34 -> 35
+    let graph = TestGraph::new(30, &[
+        (30, 31),
+        (31, 32),
+        (32, 33),
+        (32, 34),
+        (34, 35),
+        (34, 36),
+        (36, 31),
+    ]);
+    let reachable = reachable(&graph);
+    assert!((30..36).all(|i| reachable.can_reach(30, i)));
+    assert!((31..36).all(|i| reachable.can_reach(31, i)));
+    assert!((31..36).all(|i| reachable.can_reach(32, i)));
+    assert!((31..36).all(|i| reachable.can_reach(34, i)));
+    assert!((31..36).all(|i| reachable.can_reach(36, i)));
+    assert!(reachable.can_reach(33, 33));
+    assert!(!reachable.can_reach(33, 35));
+    assert!(!reachable.can_reach(35, 33));
+}
diff --git a/src/librustc_data_structures/control_flow_graph/reference.rs b/src/librustc_data_structures/control_flow_graph/reference.rs
new file mode 100644 (file)
index 0000000..d735be1
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::*;
+
+impl<'graph, G: ControlFlowGraph> ControlFlowGraph for &'graph G {
+    type Node = G::Node;
+
+    fn num_nodes(&self) -> usize {
+        (**self).num_nodes()
+    }
+
+    fn start_node(&self) -> Self::Node {
+        (**self).start_node()
+    }
+
+    fn predecessors<'iter>(&'iter self, node: Self::Node)
+                            -> <Self as GraphPredecessors<'iter>>::Iter {
+        (**self).predecessors(node)
+    }
+
+    fn successors<'iter>(&'iter self, node: Self::Node)
+                          -> <Self as GraphSuccessors<'iter>>::Iter {
+        (**self).successors(node)
+    }
+}
+
+impl<'iter, 'graph, G: ControlFlowGraph> GraphPredecessors<'iter> for &'graph G {
+    type Item = G::Node;
+    type Iter = <G as GraphPredecessors<'iter>>::Iter;
+}
+
+impl<'iter, 'graph, G: ControlFlowGraph> GraphSuccessors<'iter> for &'graph G {
+    type Item = G::Node;
+    type Iter = <G as GraphSuccessors<'iter>>::Iter;
+}
diff --git a/src/librustc_data_structures/control_flow_graph/test.rs b/src/librustc_data_structures/control_flow_graph/test.rs
new file mode 100644 (file)
index 0000000..57b2a85
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashMap;
+use std::cmp::max;
+use std::slice;
+use std::iter;
+
+use super::{ControlFlowGraph, GraphPredecessors, GraphSuccessors};
+
+pub struct TestGraph {
+    num_nodes: usize,
+    start_node: usize,
+    successors: HashMap<usize, Vec<usize>>,
+    predecessors: HashMap<usize, Vec<usize>>,
+}
+
+impl TestGraph {
+    pub fn new(start_node: usize, edges: &[(usize, usize)]) -> Self {
+        let mut graph = TestGraph {
+            num_nodes: start_node + 1,
+            start_node: start_node,
+            successors: HashMap::new(),
+            predecessors: HashMap::new()
+        };
+        for &(source, target) in edges {
+            graph.num_nodes = max(graph.num_nodes, source + 1);
+            graph.num_nodes = max(graph.num_nodes, target + 1);
+            graph.successors.entry(source).or_insert(vec![]).push(target);
+            graph.predecessors.entry(target).or_insert(vec![]).push(source);
+        }
+        for node in 0..graph.num_nodes {
+            graph.successors.entry(node).or_insert(vec![]);
+            graph.predecessors.entry(node).or_insert(vec![]);
+        }
+        graph
+    }
+}
+
+impl ControlFlowGraph for TestGraph {
+    type Node = usize;
+
+    fn start_node(&self) -> usize {
+        self.start_node
+    }
+
+    fn num_nodes(&self) -> usize {
+        self.num_nodes
+    }
+
+    fn predecessors<'graph>(&'graph self, node: usize)
+                            -> <Self as GraphPredecessors<'graph>>::Iter {
+       self.predecessors[&node].iter().cloned()
+    }
+
+    fn successors<'graph>(&'graph self, node: usize)
+                            -> <Self as GraphSuccessors<'graph>>::Iter {
+        self.successors[&node].iter().cloned()
+    }
+}
+
+impl<'graph> GraphPredecessors<'graph> for TestGraph {
+    type Item = usize;
+    type Iter = iter::Cloned<slice::Iter<'graph, usize>>;
+}
+
+impl<'graph> GraphSuccessors<'graph> for TestGraph {
+    type Item = usize;
+    type Iter = iter::Cloned<slice::Iter<'graph, usize>>;
+}
+
diff --git a/src/librustc_data_structures/control_flow_graph/transpose.rs b/src/librustc_data_structures/control_flow_graph/transpose.rs
new file mode 100644 (file)
index 0000000..792e079
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::*;
+
+pub struct TransposedGraph<G: ControlFlowGraph> {
+    base_graph: G,
+    start_node: G::Node,
+}
+
+impl<G: ControlFlowGraph> TransposedGraph<G> {
+    pub fn new(base_graph: G) -> Self {
+        let start_node = base_graph.start_node();
+        Self::with_start(base_graph, start_node)
+    }
+
+    pub fn with_start(base_graph: G, start_node: G::Node) -> Self {
+        TransposedGraph { base_graph: base_graph, start_node: start_node }
+    }
+}
+
+impl<G: ControlFlowGraph> ControlFlowGraph for TransposedGraph<G> {
+    type Node = G::Node;
+
+    fn num_nodes(&self) -> usize {
+        self.base_graph.num_nodes()
+    }
+
+    fn start_node(&self) -> Self::Node {
+        self.start_node
+    }
+
+    fn predecessors<'graph>(&'graph self, node: Self::Node)
+                            -> <Self as GraphPredecessors<'graph>>::Iter {
+        self.base_graph.successors(node)
+    }
+
+    fn successors<'graph>(&'graph self, node: Self::Node)
+                          -> <Self as GraphSuccessors<'graph>>::Iter {
+        self.base_graph.predecessors(node)
+    }
+}
+
+impl<'graph, G: ControlFlowGraph> GraphPredecessors<'graph> for TransposedGraph<G> {
+    type Item = G::Node;
+    type Iter = <G as GraphSuccessors<'graph>>::Iter;
+}
+
+impl<'graph, G: ControlFlowGraph> GraphSuccessors<'graph> for TransposedGraph<G> {
+    type Item = G::Node;
+    type Iter = <G as GraphPredecessors<'graph>>::Iter;
+}
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
new file mode 100644 (file)
index 0000000..9123463
--- /dev/null
@@ -0,0 +1,242 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fmt::Debug;
+use std::iter::{self, FromIterator};
+use std::slice;
+use std::marker::PhantomData;
+use std::ops::{Index, IndexMut, Range};
+use std::fmt;
+use std::vec;
+use std::u32;
+
+use rustc_serialize as serialize;
+
+/// Represents some newtyped `usize` wrapper.
+///
+/// (purpose: avoid mixing indexes for different bitvector domains.)
+pub trait Idx: Copy + 'static + Eq + Debug {
+    fn new(usize) -> Self;
+    fn index(self) -> usize;
+}
+
+impl Idx for usize {
+    fn new(idx: usize) -> Self { idx }
+    fn index(self) -> usize { self }
+}
+
+impl Idx for u32 {
+    fn new(idx: usize) -> Self { assert!(idx <= u32::MAX as usize); idx as u32 }
+    fn index(self) -> usize { self as usize }
+}
+
+#[derive(Clone)]
+pub struct IndexVec<I: Idx, T> {
+    pub raw: Vec<T>,
+    _marker: PhantomData<Fn(&I)>
+}
+
+impl<I: Idx, T: serialize::Encodable> serialize::Encodable for IndexVec<I, T> {
+    fn encode<S: serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        serialize::Encodable::encode(&self.raw, s)
+    }
+}
+
+impl<I: Idx, T: serialize::Decodable> serialize::Decodable for IndexVec<I, T> {
+    fn decode<D: serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
+        serialize::Decodable::decode(d).map(|v| {
+            IndexVec { raw: v, _marker: PhantomData }
+        })
+    }
+}
+
+impl<I: Idx, T: fmt::Debug> fmt::Debug for IndexVec<I, T> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(&self.raw, fmt)
+    }
+}
+
+pub type Enumerated<I, J> = iter::Map<iter::Enumerate<J>, IntoIdx<I>>;
+
+impl<I: Idx, T> IndexVec<I, T> {
+    #[inline]
+    pub fn new() -> Self {
+        IndexVec { raw: Vec::new(), _marker: PhantomData }
+    }
+
+    #[inline]
+    pub fn with_capacity(capacity: usize) -> Self {
+        IndexVec { raw: Vec::with_capacity(capacity), _marker: PhantomData }
+    }
+
+    #[inline]
+    pub fn from_elem<S>(elem: T, universe: &IndexVec<I, S>) -> Self
+        where T: Clone
+    {
+        IndexVec { raw: vec![elem; universe.len()], _marker: PhantomData }
+    }
+
+    #[inline]
+    pub fn from_elem_n(elem: T, n: usize) -> Self
+        where T: Clone
+    {
+        IndexVec { raw: vec![elem; n], _marker: PhantomData }
+    }
+
+    #[inline]
+    pub fn push(&mut self, d: T) -> I {
+        let idx = I::new(self.len());
+        self.raw.push(d);
+        idx
+    }
+
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.raw.len()
+    }
+
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.raw.is_empty()
+    }
+
+    #[inline]
+    pub fn into_iter(self) -> vec::IntoIter<T> {
+        self.raw.into_iter()
+    }
+
+    #[inline]
+    pub fn into_iter_enumerated(self) -> Enumerated<I, vec::IntoIter<T>>
+    {
+        self.raw.into_iter().enumerate().map(IntoIdx { _marker: PhantomData })
+    }
+
+    #[inline]
+    pub fn iter(&self) -> slice::Iter<T> {
+        self.raw.iter()
+    }
+
+    #[inline]
+    pub fn iter_enumerated(&self) -> Enumerated<I, slice::Iter<T>>
+    {
+        self.raw.iter().enumerate().map(IntoIdx { _marker: PhantomData })
+    }
+
+    #[inline]
+    pub fn indices(&self) -> iter::Map<Range<usize>, IntoIdx<I>> {
+        (0..self.len()).map(IntoIdx { _marker: PhantomData })
+    }
+
+    #[inline]
+    pub fn iter_mut(&mut self) -> slice::IterMut<T> {
+        self.raw.iter_mut()
+    }
+
+    #[inline]
+    pub fn iter_enumerated_mut(&mut self) -> Enumerated<I, slice::IterMut<T>>
+    {
+        self.raw.iter_mut().enumerate().map(IntoIdx { _marker: PhantomData })
+    }
+
+    #[inline]
+    pub fn last(&self) -> Option<I> {
+        self.len().checked_sub(1).map(I::new)
+    }
+}
+
+impl<I: Idx, T> Index<I> for IndexVec<I, T> {
+    type Output = T;
+
+    #[inline]
+    fn index(&self, index: I) -> &T {
+        &self.raw[index.index()]
+    }
+}
+
+impl<I: Idx, T> IndexMut<I> for IndexVec<I, T> {
+    #[inline]
+    fn index_mut(&mut self, index: I) -> &mut T {
+        &mut self.raw[index.index()]
+    }
+}
+
+impl<I: Idx, T> Extend<T> for IndexVec<I, T> {
+    #[inline]
+    fn extend<J: IntoIterator<Item = T>>(&mut self, iter: J) {
+        self.raw.extend(iter);
+    }
+}
+
+impl<I: Idx, T> FromIterator<T> for IndexVec<I, T> {
+    #[inline]
+    fn from_iter<J>(iter: J) -> Self where J: IntoIterator<Item=T> {
+        IndexVec { raw: FromIterator::from_iter(iter), _marker: PhantomData }
+    }
+}
+
+impl<I: Idx, T> IntoIterator for IndexVec<I, T> {
+    type Item = T;
+    type IntoIter = vec::IntoIter<T>;
+
+    #[inline]
+    fn into_iter(self) -> vec::IntoIter<T> {
+        self.raw.into_iter()
+    }
+
+}
+
+impl<'a, I: Idx, T> IntoIterator for &'a IndexVec<I, T> {
+    type Item = &'a T;
+    type IntoIter = slice::Iter<'a, T>;
+
+    #[inline]
+    fn into_iter(self) -> slice::Iter<'a, T> {
+        self.raw.iter()
+    }
+}
+
+impl<'a, I: Idx, T> IntoIterator for &'a mut IndexVec<I, T> {
+    type Item = &'a mut T;
+    type IntoIter = slice::IterMut<'a, T>;
+
+    #[inline]
+    fn into_iter(mut self) -> slice::IterMut<'a, T> {
+        self.raw.iter_mut()
+    }
+}
+
+pub struct IntoIdx<I: Idx> { _marker: PhantomData<fn(&I)> }
+impl<I: Idx, T> FnOnce<((usize, T),)> for IntoIdx<I> {
+    type Output = (I, T);
+
+    extern "rust-call" fn call_once(self, ((n, t),): ((usize, T),)) -> Self::Output {
+        (I::new(n), t)
+    }
+}
+
+impl<I: Idx, T> FnMut<((usize, T),)> for IntoIdx<I> {
+    extern "rust-call" fn call_mut(&mut self, ((n, t),): ((usize, T),)) -> Self::Output {
+        (I::new(n), t)
+    }
+}
+
+impl<I: Idx> FnOnce<(usize,)> for IntoIdx<I> {
+    type Output = I;
+
+    extern "rust-call" fn call_once(self, (n,): (usize,)) -> Self::Output {
+        I::new(n)
+    }
+}
+
+impl<I: Idx> FnMut<(usize,)> for IntoIdx<I> {
+    extern "rust-call" fn call_mut(&mut self, (n,): (usize,)) -> Self::Output {
+        I::new(n)
+    }
+}
index 926ee85230a311e0498b8eaa806d05420ae76bbb..34c3961d5b4c18980c4b2ea36f6729d06dd22f20 100644 (file)
@@ -41,13 +41,16 @@ extern crate serialize as rustc_serialize; // used by deriving
 pub mod bitvec;
 pub mod graph;
 pub mod ivar;
+pub mod indexed_vec;
 pub mod obligation_forest;
+pub mod snapshot_map;
 pub mod snapshot_vec;
 pub mod transitive_relation;
 pub mod unify;
 pub mod fnv;
 pub mod tuple_slice;
 pub mod veccell;
+pub mod control_flow_graph;
 
 // See comments in src/librustc/lib.rs
 #[doc(hidden)]
diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs
new file mode 100644 (file)
index 0000000..b398901
--- /dev/null
@@ -0,0 +1,138 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use fnv::FnvHashMap;
+use std::hash::Hash;
+use std::ops;
+
+#[cfg(test)]
+mod test;
+
+pub struct SnapshotMap<K, V>
+    where K: Hash + Clone + Eq
+{
+    map: FnvHashMap<K, V>,
+    undo_log: Vec<UndoLog<K, V>>,
+}
+
+pub struct Snapshot {
+    len: usize
+}
+
+enum UndoLog<K, V> {
+    OpenSnapshot,
+    CommittedSnapshot,
+    Inserted(K),
+    Overwrite(K, V),
+}
+
+impl<K, V> SnapshotMap<K, V>
+    where K: Hash + Clone + Eq
+{
+    pub fn new() -> Self {
+        SnapshotMap {
+            map: FnvHashMap(),
+            undo_log: vec![]
+        }
+    }
+
+    pub fn insert(&mut self, key: K, value: V) -> bool {
+        match self.map.insert(key.clone(), value) {
+            None => {
+                if !self.undo_log.is_empty() {
+                    self.undo_log.push(UndoLog::Inserted(key));
+                }
+                true
+            }
+            Some(old_value) => {
+                if !self.undo_log.is_empty() {
+                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
+                }
+                false
+            }
+        }
+    }
+
+    pub fn remove(&mut self, key: K) -> bool {
+        match self.map.remove(&key) {
+            Some(old_value) => {
+                if !self.undo_log.is_empty() {
+                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
+                }
+                true
+            }
+            None => {
+                false
+            }
+        }
+    }
+
+    pub fn get(&self, key: &K) -> Option<&V> {
+        self.map.get(key)
+    }
+
+    pub fn snapshot(&mut self) -> Snapshot {
+        self.undo_log.push(UndoLog::OpenSnapshot);
+        let len = self.undo_log.len() - 1;
+        Snapshot { len: len }
+    }
+
+    fn assert_open_snapshot(&self, snapshot: &Snapshot) {
+        assert!(snapshot.len < self.undo_log.len());
+        assert!(match self.undo_log[snapshot.len] {
+            UndoLog::OpenSnapshot => true,
+            _ => false
+        });
+    }
+
+    pub fn commit(&mut self, snapshot: Snapshot) {
+        self.assert_open_snapshot(&snapshot);
+        if snapshot.len == 0 {
+            // The root snapshot.
+            self.undo_log.truncate(0);
+        } else {
+            self.undo_log[snapshot.len] = UndoLog::CommittedSnapshot;
+        }
+    }
+
+    pub fn rollback_to(&mut self, snapshot: Snapshot) {
+        self.assert_open_snapshot(&snapshot);
+        while self.undo_log.len() > snapshot.len + 1 {
+            match self.undo_log.pop().unwrap() {
+                UndoLog::OpenSnapshot => {
+                    panic!("cannot rollback an uncommitted snapshot");
+                }
+
+                UndoLog::CommittedSnapshot => { }
+
+                UndoLog::Inserted(key) => {
+                    self.map.remove(&key);
+                }
+
+                UndoLog::Overwrite(key, old_value) => {
+                    self.map.insert(key, old_value);
+                }
+            }
+        }
+
+        let v = self.undo_log.pop().unwrap();
+        assert!(match v { UndoLog::OpenSnapshot => true, _ => false });
+        assert!(self.undo_log.len() == snapshot.len);
+    }
+}
+
+impl<'k, K, V> ops::Index<&'k K> for SnapshotMap<K, V>
+    where K: Hash + Clone + Eq
+{
+    type Output = V;
+    fn index(&self, key: &'k K) -> &V {
+        &self.map[key]
+    }
+}
diff --git a/src/librustc_data_structures/snapshot_map/test.rs b/src/librustc_data_structures/snapshot_map/test.rs
new file mode 100644 (file)
index 0000000..4114082
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::SnapshotMap;
+
+#[test]
+fn basic() {
+    let mut map = SnapshotMap::new();
+    map.insert(22, "twenty-two");
+    let snapshot = map.snapshot();
+    map.insert(22, "thirty-three");
+    assert_eq!(map[&22], "thirty-three");
+    map.insert(44, "fourty-four");
+    assert_eq!(map[&44], "fourty-four");
+    assert_eq!(map.get(&33), None);
+    map.rollback_to(snapshot);
+    assert_eq!(map[&22], "twenty-two");
+    assert_eq!(map.get(&33), None);
+    assert_eq!(map.get(&44), None);
+}
+
+#[test]
+#[should_panic]
+fn out_of_order() {
+    let mut map = SnapshotMap::new();
+    map.insert(22, "twenty-two");
+    let snapshot1 = map.snapshot();
+    let _snapshot2 = map.snapshot();
+    map.rollback_to(snapshot1);
+}
+
+#[test]
+fn nested_commit_then_rollback() {
+    let mut map = SnapshotMap::new();
+    map.insert(22, "twenty-two");
+    let snapshot1 = map.snapshot();
+    let snapshot2 = map.snapshot();
+    map.insert(22, "thirty-three");
+    map.commit(snapshot2);
+    assert_eq!(map[&22], "thirty-three");
+    map.rollback_to(snapshot1);
+    assert_eq!(map[&22], "twenty-two");
+}
index 9a90ab8c09d44c58499da17b571a5388886980ac..b7c71dd366469eceaac01605efdafd1bfe33935e 100644 (file)
@@ -46,15 +46,25 @@ impl_tuple_slice!((T, T, T, T, T, T, T, T), 8);
 
 #[test]
 fn test_sliced_tuples() {
-    let t2 = (100i32, 101i32);
-    assert_eq!(t2.as_slice(), &[100i32, 101i32]);
+    let t2 = (100, 101);
+    assert_eq!(t2.as_slice(), &[100, 101]);
 
-    let t3 = (102i32, 103i32, 104i32);
-    assert_eq!(t3.as_slice(), &[102i32, 103i32, 104i32]);
+    let t3 = (102, 103, 104);
+    assert_eq!(t3.as_slice(), &[102, 103, 104]);
 
-    let t4 = (105i32, 106i32, 107i32, 108i32);
-    assert_eq!(t4.as_slice(), &[105i32, 106i32, 107i32, 108i32]);
+    let t4 = (105, 106, 107, 108);
+    assert_eq!(t4.as_slice(), &[105, 106, 107, 108]);
+
+    let t5 = (109, 110, 111, 112, 113);
+    assert_eq!(t5.as_slice(), &[109, 110, 111, 112, 113]);
+
+    let t6 = (114, 115, 116, 117, 118, 119);
+    assert_eq!(t6.as_slice(), &[114, 115, 116, 117, 118, 119]);
+
+    let t7 = (120, 121, 122, 123, 124, 125, 126);
+    assert_eq!(t7.as_slice(), &[120, 121, 122, 123, 124, 125, 126]);
+
+    let t8 = (127, 128, 129, 130, 131, 132, 133, 134);
+    assert_eq!(t8.as_slice(), &[127, 128, 129, 130, 131, 132, 133, 134]);
 
-    let t5 = (109i32, 110i32, 111i32, 112i32, 113i32);
-    assert_eq!(t5.as_slice(), &[109i32, 110i32, 111i32, 112i32, 113i32]);
 }
index 4533946d26ea404fb0d9daae4d4e584cb9f96357..54c62d3665994018d0e7bc0982438b878b3780f5 100644 (file)
@@ -17,6 +17,7 @@ rustc = { path = "../librustc" }
 rustc_back = { path = "../librustc_back" }
 rustc_borrowck = { path = "../librustc_borrowck" }
 rustc_const_eval = { path = "../librustc_const_eval" }
+rustc_errors = { path = "../librustc_errors" }
 rustc_lint = { path = "../librustc_lint" }
 rustc_llvm = { path = "../librustc_llvm" }
 rustc_mir = { path = "../librustc_mir" }
@@ -32,3 +33,4 @@ rustc_metadata = { path = "../librustc_metadata" }
 serialize = { path = "../libserialize" }
 syntax = { path = "../libsyntax" }
 syntax_ext = { path = "../libsyntax_ext" }
+syntax_pos = { path = "../libsyntax_pos" }
\ No newline at end of file
index 1f3df1ff6f2d8f387045383cb2787cb29f8cbc68..277789f5312eee0dd9ce8c73e133714ec6d0832a 100644 (file)
@@ -8,10 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use rustc::dep_graph::DepGraph;
 use rustc::hir;
 use rustc::hir::{map as hir_map, FreevarMap, TraitMap};
 use rustc::hir::def::DefMap;
+use rustc::hir::lowering::lower_crate;
 use rustc_mir as mir;
 use rustc::mir::mir_map::MirMap;
 use rustc::session::{Session, CompileResult, compile_result_from_err_count};
@@ -26,44 +26,39 @@ use rustc::util::nodemap::NodeSet;
 use rustc_back::sha2::{Sha256, Digest};
 use rustc_borrowck as borrowck;
 use rustc_incremental;
-use rustc_resolve as resolve;
+use rustc_resolve::{MakeGlobMap, Resolver};
 use rustc_metadata::macro_import;
 use rustc_metadata::creader::read_local_crates;
 use rustc_metadata::cstore::CStore;
-use rustc_trans::back::link;
-use rustc_trans::back::write;
+use rustc_trans::back::{link, write};
 use rustc_trans as trans;
 use rustc_typeck as typeck;
 use rustc_privacy;
 use rustc_plugin::registry::Registry;
 use rustc_plugin as plugin;
-use rustc::hir::lowering::lower_crate;
-use rustc_passes::{no_asm, loops, consts, rvalues, static_recursion};
+use rustc_passes::{ast_validation, no_asm, loops, consts, rvalues, static_recursion};
 use rustc_const_eval::check_match;
 use super::Compilation;
 
 use serialize::json;
 
-use std::cell::RefCell;
 use std::collections::HashMap;
 use std::env;
 use std::ffi::{OsString, OsStr};
 use std::fs;
 use std::io::{self, Write};
 use std::path::{Path, PathBuf};
-use syntax::ast::{self, NodeIdAssigner};
+use syntax::{ast, diagnostics, visit};
 use syntax::attr::{self, AttrMetaMethods};
-use syntax::diagnostics;
 use syntax::fold::Folder;
 use syntax::parse::{self, PResult, token};
 use syntax::util::node_count::NodeCounter;
-use syntax::visit;
 use syntax;
 use syntax_ext;
 
 #[derive(Clone)]
 pub struct Resolutions {
-    pub def_map: RefCell<DefMap>,
+    pub def_map: DefMap,
     pub freevars: FreevarMap,
     pub trait_map: TraitMap,
     pub maybe_unused_trait_imports: NodeSet,
@@ -95,15 +90,15 @@ pub fn compile_input(sess: &Session,
     // large chunks of memory alive and we want to free them as soon as
     // possible to keep the peak memory usage low
     let (outputs, trans) = {
-        let (outputs, expanded_crate, id) = {
-            let krate = match phase_1_parse_input(sess, cfg, input) {
-                Ok(krate) => krate,
-                Err(mut parse_error) => {
-                    parse_error.emit();
-                    return Err(1);
-                }
-            };
+        let krate = match phase_1_parse_input(sess, cfg, input) {
+            Ok(krate) => krate,
+            Err(mut parse_error) => {
+                parse_error.emit();
+                return Err(1);
+            }
+        };
 
+        let krate = {
             let mut compile_state = CompileState::state_after_parse(input,
                                                                     sess,
                                                                     outdir,
@@ -114,75 +109,33 @@ pub fn compile_input(sess: &Session,
                                     sess,
                                     compile_state,
                                     Ok(()));
-            let krate = compile_state.krate.unwrap();
 
-            let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess);
-            let id = link::find_crate_name(Some(sess), &krate.attrs, input);
-            let expanded_crate = phase_2_configure_and_expand(sess,
-                                                              &cstore,
-                                                              krate,
-                                                              &id,
-                                                              addl_plugins)?;
-
-            (outputs, expanded_crate, id)
+            compile_state.krate.unwrap()
         };
 
-        controller_entry_point!(after_expand,
-                                sess,
-                                CompileState::state_after_expand(input,
-                                                                 sess,
-                                                                 outdir,
-                                                                 output,
-                                                                 &cstore,
-                                                                 &expanded_crate,
-                                                                 &id),
-                                Ok(()));
-
-        write_out_deps(sess, &outputs, &id);
-
-        controller_entry_point!(after_write_deps,
-                                sess,
-                                CompileState::state_after_write_deps(input,
-                                                                     sess,
-                                                                     outdir,
-                                                                     output,
-                                                                     &cstore,
-                                                                     &expanded_crate,
-                                                                     &id),
-                                Ok(()));
-
-        let expanded_crate = assign_node_ids(sess, expanded_crate);
-
-        // Collect defintions for def ids.
-        let mut defs = time(sess.time_passes(),
-                            "collecting defs",
-                            || hir_map::collect_definitions(&expanded_crate));
-
-        time(sess.time_passes(),
-             "external crate/lib resolution",
-             || read_local_crates(sess, &cstore, &defs, &expanded_crate, &id, &sess.dep_graph));
-
-        time(sess.time_passes(),
-             "early lint checks",
-             || lint::check_ast_crate(sess, &expanded_crate));
-
-        let (analysis, resolutions, mut hir_forest) = {
-            lower_and_resolve(sess, &id, &mut defs, &expanded_crate,
-                              &sess.dep_graph, control.make_glob_map)
+        let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess);
+        let id = link::find_crate_name(Some(sess), &krate.attrs, input);
+        let ExpansionResult { expanded_crate, defs, analysis, resolutions, mut hir_forest } = {
+            phase_2_configure_and_expand(
+                sess, &cstore, krate, &id, addl_plugins, control.make_glob_map,
+                |expanded_crate| {
+                    let mut state = CompileState::state_after_expand(
+                        input, sess, outdir, output, &cstore, expanded_crate, &id,
+                    );
+                    controller_entry_point!(after_expand, sess, state, Ok(()));
+                    Ok(())
+                }
+            )?
         };
 
-        // Discard MTWT tables that aren't required past lowering to HIR.
-        if !keep_mtwt_tables(sess) {
-            syntax::ext::mtwt::clear_tables();
-        }
+        write_out_deps(sess, &outputs, &id);
 
         let arenas = ty::CtxtArenas::new();
 
         // Construct the HIR map
-        let hir_forest = &mut hir_forest;
         let hir_map = time(sess.time_passes(),
                            "indexing hir",
-                           move || hir_map::map_crate(hir_forest, defs));
+                           || hir_map::map_crate(&mut hir_forest, defs));
 
         {
             let _ignore = hir_map.dep_graph.in_ignore();
@@ -272,8 +225,15 @@ pub fn compile_input(sess: &Session,
                             phase5_result);
     phase5_result?;
 
+    write::cleanup_llvm(&trans);
+
     phase_6_link_output(sess, &trans, &outputs);
 
+    controller_entry_point!(compilation_done,
+                            sess,
+                            CompileState::state_when_compilation_done(input, sess, outdir, output),
+                            Ok(()));
+
     Ok(())
 }
 
@@ -318,12 +278,12 @@ pub fn source_name(input: &Input) -> String {
 pub struct CompileController<'a> {
     pub after_parse: PhaseController<'a>,
     pub after_expand: PhaseController<'a>,
-    pub after_write_deps: PhaseController<'a>,
     pub after_hir_lowering: PhaseController<'a>,
     pub after_analysis: PhaseController<'a>,
     pub after_llvm: PhaseController<'a>,
+    pub compilation_done: PhaseController<'a>,
 
-    pub make_glob_map: resolve::MakeGlobMap,
+    pub make_glob_map: MakeGlobMap,
 }
 
 impl<'a> CompileController<'a> {
@@ -331,11 +291,11 @@ impl<'a> CompileController<'a> {
         CompileController {
             after_parse: PhaseController::basic(),
             after_expand: PhaseController::basic(),
-            after_write_deps: PhaseController::basic(),
             after_hir_lowering: PhaseController::basic(),
             after_analysis: PhaseController::basic(),
             after_llvm: PhaseController::basic(),
-            make_glob_map: resolve::MakeGlobMap::No,
+            compilation_done: PhaseController::basic(),
+            make_glob_map: MakeGlobMap::No,
         }
     }
 }
@@ -439,23 +399,6 @@ impl<'a, 'b, 'ast, 'tcx> CompileState<'a, 'b, 'ast, 'tcx> {
         }
     }
 
-    fn state_after_write_deps(input: &'a Input,
-                              session: &'ast Session,
-                              out_dir: &'a Option<PathBuf>,
-                              out_file: &'a Option<PathBuf>,
-                              cstore: &'a CStore,
-                              krate: &'a ast::Crate,
-                              crate_name: &'a str)
-                              -> CompileState<'a, 'b, 'ast, 'tcx> {
-        CompileState {
-            crate_name: Some(crate_name),
-            cstore: Some(cstore),
-            expanded_crate: Some(krate),
-            out_file: out_file.as_ref().map(|s| &**s),
-            ..CompileState::empty(input, session, out_dir)
-        }
-    }
-
     fn state_after_hir_lowering(input: &'a Input,
                                 session: &'ast Session,
                                 out_dir: &'a Option<PathBuf>,
@@ -519,6 +462,17 @@ impl<'a, 'b, 'ast, 'tcx> CompileState<'a, 'b, 'ast, 'tcx> {
             ..CompileState::empty(input, session, out_dir)
         }
     }
+
+    fn state_when_compilation_done(input: &'a Input,
+                                    session: &'ast Session,
+                                    out_dir: &'a Option<PathBuf>,
+                                    out_file: &'a Option<PathBuf>)
+                                    -> CompileState<'a, 'b, 'ast, 'tcx> {
+        CompileState {
+            out_file: out_file.as_ref().map(|s| &**s),
+            ..CompileState::empty(input, session, out_dir)
+        }
+    }
 }
 
 pub fn phase_1_parse_input<'a>(sess: &'a Session,
@@ -574,19 +528,31 @@ fn count_nodes(krate: &ast::Crate) -> usize {
 // For continuing compilation after a parsed crate has been
 // modified
 
+pub struct ExpansionResult<'a> {
+    pub expanded_crate: ast::Crate,
+    pub defs: hir_map::Definitions,
+    pub analysis: ty::CrateAnalysis<'a>,
+    pub resolutions: Resolutions,
+    pub hir_forest: hir_map::Forest,
+}
+
 /// Run the "early phases" of the compiler: initial `cfg` processing,
 /// loading compiler plugins (including those from `addl_plugins`),
 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
-/// harness if one is to be provided and injection of a dependency on the
-/// standard library and prelude.
+/// harness if one is to be provided, injection of a dependency on the
+/// standard library and prelude, and name resolution.
 ///
 /// Returns `None` if we're aborting after handling -W help.
-pub fn phase_2_configure_and_expand(sess: &Session,
-                                    cstore: &CStore,
-                                    mut krate: ast::Crate,
-                                    crate_name: &str,
-                                    addl_plugins: Option<Vec<String>>)
-                                    -> Result<ast::Crate, usize> {
+pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
+                                           cstore: &CStore,
+                                           mut krate: ast::Crate,
+                                           crate_name: &'a str,
+                                           addl_plugins: Option<Vec<String>>,
+                                           make_glob_map: MakeGlobMap,
+                                           after_expand: F)
+                                           -> Result<ExpansionResult<'a>, usize>
+    where F: FnOnce(&ast::Crate) -> CompileResult,
+{
     let time_passes = sess.time_passes();
 
     // strip before anything else because crate metadata may use #[cfg_attr]
@@ -597,14 +563,13 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     //
     // baz! should not use this definition unless foo is enabled.
 
-    let mut feature_gated_cfgs = vec![];
-    krate = time(time_passes, "configuration 1", || {
-        sess.track_errors(|| {
-            syntax::config::strip_unconfigured_items(sess.diagnostic(),
-                                                     krate,
-                                                     &mut feature_gated_cfgs)
-        })
-    })?;
+    krate = time(time_passes, "configuration", || {
+        let (krate, features) =
+            syntax::config::strip_unconfigured_items(krate, &sess.parse_sess, sess.opts.test);
+        // these need to be set "early" so that expansion sees `quote` if enabled.
+        *sess.features.borrow_mut() = features;
+        krate
+    });
 
     *sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs);
     sess.crate_disambiguator.set(token::intern(&compute_crate_disambiguator(sess)));
@@ -613,21 +578,11 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         middle::recursion_limit::update_recursion_limit(sess, &krate);
     });
 
-    // these need to be set "early" so that expansion sees `quote` if enabled.
-    sess.track_errors(|| {
-        *sess.features.borrow_mut() =
-            syntax::feature_gate::get_features(&sess.parse_sess.span_diagnostic,
-                                               &krate);
-    })?;
-
     krate = time(time_passes, "crate injection", || {
-        syntax::std_inject::maybe_inject_crates_ref(krate, sess.opts.alt_std_name.clone())
+        let alt_std_name = sess.opts.alt_std_name.clone();
+        syntax::std_inject::maybe_inject_crates_ref(&sess.parse_sess, krate, alt_std_name)
     });
 
-    let macros = time(time_passes,
-                      "macro loading",
-                      || macro_import::read_macro_defs(sess, &cstore, &krate, crate_name));
-
     let mut addl_plugins = Some(addl_plugins);
     let registrars = time(time_passes, "plugin loading", || {
         plugin::load::load_plugins(sess,
@@ -689,12 +644,24 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         // dependent dlls. Note that this uses cfg!(windows) as opposed to
         // targ_cfg because syntax extensions are always loaded for the host
         // compiler, not for the target.
-        let mut _old_path = OsString::new();
+        //
+        // This is somewhat of an inherently racy operation, however, as
+        // multiple threads calling this function could possibly continue
+        // extending PATH far beyond what it should. To solve this for now we
+        // just don't add any new elements to PATH which are already there
+        // within PATH. This is basically a targeted fix at #17360 for rustdoc
+        // which runs rustc in parallel but has been seen (#33844) to cause
+        // problems with PATH becoming too long.
+        let mut old_path = OsString::new();
         if cfg!(windows) {
-            _old_path = env::var_os("PATH").unwrap_or(_old_path);
+            old_path = env::var_os("PATH").unwrap_or(old_path);
             let mut new_path = sess.host_filesearch(PathKind::All)
                                    .get_dylib_search_paths();
-            new_path.extend(env::split_paths(&_old_path));
+            for path in env::split_paths(&old_path) {
+                if !new_path.contains(&path) {
+                    new_path.push(path);
+                }
+            }
             env::set_var("PATH", &env::join_paths(new_path).unwrap());
         }
         let features = sess.features.borrow();
@@ -703,57 +670,31 @@ pub fn phase_2_configure_and_expand(sess: &Session,
             features: Some(&features),
             recursion_limit: sess.recursion_limit.get(),
             trace_mac: sess.opts.debugging_opts.trace_macros,
+            should_test: sess.opts.test,
         };
+        let mut loader = macro_import::MacroLoader::new(sess, &cstore, crate_name);
         let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess,
                                                       krate.config.clone(),
                                                       cfg,
-                                                      &mut feature_gated_cfgs);
+                                                      &mut loader);
         syntax_ext::register_builtins(&mut ecx.syntax_env);
         let (ret, macro_names) = syntax::ext::expand::expand_crate(ecx,
-                                                                   macros,
                                                                    syntax_exts,
                                                                    krate);
         if cfg!(windows) {
-            env::set_var("PATH", &_old_path);
+            env::set_var("PATH", &old_path);
         }
         *sess.available_macros.borrow_mut() = macro_names;
         ret
     });
 
-    // JBC: make CFG processing part of expansion to avoid this problem:
-
-    // strip again, in case expansion added anything with a #[cfg].
-    krate = sess.track_errors(|| {
-        let krate = time(time_passes, "configuration 2", || {
-            syntax::config::strip_unconfigured_items(sess.diagnostic(),
-                                                     krate,
-                                                     &mut feature_gated_cfgs)
-        });
-
-        time(time_passes, "gated configuration checking", || {
-            let features = sess.features.borrow();
-            feature_gated_cfgs.sort();
-            feature_gated_cfgs.dedup();
-            for cfg in &feature_gated_cfgs {
-                cfg.check_and_emit(sess.diagnostic(), &features, sess.codemap());
-            }
-        });
-
-        krate
-    })?;
-
     krate = time(time_passes, "maybe building test harness", || {
-        syntax::test::modify_for_testing(&sess.parse_sess, &sess.opts.cfg, krate, sess.diagnostic())
+        syntax::test::modify_for_testing(&sess.parse_sess,
+                                         sess.opts.test,
+                                         krate,
+                                         sess.diagnostic())
     });
 
-    krate = time(time_passes,
-                 "prelude injection",
-                 || syntax::std_inject::maybe_inject_prelude(&sess.parse_sess, krate));
-
-    time(time_passes,
-         "checking that all macro invocations are gone",
-         || syntax::ext::expand::check_for_macros(&sess.parse_sess, &krate));
-
     time(time_passes,
          "checking for inline asm in case the target doesn't support it",
          || no_asm::check_crate(sess, &krate));
@@ -761,12 +702,11 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     // Needs to go *after* expansion to be able to check the results of macro expansion.
     time(time_passes, "complete gated feature checking", || {
         sess.track_errors(|| {
-            let features = syntax::feature_gate::check_crate(sess.codemap(),
-                                                             &sess.parse_sess.span_diagnostic,
-                                                             &krate,
-                                                             &attributes,
-                                                             sess.opts.unstable_features);
-            *sess.features.borrow_mut() = features;
+            syntax::feature_gate::check_crate(&krate,
+                                              &sess.parse_sess,
+                                              &sess.features.borrow(),
+                                              &attributes,
+                                              sess.opts.unstable_features);
         })
     })?;
 
@@ -774,10 +714,79 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         println!("Post-expansion node count: {}", count_nodes(&krate));
     }
 
-    Ok(krate)
+    krate = assign_node_ids(sess, krate);
+
+    let resolver_arenas = Resolver::arenas();
+    let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas);
+
+    // Collect defintions for def ids.
+    time(sess.time_passes(), "collecting defs", || resolver.definitions.collect(&krate));
+
+    time(sess.time_passes(), "external crate/lib resolution", || {
+        let defs = &resolver.definitions;
+        read_local_crates(sess, &cstore, defs, &krate, crate_name, &sess.dep_graph)
+    });
+
+    time(sess.time_passes(),
+         "early lint checks",
+         || lint::check_ast_crate(sess, &krate));
+
+    time(sess.time_passes(),
+         "AST validation",
+         || ast_validation::check_crate(sess, &krate));
+
+    time(sess.time_passes(), "name resolution", || -> CompileResult {
+        // Currently, we ignore the name resolution data structures for the purposes of dependency
+        // tracking. Instead we will run name resolution and include its output in the hash of each
+        // item, much like we do for macro expansion. In other words, the hash reflects not just
+        // its contents but the results of name resolution on those contents. Hopefully we'll push
+        // this back at some point.
+        let _ignore = sess.dep_graph.in_ignore();
+        resolver.build_reduced_graph(&krate);
+        resolver.resolve_imports();
+
+        // Since import resolution will eventually happen in expansion,
+        // don't perform `after_expand` until after import resolution.
+        after_expand(&krate)?;
+
+        resolver.resolve_crate(&krate);
+        Ok(())
+    })?;
+
+    // Lower ast -> hir.
+    let hir_forest = time(sess.time_passes(), "lowering ast -> hir", || {
+        hir_map::Forest::new(lower_crate(sess, &krate, &mut resolver), &sess.dep_graph)
+    });
+
+    // Discard MTWT tables that aren't required past lowering to HIR.
+    if !keep_mtwt_tables(sess) {
+        syntax::ext::mtwt::clear_tables();
+    }
+
+    Ok(ExpansionResult {
+        expanded_crate: krate,
+        defs: resolver.definitions,
+        analysis: ty::CrateAnalysis {
+            export_map: resolver.export_map,
+            access_levels: AccessLevels::default(),
+            reachable: NodeSet(),
+            name: crate_name,
+            glob_map: if resolver.make_glob_map { Some(resolver.glob_map) } else { None },
+        },
+        resolutions: Resolutions {
+            def_map: resolver.def_map,
+            freevars: resolver.freevars,
+            trait_map: resolver.trait_map,
+            maybe_unused_trait_imports: resolver.maybe_unused_trait_imports,
+        },
+        hir_forest: hir_forest
+    })
 }
 
 pub fn assign_node_ids(sess: &Session, krate: ast::Crate) -> ast::Crate {
+    use syntax::ptr::P;
+    use syntax::util::move_map::MoveMap;
+
     struct NodeIdAssigner<'a> {
         sess: &'a Session,
     }
@@ -787,6 +796,27 @@ pub fn assign_node_ids(sess: &Session, krate: ast::Crate) -> ast::Crate {
             assert_eq!(old_id, ast::DUMMY_NODE_ID);
             self.sess.next_node_id()
         }
+
+        fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
+            block.map(|mut block| {
+                block.id = self.new_id(block.id);
+
+                let stmt = block.stmts.pop();
+                block.stmts = block.stmts.move_flat_map(|s| self.fold_stmt(s).into_iter());
+                if let Some(ast::Stmt { node: ast::StmtKind::Expr(expr), span, .. }) = stmt {
+                    let expr = self.fold_expr(expr);
+                    block.stmts.push(ast::Stmt {
+                        id: expr.id,
+                        node: ast::StmtKind::Expr(expr),
+                        span: span,
+                    });
+                } else if let Some(stmt) = stmt {
+                    block.stmts.extend(self.fold_stmt(stmt));
+                }
+
+                block
+            })
+        }
     }
 
     let krate = time(sess.time_passes(),
@@ -800,38 +830,6 @@ pub fn assign_node_ids(sess: &Session, krate: ast::Crate) -> ast::Crate {
     krate
 }
 
-pub fn lower_and_resolve<'a>(sess: &Session,
-                             id: &'a str,
-                             defs: &mut hir_map::Definitions,
-                             krate: &ast::Crate,
-                             dep_graph: &DepGraph,
-                             make_glob_map: resolve::MakeGlobMap)
-                             -> (ty::CrateAnalysis<'a>, Resolutions, hir_map::Forest) {
-    resolve::with_resolver(sess, defs, make_glob_map, |mut resolver| {
-        time(sess.time_passes(), "name resolution", || {
-            resolve::resolve_crate(&mut resolver, krate);
-        });
-
-        // Lower ast -> hir.
-        let hir_forest = time(sess.time_passes(), "lowering ast -> hir", || {
-            hir_map::Forest::new(lower_crate(sess, krate, sess, &mut resolver), dep_graph)
-        });
-
-        (ty::CrateAnalysis {
-            export_map: resolver.export_map,
-            access_levels: AccessLevels::default(),
-            reachable: NodeSet(),
-            name: &id,
-            glob_map: if resolver.make_glob_map { Some(resolver.glob_map) } else { None },
-        }, Resolutions {
-            def_map: RefCell::new(resolver.def_map),
-            freevars: resolver.freevars,
-            trait_map: resolver.trait_map,
-            maybe_unused_trait_imports: resolver.maybe_unused_trait_imports,
-        }, hir_forest)
-    })
-}
-
 /// Run the resolution, typechecking, region checking and other
 /// miscellaneous analysis passes on the crate. Return various
 /// structures carrying the results of the analysis.
@@ -872,7 +870,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
                                 "lifetime resolution",
                                 || middle::resolve_lifetime::krate(sess,
                                                                    &hir_map,
-                                                                   &resolutions.def_map.borrow()))?;
+                                                                   &resolutions.def_map))?;
 
     time(time_passes,
          "looking for entry point",
@@ -892,7 +890,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 
     time(time_passes,
               "static item recursion checking",
-              || static_recursion::check_crate(sess, &resolutions.def_map.borrow(), &hir_map))?;
+              || static_recursion::check_crate(sess, &resolutions.def_map, &hir_map))?;
 
     let index = stability::Index::new(&hir_map);
 
@@ -962,11 +960,13 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
         time(time_passes, "MIR passes", || {
             let mut passes = sess.mir_passes.borrow_mut();
             // Push all the built-in passes.
-            passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
+            passes.push_hook(box mir::transform::dump_mir::DumpMir);
+            passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("initial"));
             passes.push_pass(box mir::transform::qualify_consts::QualifyAndPromoteConstants);
             passes.push_pass(box mir::transform::type_check::TypeckMir);
-            passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg);
-            passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
+            passes.push_pass(
+                box mir::transform::simplify_branches::SimplifyBranches::new("initial"));
+            passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("qualify-consts"));
             // And run everything.
             passes.run_passes(tcx, &mut mir_map);
         });
@@ -1032,10 +1032,20 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // to LLVM code.
     time(time_passes, "Prepare MIR codegen passes", || {
         let mut passes = ::rustc::mir::transform::Passes::new();
+        passes.push_hook(box mir::transform::dump_mir::DumpMir);
         passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
-        passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
+        passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("no-landing-pads"));
+
         passes.push_pass(box mir::transform::erase_regions::EraseRegions);
-        passes.push_pass(box mir::transform::break_cleanup_edges::BreakCleanupEdges);
+
+        passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
+        passes.push_pass(box borrowck::ElaborateDrops);
+        passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
+        passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops"));
+
+        passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
+        passes.push_pass(box mir::transform::dump_mir::Marker("PreTrans"));
+
         passes.run_passes(tcx, &mut mir_map);
     });
 
index 06133c508d9f027cfd97178c0c9b9de994031db0..c9569a63436f5f1b2e5a891d1260d1324d54704d 100644 (file)
@@ -42,6 +42,7 @@ extern crate rustc;
 extern crate rustc_back;
 extern crate rustc_borrowck;
 extern crate rustc_const_eval;
+extern crate rustc_errors as errors;
 extern crate rustc_passes;
 extern crate rustc_lint;
 extern crate rustc_plugin;
@@ -60,6 +61,7 @@ extern crate log;
 #[macro_use]
 extern crate syntax;
 extern crate syntax_ext;
+extern crate syntax_pos;
 
 use driver::CompileController;
 use pretty::{PpMode, UserIdentifiedItem};
@@ -92,11 +94,12 @@ use std::thread;
 
 use rustc::session::early_error;
 
-use syntax::{ast, errors, diagnostics};
-use syntax::codemap::{CodeMap, FileLoader, RealFileLoader, MultiSpan};
-use syntax::errors::emitter::Emitter;
+use syntax::{ast, json};
+use syntax::codemap::{CodeMap, FileLoader, RealFileLoader};
 use syntax::feature_gate::{GatedCfg, UnstableFeatures};
 use syntax::parse::{self, PResult, token};
+use syntax_pos::MultiSpan;
+use errors::emitter::Emitter;
 
 #[cfg(test)]
 pub mod test;
@@ -290,7 +293,7 @@ pub trait CompilerCalls<'a> {
     fn early_callback(&mut self,
                       _: &getopts::Matches,
                       _: &config::Options,
-                      _: &diagnostics::registry::Registry,
+                      _: &errors::registry::Registry,
                       _: ErrorOutputType)
                       -> Compilation {
         Compilation::Continue
@@ -329,7 +332,7 @@ pub trait CompilerCalls<'a> {
                 _: &config::Options,
                 _: &Option<PathBuf>,
                 _: &Option<PathBuf>,
-                _: &diagnostics::registry::Registry)
+                _: &errors::registry::Registry)
                 -> Option<(Input, Option<PathBuf>)> {
         None
     }
@@ -344,7 +347,7 @@ pub trait CompilerCalls<'a> {
 pub struct RustcDefaultCalls;
 
 fn handle_explain(code: &str,
-                  descriptions: &diagnostics::registry::Registry,
+                  descriptions: &errors::registry::Registry,
                   output: ErrorOutputType) {
     let normalised = if code.starts_with("E") {
         code.to_string()
@@ -374,7 +377,7 @@ fn check_cfg(sopts: &config::Options,
         config::ErrorOutputType::HumanReadable(color_config) => {
             Box::new(errors::emitter::BasicEmitter::stderr(color_config))
         }
-        config::ErrorOutputType::Json => Box::new(errors::json::JsonEmitter::basic()),
+        config::ErrorOutputType::Json => Box::new(json::JsonEmitter::basic()),
     };
 
     let mut saw_invalid_predicate = false;
@@ -401,7 +404,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
     fn early_callback(&mut self,
                       matches: &getopts::Matches,
                       sopts: &config::Options,
-                      descriptions: &diagnostics::registry::Registry,
+                      descriptions: &errors::registry::Registry,
                       output: ErrorOutputType)
                       -> Compilation {
         if let Some(ref code) = matches.opt_str("explain") {
@@ -418,7 +421,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
                 sopts: &config::Options,
                 odir: &Option<PathBuf>,
                 ofile: &Option<PathBuf>,
-                descriptions: &diagnostics::registry::Registry)
+                descriptions: &errors::registry::Registry)
                 -> Option<(Input, Option<PathBuf>)> {
         match matches.free.len() {
             0 => {
@@ -508,11 +511,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
         }
 
         if sess.opts.no_analysis || sess.opts.debugging_opts.ast_json {
-            control.after_write_deps.stop = Compilation::Stop;
-        }
-
-        if sess.opts.no_trans {
-            control.after_analysis.stop = Compilation::Stop;
+            control.after_hir_lowering.stop = Compilation::Stop;
         }
 
         if !sess.opts.output_types.keys().any(|&i| i == OutputType::Exe) {
@@ -1085,8 +1084,8 @@ fn exit_on_err() -> ! {
     panic!();
 }
 
-pub fn diagnostics_registry() -> diagnostics::registry::Registry {
-    use syntax::diagnostics::registry::Registry;
+pub fn diagnostics_registry() -> errors::registry::Registry {
+    use errors::registry::Registry;
 
     let mut all_errors = Vec::new();
     all_errors.extend_from_slice(&rustc::DIAGNOSTICS);
index 8c84e561e317a033704f4e7e40ce9d41ff69b0ba..baac455a25f334c7c6c6a3194f0ed6c00f44da56 100644 (file)
@@ -31,12 +31,12 @@ use rustc_mir::pretty::write_mir_pretty;
 use rustc_mir::graphviz::write_mir_graphviz;
 
 use syntax::ast::{self, BlockCheckMode};
-use syntax::codemap;
 use syntax::fold::{self, Folder};
 use syntax::print::{pp, pprust};
 use syntax::print::pprust::PrintState;
 use syntax::ptr::P;
 use syntax::util::small_vector::SmallVector;
+use syntax_pos;
 
 use graphviz as dot;
 
@@ -657,11 +657,14 @@ impl fold::Folder for ReplaceBodyWithLoop {
     fn fold_block(&mut self, b: P<ast::Block>) -> P<ast::Block> {
         fn expr_to_block(rules: ast::BlockCheckMode, e: Option<P<ast::Expr>>) -> P<ast::Block> {
             P(ast::Block {
-                expr: e,
-                stmts: vec![],
+                stmts: e.map(|e| ast::Stmt {
+                    id: ast::DUMMY_NODE_ID,
+                    span: e.span,
+                    node: ast::StmtKind::Expr(e),
+                }).into_iter().collect(),
                 rules: rules,
                 id: ast::DUMMY_NODE_ID,
-                span: codemap::DUMMY_SP,
+                span: syntax_pos::DUMMY_SP,
             })
         }
 
@@ -671,8 +674,8 @@ impl fold::Folder for ReplaceBodyWithLoop {
             let loop_expr = P(ast::Expr {
                 node: ast::ExprKind::Loop(empty_block, None),
                 id: ast::DUMMY_NODE_ID,
-                span: codemap::DUMMY_SP,
-                attrs: None,
+                span: syntax_pos::DUMMY_SP,
+                attrs: ast::ThinVec::new(),
             });
 
             expr_to_block(b.rules, Some(loop_expr))
@@ -757,6 +760,7 @@ fn get_source(input: &Input, sess: &Session) -> (Vec<u8>, String) {
     let src_name = driver::source_name(input);
     let src = sess.codemap()
                   .get_filemap(&src_name)
+                  .unwrap()
                   .src
                   .as_ref()
                   .unwrap()
index e0d693c423096db41c145ab159494e080fdf760e..15a0ab0f284b3efdde3be345078029b4be4446af 100644 (file)
@@ -26,18 +26,18 @@ use rustc::traits::ProjectionMode;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc::infer::{self, InferOk, InferResult, TypeOrigin};
 use rustc_metadata::cstore::CStore;
-use rustc_metadata::creader::read_local_crates;
 use rustc::hir::map as hir_map;
 use rustc::session::{self, config};
 use std::rc::Rc;
 use syntax::ast;
 use syntax::abi::Abi;
-use syntax::codemap::{CodeMap, DUMMY_SP};
-use syntax::errors;
-use syntax::errors::emitter::{CoreEmitter, Emitter};
-use syntax::errors::{Level, RenderSpan};
+use syntax::codemap::CodeMap;
+use errors;
+use errors::emitter::{CoreEmitter, Emitter};
+use errors::{Level, RenderSpan};
 use syntax::parse::token;
 use syntax::feature_gate::UnstableFeatures;
+use syntax_pos::DUMMY_SP;
 
 use rustc::hir;
 
@@ -116,25 +116,19 @@ fn test_env<F>(source_string: &str,
         input: source_string.to_string(),
     };
     let krate = driver::phase_1_parse_input(&sess, krate_config, &input).unwrap();
-    let krate = driver::phase_2_configure_and_expand(&sess, &cstore, krate, "test", None)
-                    .expect("phase 2 aborted");
-
-    let krate = driver::assign_node_ids(&sess, krate);
-    let mut defs = hir_map::collect_definitions(&krate);
-    read_local_crates(&sess, &cstore, &defs, &krate, "test_crate", &dep_graph);
-    let _ignore = dep_graph.in_ignore();
-
-    let (_, resolutions, mut hir_forest) = {
-        driver::lower_and_resolve(&sess, "test-crate", &mut defs, &krate,
-                                  &sess.dep_graph, MakeGlobMap::No)
+    let driver::ExpansionResult { defs, resolutions, mut hir_forest, .. } = {
+        driver::phase_2_configure_and_expand(
+            &sess, &cstore, krate, "test", None, MakeGlobMap::No, |_| Ok(()),
+        ).expect("phase 2 aborted")
     };
+    let _ignore = dep_graph.in_ignore();
 
     let arenas = ty::CtxtArenas::new();
     let ast_map = hir_map::map_crate(&mut hir_forest, defs);
 
     // run just enough stuff to build a tcx:
     let lang_items = lang_items::collect_language_items(&sess, &ast_map);
-    let named_region_map = resolve_lifetime::krate(&sess, &ast_map, &resolutions.def_map.borrow());
+    let named_region_map = resolve_lifetime::krate(&sess, &ast_map, &resolutions.def_map);
     let region_map = region::resolve_crate(&sess, &ast_map);
     let index = stability::Index::new(&ast_map);
     TyCtxt::create_and_enter(&sess,
diff --git a/src/librustc_errors/Cargo.toml b/src/librustc_errors/Cargo.toml
new file mode 100644 (file)
index 0000000..128c270
--- /dev/null
@@ -0,0 +1,14 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_errors"
+version = "0.0.0"
+
+[lib]
+name = "rustc_errors"
+path = "lib.rs"
+crate-type = ["dylib"]
+
+[dependencies]
+log = { path = "../liblog" }
+serialize = { path = "../libserialize" }
+syntax_pos = { path = "../libsyntax_pos" }
\ No newline at end of file
diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs
new file mode 100644 (file)
index 0000000..a7c68e3
--- /dev/null
@@ -0,0 +1,635 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use self::Destination::*;
+
+use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, Span, MultiSpan, LineInfo};
+use registry;
+
+use check_old_skool;
+use {Level, RenderSpan, CodeSuggestion, DiagnosticBuilder, CodeMapper};
+use RenderSpan::*;
+use Level::*;
+use snippet::{RenderedLineKind, SnippetData, Style, FormatMode};
+
+use std::{cmp, fmt};
+use std::io::prelude::*;
+use std::io;
+use std::rc::Rc;
+use term;
+
+/// Emitter trait for emitting errors. Do not implement this directly:
+/// implement `CoreEmitter` instead.
+pub trait Emitter {
+    /// Emit a standalone diagnostic message.
+    fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level);
+
+    /// Emit a structured diagnostic.
+    fn emit_struct(&mut self, db: &DiagnosticBuilder);
+}
+
+pub trait CoreEmitter {
+    fn emit_message(&mut self,
+                    rsp: &RenderSpan,
+                    msg: &str,
+                    code: Option<&str>,
+                    lvl: Level,
+                    is_header: bool,
+                    show_snippet: bool);
+}
+
+impl<T: CoreEmitter> Emitter for T {
+    fn emit(&mut self,
+            msp: &MultiSpan,
+            msg: &str,
+            code: Option<&str>,
+            lvl: Level) {
+        self.emit_message(&FullSpan(msp.clone()),
+                          msg,
+                          code,
+                          lvl,
+                          true,
+                          true);
+    }
+
+    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
+        let old_school = check_old_skool();
+        let db_span = FullSpan(db.span.clone());
+        self.emit_message(&FullSpan(db.span.clone()),
+                          &db.message,
+                          db.code.as_ref().map(|s| &**s),
+                          db.level,
+                          true,
+                          true);
+        for child in &db.children {
+            let render_span = child.render_span
+                                   .clone()
+                                   .unwrap_or_else(
+                                       || FullSpan(child.span.clone()));
+
+            if !old_school {
+                self.emit_message(&render_span,
+                                    &child.message,
+                                    None,
+                                    child.level,
+                                    false,
+                                    true);
+            } else {
+                let (render_span, show_snippet) = match render_span.span().primary_span() {
+                    None => (db_span.clone(), false),
+                    _ => (render_span, true)
+                };
+                self.emit_message(&render_span,
+                                    &child.message,
+                                    None,
+                                    child.level,
+                                    false,
+                                    show_snippet);
+            }
+        }
+    }
+}
+
+/// maximum number of lines we will print for each error; arbitrary.
+pub const MAX_HIGHLIGHT_LINES: usize = 6;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ColorConfig {
+    Auto,
+    Always,
+    Never,
+}
+
+impl ColorConfig {
+    fn use_color(&self) -> bool {
+        match *self {
+            ColorConfig::Always => true,
+            ColorConfig::Never  => false,
+            ColorConfig::Auto   => stderr_isatty(),
+        }
+    }
+}
+
+/// A basic emitter for when we don't have access to a codemap or registry. Used
+/// for reporting very early errors, etc.
+pub struct BasicEmitter {
+    dst: Destination,
+}
+
+impl CoreEmitter for BasicEmitter {
+    fn emit_message(&mut self,
+                    _rsp: &RenderSpan,
+                    msg: &str,
+                    code: Option<&str>,
+                    lvl: Level,
+                    _is_header: bool,
+                    _show_snippet: bool) {
+        // we ignore the span as we have no access to a codemap at this point
+        if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
+    }
+}
+
+impl BasicEmitter {
+    pub fn stderr(color_config: ColorConfig) -> BasicEmitter {
+        if color_config.use_color() {
+            let dst = Destination::from_stderr();
+            BasicEmitter { dst: dst }
+        } else {
+            BasicEmitter { dst: Raw(Box::new(io::stderr())) }
+        }
+    }
+}
+
+pub struct EmitterWriter {
+    dst: Destination,
+    registry: Option<registry::Registry>,
+    cm: Rc<CodeMapper>,
+
+    /// Is this the first error emitted thus far? If not, we emit a
+    /// `\n` before the top-level errors.
+    first: bool,
+
+    // For now, allow an old-school mode while we transition
+    format_mode: FormatMode
+}
+
+impl CoreEmitter for EmitterWriter {
+    fn emit_message(&mut self,
+                    rsp: &RenderSpan,
+                    msg: &str,
+                    code: Option<&str>,
+                    lvl: Level,
+                    is_header: bool,
+                    show_snippet: bool) {
+        match self.emit_message_(rsp, msg, code, lvl, is_header, show_snippet) {
+            Ok(()) => { }
+            Err(e) => panic!("failed to emit error: {}", e)
+        }
+    }
+}
+
+/// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See
+/// `EmitterWriter::print_maybe_styled` for details.
+macro_rules! print_maybe_styled {
+    ($dst: expr, $style: expr, $($arg: tt)*) => {
+        $dst.print_maybe_styled(format_args!($($arg)*), $style, false)
+    }
+}
+
+macro_rules! println_maybe_styled {
+    ($dst: expr, $style: expr, $($arg: tt)*) => {
+        $dst.print_maybe_styled(format_args!($($arg)*), $style, true)
+    }
+}
+
+impl EmitterWriter {
+    pub fn stderr(color_config: ColorConfig,
+                  registry: Option<registry::Registry>,
+                  code_map: Rc<CodeMapper>,
+                  format_mode: FormatMode)
+                  -> EmitterWriter {
+        if color_config.use_color() {
+            let dst = Destination::from_stderr();
+            EmitterWriter { dst: dst,
+                            registry: registry,
+                            cm: code_map,
+                            first: true,
+                            format_mode: format_mode.clone() }
+        } else {
+            EmitterWriter { dst: Raw(Box::new(io::stderr())),
+                            registry: registry,
+                            cm: code_map,
+                            first: true,
+                            format_mode: format_mode.clone() }
+        }
+    }
+
+    pub fn new(dst: Box<Write + Send>,
+               registry: Option<registry::Registry>,
+               code_map: Rc<CodeMapper>,
+               format_mode: FormatMode)
+               -> EmitterWriter {
+        EmitterWriter { dst: Raw(dst),
+                        registry: registry,
+                        cm: code_map,
+                        first: true,
+                        format_mode: format_mode.clone() }
+    }
+
+    fn emit_message_(&mut self,
+                     rsp: &RenderSpan,
+                     msg: &str,
+                     code: Option<&str>,
+                     lvl: Level,
+                     is_header: bool,
+                     show_snippet: bool)
+                     -> io::Result<()> {
+        let old_school = match self.format_mode {
+            FormatMode::NewErrorFormat => false,
+            FormatMode::OriginalErrorFormat => true,
+            FormatMode::EnvironmentSelected => check_old_skool()
+        };
+
+        if is_header {
+            if self.first {
+                self.first = false;
+            } else {
+                if !old_school {
+                    write!(self.dst, "\n")?;
+                }
+            }
+        }
+
+        match code {
+            Some(code) if self.registry.as_ref()
+                                       .and_then(|registry| registry.find_description(code))
+                                       .is_some() => {
+                let code_with_explain = String::from("--explain ") + code;
+                if old_school {
+                    let loc = match rsp.span().primary_span() {
+                        Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(),
+                        Some(ps) => self.cm.span_to_string(ps),
+                        None => "".to_string()
+                    };
+                    print_diagnostic(&mut self.dst, &loc, lvl, msg, Some(code))?
+                }
+                else {
+                    print_diagnostic(&mut self.dst, "", lvl, msg, Some(&code_with_explain))?
+                }
+            }
+            _ => {
+                if old_school {
+                    let loc = match rsp.span().primary_span() {
+                        Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(),
+                        Some(ps) => self.cm.span_to_string(ps),
+                        None => "".to_string()
+                    };
+                    print_diagnostic(&mut self.dst, &loc, lvl, msg, code)?
+                }
+                else {
+                    print_diagnostic(&mut self.dst, "", lvl, msg, code)?
+                }
+            }
+        }
+
+        if !show_snippet {
+            return Ok(());
+        }
+
+        // Watch out for various nasty special spans; don't try to
+        // print any filename or anything for those.
+        match rsp.span().primary_span() {
+            Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => {
+                return Ok(());
+            }
+            _ => { }
+        }
+
+        // Otherwise, print out the snippet etc as needed.
+        match *rsp {
+            FullSpan(ref msp) => {
+                self.highlight_lines(msp, lvl)?;
+                if let Some(primary_span) = msp.primary_span() {
+                    self.print_macro_backtrace(primary_span)?;
+                }
+            }
+            Suggestion(ref suggestion) => {
+                self.highlight_suggestion(suggestion)?;
+                if let Some(primary_span) = rsp.span().primary_span() {
+                    self.print_macro_backtrace(primary_span)?;
+                }
+            }
+        }
+        if old_school {
+            match code {
+                Some(code) if self.registry.as_ref()
+                                        .and_then(|registry| registry.find_description(code))
+                                        .is_some() => {
+                    let loc = match rsp.span().primary_span() {
+                        Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(),
+                        Some(ps) => self.cm.span_to_string(ps),
+                        None => "".to_string()
+                    };
+                    let msg = "run `rustc --explain ".to_string() + &code.to_string() +
+                        "` to see a detailed explanation";
+                    print_diagnostic(&mut self.dst, &loc, Level::Help, &msg,
+                        None)?
+                }
+                _ => ()
+            }
+        }
+        Ok(())
+    }
+
+    fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()>
+    {
+        use std::borrow::Borrow;
+
+        let primary_span = suggestion.msp.primary_span().unwrap();
+        let lines = self.cm.span_to_lines(primary_span).unwrap();
+        assert!(!lines.lines.is_empty());
+
+        let complete = suggestion.splice_lines(self.cm.borrow());
+        let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES);
+        let display_lines = &lines.lines[..line_count];
+
+        let fm = &*lines.file;
+        // Calculate the widest number to format evenly
+        let max_digits = line_num_max_digits(display_lines.last().unwrap());
+
+        // print the suggestion without any line numbers, but leave
+        // space for them. This helps with lining up with previous
+        // snippets from the actual error being reported.
+        let mut lines = complete.lines();
+        for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
+            write!(&mut self.dst, "{0}:{1:2$} {3}\n",
+                   fm.name, "", max_digits, line)?;
+        }
+
+        // if we elided some lines, add an ellipsis
+        if let Some(_) = lines.next() {
+            write!(&mut self.dst, "{0:1$} {0:2$} ...\n",
+                   "", fm.name.len(), max_digits)?;
+        }
+
+        Ok(())
+    }
+
+    pub fn highlight_lines(&mut self,
+                       msp: &MultiSpan,
+                       lvl: Level)
+                       -> io::Result<()>
+    {
+        let old_school = match self.format_mode {
+            FormatMode::NewErrorFormat => false,
+            FormatMode::OriginalErrorFormat => true,
+            FormatMode::EnvironmentSelected => check_old_skool()
+        };
+
+        let mut snippet_data = SnippetData::new(self.cm.clone(),
+                                                msp.primary_span(),
+                                                self.format_mode.clone());
+        if old_school {
+            let mut output_vec = vec![];
+
+            for span_label in msp.span_labels() {
+                let mut snippet_data = SnippetData::new(self.cm.clone(),
+                                                        Some(span_label.span),
+                                                        self.format_mode.clone());
+
+                snippet_data.push(span_label.span,
+                                  span_label.is_primary,
+                                  span_label.label);
+                if span_label.is_primary {
+                    output_vec.insert(0, snippet_data);
+                }
+                else {
+                    output_vec.push(snippet_data);
+                }
+            }
+
+            for snippet_data in output_vec.iter() {
+                let rendered_lines = snippet_data.render_lines();
+                for rendered_line in &rendered_lines {
+                    for styled_string in &rendered_line.text {
+                        self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?;
+                        write!(&mut self.dst, "{}", styled_string.text)?;
+                        self.dst.reset_attrs()?;
+                    }
+                    write!(&mut self.dst, "\n")?;
+                }
+            }
+        }
+        else {
+            for span_label in msp.span_labels() {
+                snippet_data.push(span_label.span,
+                                  span_label.is_primary,
+                                  span_label.label);
+            }
+            let rendered_lines = snippet_data.render_lines();
+            for rendered_line in &rendered_lines {
+                for styled_string in &rendered_line.text {
+                    self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?;
+                    write!(&mut self.dst, "{}", styled_string.text)?;
+                    self.dst.reset_attrs()?;
+                }
+                write!(&mut self.dst, "\n")?;
+            }
+        }
+        Ok(())
+    }
+
+    fn print_macro_backtrace(&mut self,
+                             sp: Span)
+                             -> io::Result<()> {
+        for trace in self.cm.macro_backtrace(sp) {
+            let mut diag_string =
+                format!("in this expansion of {}", trace.macro_decl_name);
+            if let Some(def_site_span) = trace.def_site_span {
+                diag_string.push_str(
+                    &format!(" (defined in {})",
+                        self.cm.span_to_filename(def_site_span)));
+            }
+            let snippet = self.cm.span_to_string(trace.call_site);
+            print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?;
+        }
+        Ok(())
+    }
+}
+
+fn line_num_max_digits(line: &LineInfo) -> usize {
+    let mut max_line_num = line.line_index + 1;
+    let mut digits = 0;
+    while max_line_num > 0 {
+        max_line_num /= 10;
+        digits += 1;
+    }
+    digits
+}
+
+fn print_diagnostic(dst: &mut Destination,
+                    topic: &str,
+                    lvl: Level,
+                    msg: &str,
+                    code: Option<&str>)
+                    -> io::Result<()> {
+    if !topic.is_empty() {
+        let old_school = check_old_skool();
+        if !old_school {
+            write!(dst, "{}: ", topic)?;
+        }
+        else {
+            write!(dst, "{} ", topic)?;
+        }
+        dst.reset_attrs()?;
+    }
+    dst.start_attr(term::Attr::Bold)?;
+    dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
+    write!(dst, "{}", lvl.to_string())?;
+    dst.reset_attrs()?;
+    write!(dst, ": ")?;
+    dst.start_attr(term::Attr::Bold)?;
+    write!(dst, "{}", msg)?;
+
+    if let Some(code) = code {
+        let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
+        print_maybe_styled!(dst, style, " [{}]", code.clone())?;
+    }
+
+    dst.reset_attrs()?;
+    write!(dst, "\n")?;
+    Ok(())
+}
+
+#[cfg(unix)]
+fn stderr_isatty() -> bool {
+    use libc;
+    unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
+}
+#[cfg(windows)]
+fn stderr_isatty() -> bool {
+    type DWORD = u32;
+    type BOOL = i32;
+    type HANDLE = *mut u8;
+    const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
+    extern "system" {
+        fn GetStdHandle(which: DWORD) -> HANDLE;
+        fn GetConsoleMode(hConsoleHandle: HANDLE,
+                          lpMode: *mut DWORD) -> BOOL;
+    }
+    unsafe {
+        let handle = GetStdHandle(STD_ERROR_HANDLE);
+        let mut out = 0;
+        GetConsoleMode(handle, &mut out) != 0
+    }
+}
+
+enum Destination {
+    Terminal(Box<term::StderrTerminal>),
+    Raw(Box<Write + Send>),
+}
+
+impl Destination {
+    fn from_stderr() -> Destination {
+        match term::stderr() {
+            Some(t) => Terminal(t),
+            None    => Raw(Box::new(io::stderr())),
+        }
+    }
+
+    fn apply_style(&mut self,
+                   lvl: Level,
+                   _kind: &RenderedLineKind,
+                   style: Style)
+                   -> io::Result<()> {
+        match style {
+            Style::FileNameStyle |
+            Style::LineAndColumn => {
+            }
+            Style::LineNumber => {
+                self.start_attr(term::Attr::Bold)?;
+                self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
+            }
+            Style::Quotation => {
+            }
+            Style::OldSkoolNote => {
+                self.start_attr(term::Attr::Bold)?;
+                self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN))?;
+            }
+            Style::OldSkoolNoteText => {
+                self.start_attr(term::Attr::Bold)?;
+            }
+            Style::UnderlinePrimary | Style::LabelPrimary => {
+                self.start_attr(term::Attr::Bold)?;
+                self.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
+            }
+            Style::UnderlineSecondary | Style::LabelSecondary => {
+                self.start_attr(term::Attr::Bold)?;
+                self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
+            }
+            Style::NoStyle => {
+            }
+        }
+        Ok(())
+    }
+
+    fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> {
+        match *self {
+            Terminal(ref mut t) => { t.attr(attr)?; }
+            Raw(_) => { }
+        }
+        Ok(())
+    }
+
+    fn reset_attrs(&mut self) -> io::Result<()> {
+        match *self {
+            Terminal(ref mut t) => { t.reset()?; }
+            Raw(_) => { }
+        }
+        Ok(())
+    }
+
+    fn print_maybe_styled(&mut self,
+                          args: fmt::Arguments,
+                          color: term::Attr,
+                          print_newline_at_end: bool)
+                          -> io::Result<()> {
+        match *self {
+            Terminal(ref mut t) => {
+                t.attr(color)?;
+                // If `msg` ends in a newline, we need to reset the color before
+                // the newline. We're making the assumption that we end up writing
+                // to a `LineBufferedWriter`, which means that emitting the reset
+                // after the newline ends up buffering the reset until we print
+                // another line or exit. Buffering the reset is a problem if we're
+                // sharing the terminal with any other programs (e.g. other rustc
+                // instances via `make -jN`).
+                //
+                // Note that if `msg` contains any internal newlines, this will
+                // result in the `LineBufferedWriter` flushing twice instead of
+                // once, which still leaves the opportunity for interleaved output
+                // to be miscolored. We assume this is rare enough that we don't
+                // have to worry about it.
+                t.write_fmt(args)?;
+                t.reset()?;
+                if print_newline_at_end {
+                    t.write_all(b"\n")
+                } else {
+                    Ok(())
+                }
+            }
+            Raw(ref mut w) => {
+                w.write_fmt(args)?;
+                if print_newline_at_end {
+                    w.write_all(b"\n")
+                } else {
+                    Ok(())
+                }
+            }
+        }
+    }
+}
+
+impl Write for Destination {
+    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
+        match *self {
+            Terminal(ref mut t) => t.write(bytes),
+            Raw(ref mut w) => w.write(bytes),
+        }
+    }
+    fn flush(&mut self) -> io::Result<()> {
+        match *self {
+            Terminal(ref mut t) => t.flush(),
+            Raw(ref mut w) => w.flush(),
+        }
+    }
+}
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs
new file mode 100644 (file)
index 0000000..18fc826
--- /dev/null
@@ -0,0 +1,746 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name = "rustc_errors"]
+#![unstable(feature = "rustc_private", issue = "27812")]
+#![crate_type = "dylib"]
+#![crate_type = "rlib"]
+#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
+      html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![cfg_attr(not(stage0), deny(warnings))]
+
+#![feature(custom_attribute)]
+#![allow(unused_attributes)]
+#![feature(rustc_private)]
+#![feature(staged_api)]
+#![feature(question_mark)]
+#![feature(range_contains)]
+#![feature(libc)]
+#![feature(unicode)]
+
+extern crate serialize;
+extern crate term;
+#[macro_use] extern crate log;
+#[macro_use] extern crate libc;
+extern crate rustc_unicode;
+extern crate serialize as rustc_serialize; // used by deriving
+extern crate syntax_pos;
+
+pub use emitter::ColorConfig;
+
+use self::Level::*;
+use self::RenderSpan::*;
+
+use emitter::{Emitter, EmitterWriter};
+
+use std::cell::{RefCell, Cell};
+use std::{error, fmt};
+use std::rc::Rc;
+use std::thread::panicking;
+
+pub mod emitter;
+pub mod snippet;
+pub mod registry;
+
+use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION };
+use syntax_pos::{MacroBacktrace};
+
+#[derive(Clone)]
+pub enum RenderSpan {
+    /// A FullSpan renders with both with an initial line for the
+    /// message, prefixed by file:linenum, followed by a summary of
+    /// the source code covered by the span.
+    FullSpan(MultiSpan),
+
+    /// A suggestion renders with both with an initial line for the
+    /// message, prefixed by file:linenum, followed by a summary
+    /// of hypothetical source code, where each `String` is spliced
+    /// into the lines in place of the code covered by each span.
+    Suggestion(CodeSuggestion),
+}
+
+#[derive(Clone)]
+pub struct CodeSuggestion {
+    pub msp: MultiSpan,
+    pub substitutes: Vec<String>,
+}
+
+pub trait CodeMapper {
+    fn lookup_char_pos(&self, pos: BytePos) -> Loc;
+    fn span_to_lines(&self, sp: Span) -> FileLinesResult;
+    fn span_to_string(&self, sp: Span) -> String;
+    fn span_to_filename(&self, sp: Span) -> FileName;
+    fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace>;
+}
+
+impl RenderSpan {
+    fn span(&self) -> &MultiSpan {
+        match *self {
+            FullSpan(ref msp) |
+            Suggestion(CodeSuggestion { ref msp, .. }) =>
+                msp
+        }
+    }
+}
+
+impl CodeSuggestion {
+    /// Returns the assembled code suggestion.
+    pub fn splice_lines(&self, cm: &CodeMapper) -> String {
+        use syntax_pos::{CharPos, Loc, Pos};
+
+        fn push_trailing(buf: &mut String, line_opt: Option<&str>,
+                         lo: &Loc, hi_opt: Option<&Loc>) {
+            let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi|hi.col.to_usize()));
+            if let Some(line) = line_opt {
+                if line.len() > lo {
+                    buf.push_str(match hi_opt {
+                        Some(hi) => &line[lo..hi],
+                        None => &line[lo..],
+                    });
+                }
+                if let None = hi_opt {
+                    buf.push('\n');
+                }
+            }
+        }
+
+        let mut primary_spans = self.msp.primary_spans().to_owned();
+
+        assert_eq!(primary_spans.len(), self.substitutes.len());
+        if primary_spans.is_empty() {
+            return format!("");
+        }
+
+        // Assumption: all spans are in the same file, and all spans
+        // are disjoint. Sort in ascending order.
+        primary_spans.sort_by_key(|sp| sp.lo);
+
+        // Find the bounding span.
+        let lo = primary_spans.iter().map(|sp| sp.lo).min().unwrap();
+        let hi = primary_spans.iter().map(|sp| sp.hi).min().unwrap();
+        let bounding_span = Span { lo: lo, hi: hi, expn_id: NO_EXPANSION };
+        let lines = cm.span_to_lines(bounding_span).unwrap();
+        assert!(!lines.lines.is_empty());
+
+        // To build up the result, we do this for each span:
+        // - push the line segment trailing the previous span
+        //   (at the beginning a "phantom" span pointing at the start of the line)
+        // - push lines between the previous and current span (if any)
+        // - if the previous and current span are not on the same line
+        //   push the line segment leading up to the current span
+        // - splice in the span substitution
+        //
+        // Finally push the trailing line segment of the last span
+        let fm = &lines.file;
+        let mut prev_hi = cm.lookup_char_pos(bounding_span.lo);
+        prev_hi.col = CharPos::from_usize(0);
+
+        let mut prev_line = fm.get_line(lines.lines[0].line_index);
+        let mut buf = String::new();
+
+        for (sp, substitute) in primary_spans.iter().zip(self.substitutes.iter()) {
+            let cur_lo = cm.lookup_char_pos(sp.lo);
+            if prev_hi.line == cur_lo.line {
+                push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo));
+            } else {
+                push_trailing(&mut buf, prev_line, &prev_hi, None);
+                // push lines between the previous and current span (if any)
+                for idx in prev_hi.line..(cur_lo.line - 1) {
+                    if let Some(line) = fm.get_line(idx) {
+                        buf.push_str(line);
+                        buf.push('\n');
+                    }
+                }
+                if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
+                    buf.push_str(&cur_line[.. cur_lo.col.to_usize()]);
+                }
+            }
+            buf.push_str(substitute);
+            prev_hi = cm.lookup_char_pos(sp.hi);
+            prev_line = fm.get_line(prev_hi.line - 1);
+        }
+        push_trailing(&mut buf, prev_line, &prev_hi, None);
+        // remove trailing newline
+        buf.pop();
+        buf
+    }
+}
+
+/// Used as a return value to signify a fatal error occurred. (It is also
+/// used as the argument to panic at the moment, but that will eventually
+/// not be true.)
+#[derive(Copy, Clone, Debug)]
+#[must_use]
+pub struct FatalError;
+
+impl fmt::Display for FatalError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(f, "parser fatal error")
+    }
+}
+
+impl error::Error for FatalError {
+    fn description(&self) -> &str {
+        "The parser has encountered a fatal error"
+    }
+}
+
+/// Signifies that the compiler died with an explicit call to `.bug`
+/// or `.span_bug` rather than a failed assertion, etc.
+#[derive(Copy, Clone, Debug)]
+pub struct ExplicitBug;
+
+impl fmt::Display for ExplicitBug {
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(f, "parser internal bug")
+    }
+}
+
+impl error::Error for ExplicitBug {
+    fn description(&self) -> &str {
+        "The parser has encountered an internal bug"
+    }
+}
+
+/// Used for emitting structured error messages and other diagnostic information.
+#[must_use]
+#[derive(Clone)]
+pub struct DiagnosticBuilder<'a> {
+    handler: &'a Handler,
+    pub level: Level,
+    pub message: String,
+    pub code: Option<String>,
+    pub span: MultiSpan,
+    pub children: Vec<SubDiagnostic>,
+}
+
+/// For example a note attached to an error.
+#[derive(Clone)]
+pub struct SubDiagnostic {
+    pub level: Level,
+    pub message: String,
+    pub span: MultiSpan,
+    pub render_span: Option<RenderSpan>,
+}
+
+impl<'a> DiagnosticBuilder<'a> {
+    /// Emit the diagnostic.
+    pub fn emit(&mut self) {
+        if self.cancelled() {
+            return;
+        }
+
+        self.handler.emit.borrow_mut().emit_struct(&self);
+        self.cancel();
+        self.handler.panic_if_treat_err_as_bug();
+
+        // if self.is_fatal() {
+        //     panic!(FatalError);
+        // }
+    }
+
+    /// Cancel the diagnostic (a structured diagnostic must either be emitted or
+    /// cancelled or it will panic when dropped).
+    /// BEWARE: if this DiagnosticBuilder is an error, then creating it will
+    /// bump the error count on the Handler and cancelling it won't undo that.
+    /// If you want to decrement the error count you should use `Handler::cancel`.
+    pub fn cancel(&mut self) {
+        self.level = Level::Cancelled;
+    }
+
+    pub fn cancelled(&self) -> bool {
+        self.level == Level::Cancelled
+    }
+
+    pub fn is_fatal(&self) -> bool {
+        self.level == Level::Fatal
+    }
+
+    /// Add a span/label to be included in the resulting snippet.
+    /// This is pushed onto the `MultiSpan` that was created when the
+    /// diagnostic was first built. If you don't call this function at
+    /// all, and you just supplied a `Span` to create the diagnostic,
+    /// then the snippet will just include that `Span`, which is
+    /// called the primary span.
+    pub fn span_label(&mut self, span: Span, label: &fmt::Display)
+                      -> &mut DiagnosticBuilder<'a> {
+        self.span.push_span_label(span, format!("{}", label));
+        self
+    }
+
+    pub fn note_expected_found(&mut self,
+                               label: &fmt::Display,
+                               expected: &fmt::Display,
+                               found: &fmt::Display)
+                               -> &mut DiagnosticBuilder<'a>
+    {
+        // For now, just attach these as notes
+        self.note(&format!("expected {} `{}`", label, expected));
+        self.note(&format!("   found {} `{}`", label, found));
+        self
+    }
+
+    pub fn note(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Note, msg, MultiSpan::new(), None);
+        self
+    }
+    pub fn span_note<S: Into<MultiSpan>>(&mut self,
+                                         sp: S,
+                                         msg: &str)
+                                         -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Note, msg, sp.into(), None);
+        self
+    }
+    pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Warning, msg, MultiSpan::new(), None);
+        self
+    }
+    pub fn span_warn<S: Into<MultiSpan>>(&mut self,
+                                         sp: S,
+                                         msg: &str)
+                                         -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Warning, msg, sp.into(), None);
+        self
+    }
+    pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Help, msg, MultiSpan::new(), None);
+        self
+    }
+    pub fn span_help<S: Into<MultiSpan>>(&mut self,
+                                         sp: S,
+                                         msg: &str)
+                                         -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Help, msg, sp.into(), None);
+        self
+    }
+    /// Prints out a message with a suggested edit of the code.
+    ///
+    /// See `diagnostic::RenderSpan::Suggestion` for more information.
+    pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
+                                               sp: S,
+                                               msg: &str,
+                                               suggestion: String)
+                                               -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Help, msg, MultiSpan::new(), Some(Suggestion(CodeSuggestion {
+            msp: sp.into(),
+            substitutes: vec![suggestion],
+        })));
+        self
+    }
+
+    pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
+        self.span = sp.into();
+        self
+    }
+
+    pub fn code(&mut self, s: String) -> &mut Self {
+        self.code = Some(s);
+        self
+    }
+
+    pub fn message(&self) -> &str {
+        &self.message
+    }
+
+    pub fn level(&self) -> Level {
+        self.level
+    }
+
+    /// Convenience function for internal use, clients should use one of the
+    /// struct_* methods on Handler.
+    fn new(handler: &'a Handler,
+           level: Level,
+           message: &str) -> DiagnosticBuilder<'a> {
+        DiagnosticBuilder {
+            handler: handler,
+            level: level,
+            message: message.to_owned(),
+            code: None,
+            span: MultiSpan::new(),
+            children: vec![],
+        }
+    }
+
+    /// Convenience function for internal use, clients should use one of the
+    /// public methods above.
+    fn sub(&mut self,
+           level: Level,
+           message: &str,
+           span: MultiSpan,
+           render_span: Option<RenderSpan>) {
+        let sub = SubDiagnostic {
+            level: level,
+            message: message.to_owned(),
+            span: span,
+            render_span: render_span,
+        };
+        self.children.push(sub);
+    }
+}
+
+impl<'a> fmt::Debug for DiagnosticBuilder<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.message.fmt(f)
+    }
+}
+
+/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or
+/// we emit a bug.
+impl<'a> Drop for DiagnosticBuilder<'a> {
+    fn drop(&mut self) {
+        if !panicking() && !self.cancelled() {
+            self.handler.emit.borrow_mut().emit(&MultiSpan::new(),
+                                                "Error constructed but not emitted",
+                                                None,
+                                                Bug);
+            panic!();
+        }
+    }
+}
+
+/// A handler deals with errors; certain errors
+/// (fatal, bug, unimpl) may cause immediate exit,
+/// others log errors for later reporting.
+pub struct Handler {
+    err_count: Cell<usize>,
+    emit: RefCell<Box<Emitter>>,
+    pub can_emit_warnings: bool,
+    treat_err_as_bug: bool,
+    continue_after_error: Cell<bool>,
+    delayed_span_bug: RefCell<Option<(MultiSpan, String)>>,
+}
+
+impl Handler {
+    pub fn with_tty_emitter(color_config: ColorConfig,
+                            registry: Option<registry::Registry>,
+                            can_emit_warnings: bool,
+                            treat_err_as_bug: bool,
+                            cm: Rc<CodeMapper>)
+                            -> Handler {
+        let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm,
+                               snippet::FormatMode::EnvironmentSelected));
+        Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter)
+    }
+
+    pub fn with_emitter(can_emit_warnings: bool,
+                        treat_err_as_bug: bool,
+                        e: Box<Emitter>) -> Handler {
+        Handler {
+            err_count: Cell::new(0),
+            emit: RefCell::new(e),
+            can_emit_warnings: can_emit_warnings,
+            treat_err_as_bug: treat_err_as_bug,
+            continue_after_error: Cell::new(true),
+            delayed_span_bug: RefCell::new(None),
+        }
+    }
+
+    pub fn set_continue_after_error(&self, continue_after_error: bool) {
+        self.continue_after_error.set(continue_after_error);
+    }
+
+    pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> {
+        DiagnosticBuilder::new(self, Level::Cancelled, "")
+    }
+
+    pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self,
+                                                    sp: S,
+                                                    msg: &str)
+                                                    -> DiagnosticBuilder<'a> {
+        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
+        result.set_span(sp);
+        if !self.can_emit_warnings {
+            result.cancel();
+        }
+        result
+    }
+    pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(&'a self,
+                                                              sp: S,
+                                                              msg: &str,
+                                                              code: &str)
+                                                              -> DiagnosticBuilder<'a> {
+        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
+        result.set_span(sp);
+        result.code(code.to_owned());
+        if !self.can_emit_warnings {
+            result.cancel();
+        }
+        result
+    }
+    pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
+        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
+        if !self.can_emit_warnings {
+            result.cancel();
+        }
+        result
+    }
+    pub fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
+                                                   sp: S,
+                                                   msg: &str)
+                                                   -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
+        result.set_span(sp);
+        result
+    }
+    pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
+                                                             sp: S,
+                                                             msg: &str,
+                                                             code: &str)
+                                                             -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
+        result.set_span(sp);
+        result.code(code.to_owned());
+        result
+    }
+    pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        DiagnosticBuilder::new(self, Level::Error, msg)
+    }
+    pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self,
+                                                     sp: S,
+                                                     msg: &str)
+                                                     -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
+        result.set_span(sp);
+        result
+    }
+    pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self,
+                                                               sp: S,
+                                                               msg: &str,
+                                                               code: &str)
+                                                               -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
+        result.set_span(sp);
+        result.code(code.to_owned());
+        result
+    }
+    pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        DiagnosticBuilder::new(self, Level::Fatal, msg)
+    }
+
+    pub fn cancel(&mut self, err: &mut DiagnosticBuilder) {
+        if err.level == Level::Error || err.level == Level::Fatal {
+            assert!(self.has_errors());
+            self.err_count.set(self.err_count.get() + 1);
+        }
+        err.cancel();
+    }
+
+    fn panic_if_treat_err_as_bug(&self) {
+        if self.treat_err_as_bug {
+            panic!("encountered error with `-Z treat_err_as_bug");
+        }
+    }
+
+    pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str)
+                                          -> FatalError {
+        self.emit(&sp.into(), msg, Fatal);
+        self.bump_err_count();
+        self.panic_if_treat_err_as_bug();
+        return FatalError;
+    }
+    pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str)
+                                                    -> FatalError {
+        self.emit_with_code(&sp.into(), msg, code, Fatal);
+        self.bump_err_count();
+        self.panic_if_treat_err_as_bug();
+        return FatalError;
+    }
+    pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
+        self.emit(&sp.into(), msg, Error);
+        self.bump_err_count();
+        self.panic_if_treat_err_as_bug();
+    }
+    pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
+        self.emit_with_code(&sp.into(), msg, code, Error);
+        self.bump_err_count();
+        self.panic_if_treat_err_as_bug();
+    }
+    pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
+        self.emit(&sp.into(), msg, Warning);
+    }
+    pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
+        self.emit_with_code(&sp.into(), msg, code, Warning);
+    }
+    pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
+        self.emit(&sp.into(), msg, Bug);
+        panic!(ExplicitBug);
+    }
+    pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
+        let mut delayed = self.delayed_span_bug.borrow_mut();
+        *delayed = Some((sp.into(), msg.to_string()));
+    }
+    pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
+        self.emit(&sp.into(), msg, Bug);
+        self.bump_err_count();
+    }
+    pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
+        self.emit.borrow_mut().emit(&sp.into(), msg, None, Note);
+    }
+    pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
+        self.span_bug(sp, &format!("unimplemented {}", msg));
+    }
+    pub fn fatal(&self, msg: &str) -> FatalError {
+        if self.treat_err_as_bug {
+            self.bug(msg);
+        }
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Fatal);
+        self.bump_err_count();
+        FatalError
+    }
+    pub fn err(&self, msg: &str) {
+        if self.treat_err_as_bug {
+            self.bug(msg);
+        }
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Error);
+        self.bump_err_count();
+    }
+    pub fn warn(&self, msg: &str) {
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Warning);
+    }
+    pub fn note_without_error(&self, msg: &str) {
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Note);
+    }
+    pub fn bug(&self, msg: &str) -> ! {
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Bug);
+        panic!(ExplicitBug);
+    }
+    pub fn unimpl(&self, msg: &str) -> ! {
+        self.bug(&format!("unimplemented {}", msg));
+    }
+
+    pub fn bump_err_count(&self) {
+        self.err_count.set(self.err_count.get() + 1);
+    }
+
+    pub fn err_count(&self) -> usize {
+        self.err_count.get()
+    }
+
+    pub fn has_errors(&self) -> bool {
+        self.err_count.get() > 0
+    }
+    pub fn abort_if_errors(&self) {
+        let s;
+        match self.err_count.get() {
+            0 => {
+                let delayed_bug = self.delayed_span_bug.borrow();
+                match *delayed_bug {
+                    Some((ref span, ref errmsg)) => {
+                        self.span_bug(span.clone(), errmsg);
+                    },
+                    _ => {}
+                }
+
+                return;
+            }
+            1 => s = "aborting due to previous error".to_string(),
+            _  => {
+                s = format!("aborting due to {} previous errors",
+                            self.err_count.get());
+            }
+        }
+
+        panic!(self.fatal(&s));
+    }
+    pub fn emit(&self,
+                msp: &MultiSpan,
+                msg: &str,
+                lvl: Level) {
+        if lvl == Warning && !self.can_emit_warnings { return }
+        self.emit.borrow_mut().emit(&msp, msg, None, lvl);
+        if !self.continue_after_error.get() { self.abort_if_errors(); }
+    }
+    pub fn emit_with_code(&self,
+                          msp: &MultiSpan,
+                          msg: &str,
+                          code: &str,
+                          lvl: Level) {
+        if lvl == Warning && !self.can_emit_warnings { return }
+        self.emit.borrow_mut().emit(&msp, msg, Some(code), lvl);
+        if !self.continue_after_error.get() { self.abort_if_errors(); }
+    }
+}
+
+
+#[derive(Copy, PartialEq, Clone, Debug)]
+pub enum Level {
+    Bug,
+    Fatal,
+    // An error which while not immediately fatal, should stop the compiler
+    // progressing beyond the current phase.
+    PhaseFatal,
+    Error,
+    Warning,
+    Note,
+    Help,
+    Cancelled,
+}
+
+impl fmt::Display for Level {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.to_str().fmt(f)
+    }
+}
+
+impl Level {
+    pub fn color(self) -> term::color::Color {
+        match self {
+            Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED,
+            Warning => term::color::YELLOW,
+            Note => term::color::BRIGHT_GREEN,
+            Help => term::color::BRIGHT_CYAN,
+            Cancelled => unreachable!(),
+        }
+    }
+
+    pub fn to_str(self) -> &'static str {
+        match self {
+            Bug => "error: internal compiler error",
+            Fatal | PhaseFatal | Error => "error",
+            Warning => "warning",
+            Note => "note",
+            Help => "help",
+            Cancelled => panic!("Shouldn't call on cancelled error"),
+        }
+    }
+}
+
+pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where
+    M: FnOnce() -> String,
+{
+    match opt {
+        Some(t) => t,
+        None => diag.bug(&msg()),
+    }
+}
+
+/// True if we should use the old-skool error format style. This is
+/// the default setting until the new errors are deemed stable enough
+/// for general use.
+///
+/// FIXME(#33240)
+#[cfg(not(test))]
+pub fn check_old_skool() -> bool {
+    use std::env;
+    env::var("RUST_NEW_ERROR_FORMAT").is_err()
+}
+
+/// For unit tests, use the new format.
+#[cfg(test)]
+pub fn check_old_skool() -> bool {
+    false
+}
diff --git a/src/librustc_errors/registry.rs b/src/librustc_errors/registry.rs
new file mode 100644 (file)
index 0000000..a6cfd1a
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashMap;
+
+#[derive(Clone)]
+pub struct Registry {
+    descriptions: HashMap<&'static str, &'static str>
+}
+
+impl Registry {
+    pub fn new(descriptions: &[(&'static str, &'static str)]) -> Registry {
+        Registry { descriptions: descriptions.iter().cloned().collect() }
+    }
+
+    pub fn find_description(&self, code: &str) -> Option<&'static str> {
+        self.descriptions.get(code).cloned()
+    }
+}
diff --git a/src/librustc_errors/snippet.rs b/src/librustc_errors/snippet.rs
new file mode 100644 (file)
index 0000000..33f40ff
--- /dev/null
@@ -0,0 +1,916 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Code for annotating snippets.
+
+use syntax_pos::{Span, FileMap, CharPos, LineInfo};
+use check_old_skool;
+use CodeMapper;
+use std::cmp;
+use std::rc::Rc;
+use std::mem;
+
+#[derive(Clone)]
+pub enum FormatMode {
+    NewErrorFormat,
+    OriginalErrorFormat,
+    EnvironmentSelected
+}
+
+#[derive(Clone)]
+pub struct SnippetData {
+    codemap: Rc<CodeMapper>,
+    files: Vec<FileInfo>,
+    format_mode: FormatMode,
+}
+
+#[derive(Clone)]
+pub struct FileInfo {
+    file: Rc<FileMap>,
+
+    /// The "primary file", if any, gets a `-->` marker instead of
+    /// `>>>`, and has a line-number/column printed and not just a
+    /// filename.  It appears first in the listing. It is known to
+    /// contain at least one primary span, though primary spans (which
+    /// are designated with `^^^`) may also occur in other files.
+    primary_span: Option<Span>,
+
+    lines: Vec<Line>,
+
+    /// The type of error format to render.  We keep it here so that
+    /// it's easy to configure for both tests and regular usage
+    format_mode: FormatMode,
+}
+
+#[derive(Clone, Debug)]
+struct Line {
+    line_index: usize,
+    annotations: Vec<Annotation>,
+}
+
+#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
+struct Annotation {
+    /// Start column, 0-based indexing -- counting *characters*, not
+    /// utf-8 bytes. Note that it is important that this field goes
+    /// first, so that when we sort, we sort orderings by start
+    /// column.
+    start_col: usize,
+
+    /// End column within the line (exclusive)
+    end_col: usize,
+
+    /// Is this annotation derived from primary span
+    is_primary: bool,
+
+    /// Is this a large span minimized down to a smaller span
+    is_minimized: bool,
+
+    /// Optional label to display adjacent to the annotation.
+    label: Option<String>,
+}
+
+#[derive(Debug)]
+pub struct RenderedLine {
+    pub text: Vec<StyledString>,
+    pub kind: RenderedLineKind,
+}
+
+#[derive(Debug)]
+pub struct StyledString {
+    pub text: String,
+    pub style: Style,
+}
+
+#[derive(Debug)]
+pub struct StyledBuffer {
+    text: Vec<Vec<char>>,
+    styles: Vec<Vec<Style>>
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Style {
+    FileNameStyle,
+    LineAndColumn,
+    LineNumber,
+    Quotation,
+    UnderlinePrimary,
+    UnderlineSecondary,
+    LabelPrimary,
+    LabelSecondary,
+    OldSkoolNoteText,
+    OldSkoolNote,
+    NoStyle,
+}
+
+#[derive(Debug, Clone)]
+pub enum RenderedLineKind {
+    PrimaryFileName,
+    OtherFileName,
+    SourceText {
+        file: Rc<FileMap>,
+        line_index: usize,
+    },
+    Annotations,
+    Elision,
+}
+
+impl SnippetData {
+    pub fn new(codemap: Rc<CodeMapper>,
+               primary_span: Option<Span>,
+               format_mode: FormatMode) // (*)
+               -> Self {
+        // (*) The primary span indicates the file that must appear
+        // first, and which will have a line number etc in its
+        // name. Outside of tests, this is always `Some`, but for many
+        // tests it's not relevant to test this portion of the logic,
+        // and it's tedious to pick a primary span (read: tedious to
+        // port older tests that predate the existence of a primary
+        // span).
+
+        debug!("SnippetData::new(primary_span={:?})", primary_span);
+
+        let mut data = SnippetData {
+            codemap: codemap.clone(),
+            files: vec![],
+            format_mode: format_mode.clone()
+        };
+        if let Some(primary_span) = primary_span {
+            let lo = codemap.lookup_char_pos(primary_span.lo);
+            data.files.push(
+                FileInfo {
+                    file: lo.file,
+                    primary_span: Some(primary_span),
+                    lines: vec![],
+                    format_mode: format_mode.clone(),
+                });
+        }
+        data
+    }
+
+    pub fn push(&mut self, span: Span, is_primary: bool, label: Option<String>) {
+        debug!("SnippetData::push(span={:?}, is_primary={}, label={:?})",
+               span, is_primary, label);
+
+        let file_lines = match self.codemap.span_to_lines(span) {
+            Ok(file_lines) => file_lines,
+            Err(_) => {
+                // ignore unprintable spans completely.
+                return;
+            }
+        };
+
+        self.file(&file_lines.file)
+            .push_lines(&file_lines.lines, is_primary, label);
+    }
+
+    fn file(&mut self, file_map: &Rc<FileMap>) -> &mut FileInfo {
+        let index = self.files.iter().position(|f| f.file.name == file_map.name);
+        if let Some(index) = index {
+            return &mut self.files[index];
+        }
+
+        self.files.push(
+            FileInfo {
+                file: file_map.clone(),
+                lines: vec![],
+                primary_span: None,
+                format_mode: self.format_mode.clone()
+            });
+        self.files.last_mut().unwrap()
+    }
+
+    pub fn render_lines(&self) -> Vec<RenderedLine> {
+        debug!("SnippetData::render_lines()");
+
+        let mut rendered_lines: Vec<_> =
+            self.files.iter()
+                      .flat_map(|f| f.render_file_lines(&self.codemap))
+                      .collect();
+        prepend_prefixes(&mut rendered_lines, &self.format_mode);
+        trim_lines(&mut rendered_lines);
+        rendered_lines
+    }
+}
+
+pub trait StringSource {
+    fn make_string(self) -> String;
+}
+
+impl StringSource for String {
+    fn make_string(self) -> String {
+        self
+    }
+}
+
+impl StringSource for Vec<char> {
+    fn make_string(self) -> String {
+        self.into_iter().collect()
+    }
+}
+
+impl<S> From<(S, Style, RenderedLineKind)> for RenderedLine
+    where S: StringSource
+{
+    fn from((text, style, kind): (S, Style, RenderedLineKind)) -> Self {
+        RenderedLine {
+            text: vec![StyledString {
+                text: text.make_string(),
+                style: style,
+            }],
+            kind: kind,
+        }
+    }
+}
+
+impl<S1,S2> From<(S1, Style, S2, Style, RenderedLineKind)> for RenderedLine
+    where S1: StringSource, S2: StringSource
+{
+    fn from(tuple: (S1, Style, S2, Style, RenderedLineKind)) -> Self {
+        let (text1, style1, text2, style2, kind) = tuple;
+        RenderedLine {
+            text: vec![
+                StyledString {
+                    text: text1.make_string(),
+                    style: style1,
+                },
+                StyledString {
+                    text: text2.make_string(),
+                    style: style2,
+                }
+            ],
+            kind: kind,
+        }
+    }
+}
+
+impl RenderedLine {
+    fn trim_last(&mut self) {
+        if let Some(last_text) = self.text.last_mut() {
+            let len = last_text.text.trim_right().len();
+            last_text.text.truncate(len);
+        }
+    }
+}
+
+impl RenderedLineKind {
+    fn prefix(&self) -> StyledString {
+        match *self {
+            RenderedLineKind::SourceText { file: _, line_index } =>
+                StyledString {
+                    text: format!("{}", line_index + 1),
+                    style: Style::LineNumber,
+                },
+            RenderedLineKind::Elision =>
+                StyledString {
+                    text: String::from("..."),
+                    style: Style::LineNumber,
+                },
+            RenderedLineKind::PrimaryFileName |
+            RenderedLineKind::OtherFileName |
+            RenderedLineKind::Annotations =>
+                StyledString {
+                    text: String::from(""),
+                    style: Style::LineNumber,
+                },
+        }
+    }
+}
+
+impl StyledBuffer {
+    fn new() -> StyledBuffer {
+        StyledBuffer { text: vec![], styles: vec![] }
+    }
+
+    fn render(&self, source_kind: RenderedLineKind) -> Vec<RenderedLine> {
+        let mut output: Vec<RenderedLine> = vec![];
+        let mut styled_vec: Vec<StyledString> = vec![];
+
+        for (row, row_style) in self.text.iter().zip(&self.styles) {
+            let mut current_style = Style::NoStyle;
+            let mut current_text = String::new();
+
+            for (&c, &s) in row.iter().zip(row_style) {
+                if s != current_style {
+                    if !current_text.is_empty() {
+                        styled_vec.push(StyledString { text: current_text, style: current_style });
+                    }
+                    current_style = s;
+                    current_text = String::new();
+                }
+                current_text.push(c);
+            }
+            if !current_text.is_empty() {
+                styled_vec.push(StyledString { text: current_text, style: current_style });
+            }
+
+            if output.is_empty() {
+                //We know our first output line is source and the rest are highlights and labels
+                output.push(RenderedLine { text: styled_vec, kind: source_kind.clone() });
+            } else {
+                output.push(RenderedLine { text: styled_vec, kind: RenderedLineKind::Annotations });
+            }
+            styled_vec = vec![];
+        }
+
+        output
+    }
+
+    fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
+        while line >= self.text.len() {
+            self.text.push(vec![]);
+            self.styles.push(vec![]);
+        }
+
+        if col < self.text[line].len() {
+            self.text[line][col] = chr;
+            self.styles[line][col] = style;
+        } else {
+            let mut i = self.text[line].len();
+            while i < col {
+                let s = match self.text[0].get(i) {
+                    Some(&'\t') => '\t',
+                    _ => ' '
+                };
+                self.text[line].push(s);
+                self.styles[line].push(Style::NoStyle);
+                i += 1;
+            }
+            self.text[line].push(chr);
+            self.styles[line].push(style);
+        }
+    }
+
+    fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
+        let mut n = col;
+        for c in string.chars() {
+            self.putc(line, n, c, style);
+            n += 1;
+        }
+    }
+
+    fn set_style(&mut self, line: usize, col: usize, style: Style) {
+        if self.styles.len() > line && self.styles[line].len() > col {
+            self.styles[line][col] = style;
+        }
+    }
+
+    fn append(&mut self, line: usize, string: &str, style: Style) {
+        if line >= self.text.len() {
+            self.puts(line, 0, string, style);
+        } else {
+            let col = self.text[line].len();
+            self.puts(line, col, string, style);
+        }
+    }
+}
+
+impl FileInfo {
+    fn push_lines(&mut self,
+                  lines: &[LineInfo],
+                  is_primary: bool,
+                  label: Option<String>) {
+        assert!(lines.len() > 0);
+
+        // If a span covers multiple lines, we reduce it to a single
+        // point at the start of the span. This means that instead
+        // of producing output like this:
+        //
+        // ```
+        // --> foo.rs:2:1
+        // 2   |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
+        //     |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        // 3   |>                               -> Set<LR0Item<'grammar>>
+        //     |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        // (and so on)
+        // ```
+        //
+        // we produce:
+        //
+        // ```
+        // --> foo.rs:2:1
+        // 2   |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
+        //        ^
+        // ```
+        //
+        // Basically, although this loses information, multi-line spans just
+        // never look good.
+
+        let (line, start_col, mut end_col, is_minimized) = if lines.len() == 1 {
+            (lines[0].line_index, lines[0].start_col, lines[0].end_col, false)
+        } else {
+            (lines[0].line_index, lines[0].start_col, CharPos(lines[0].start_col.0 + 1), true)
+        };
+
+        // Watch out for "empty spans". If we get a span like 6..6, we
+        // want to just display a `^` at 6, so convert that to
+        // 6..7. This is degenerate input, but it's best to degrade
+        // gracefully -- and the parser likes to suply a span like
+        // that for EOF, in particular.
+        if start_col == end_col {
+            end_col.0 += 1;
+        }
+
+        let index = self.ensure_source_line(line);
+        self.lines[index].push_annotation(start_col,
+                                          end_col,
+                                          is_primary,
+                                          is_minimized,
+                                          label);
+    }
+
+    /// Ensure that we have a `Line` struct corresponding to
+    /// `line_index` in the file. If we already have some other lines,
+    /// then this will add the intervening lines to ensure that we
+    /// have a complete snippet. (Note that when we finally display,
+    /// some of those lines may be elided.)
+    fn ensure_source_line(&mut self, line_index: usize) -> usize {
+        if self.lines.is_empty() {
+            self.lines.push(Line::new(line_index));
+            return 0;
+        }
+
+        // Find the range of lines we have thus far.
+        let first_line_index = self.lines.first().unwrap().line_index;
+        let last_line_index = self.lines.last().unwrap().line_index;
+        assert!(first_line_index <= last_line_index);
+
+        // If the new line is lower than all the lines we have thus
+        // far, then insert the new line and any intervening lines at
+        // the front. In a silly attempt at micro-optimization, we
+        // don't just call `insert` repeatedly, but instead make a new
+        // (empty) vector, pushing the new lines onto it, and then
+        // appending the old vector.
+        if line_index < first_line_index {
+            let lines = mem::replace(&mut self.lines, vec![]);
+            self.lines.extend(
+                (line_index .. first_line_index)
+                    .map(|line| Line::new(line))
+                    .chain(lines));
+            return 0;
+        }
+
+        // If the new line comes after the ones we have so far, insert
+        // lines for it.
+        if line_index > last_line_index {
+            self.lines.extend(
+                (last_line_index+1 .. line_index+1)
+                    .map(|line| Line::new(line)));
+            return self.lines.len() - 1;
+        }
+
+        // Otherwise it should already exist.
+        return line_index - first_line_index;
+    }
+
+    fn render_file_lines(&self, codemap: &Rc<CodeMapper>) -> Vec<RenderedLine> {
+        let old_school = match self.format_mode {
+            FormatMode::OriginalErrorFormat => true,
+            FormatMode::NewErrorFormat => false,
+            FormatMode::EnvironmentSelected => check_old_skool()
+        };
+
+        // As a first step, we elide any instance of more than one
+        // continuous unannotated line.
+
+        let mut lines_iter = self.lines.iter();
+        let mut output = vec![];
+
+        // First insert the name of the file.
+        if !old_school {
+            match self.primary_span {
+                Some(span) => {
+                    let lo = codemap.lookup_char_pos(span.lo);
+                    output.push(RenderedLine {
+                        text: vec![StyledString {
+                            text: lo.file.name.clone(),
+                            style: Style::FileNameStyle,
+                        }, StyledString {
+                            text: format!(":{}:{}", lo.line, lo.col.0 + 1),
+                            style: Style::LineAndColumn,
+                        }],
+                        kind: RenderedLineKind::PrimaryFileName,
+                    });
+                    output.push(RenderedLine {
+                        text: vec![StyledString {
+                            text: "".to_string(),
+                            style: Style::FileNameStyle,
+                        }],
+                        kind: RenderedLineKind::Annotations,
+                    });
+                }
+                None => {
+                    output.push(RenderedLine {
+                        text: vec![StyledString {
+                            text: self.file.name.clone(),
+                            style: Style::FileNameStyle,
+                        }],
+                        kind: RenderedLineKind::OtherFileName,
+                    });
+                    output.push(RenderedLine {
+                        text: vec![StyledString {
+                            text: "".to_string(),
+                            style: Style::FileNameStyle,
+                        }],
+                        kind: RenderedLineKind::Annotations,
+                    });
+                }
+            }
+        }
+
+        let mut next_line = lines_iter.next();
+        while next_line.is_some() {
+            // Consume lines with annotations.
+            while let Some(line) = next_line {
+                if line.annotations.is_empty() { break; }
+
+                let mut rendered_lines = self.render_line(line);
+                assert!(!rendered_lines.is_empty());
+                if old_school {
+                    match self.primary_span {
+                        Some(span) => {
+                            let lo = codemap.lookup_char_pos(span.lo);
+                            let hi = codemap.lookup_char_pos(span.hi);
+                            //Before each secondary line in old skool-mode, print the label
+                            //as an old-style note
+                            if !line.annotations[0].is_primary {
+                                if let Some(ann) = line.annotations[0].label.clone() {
+                                    output.push(RenderedLine {
+                                        text: vec![StyledString {
+                                            text: lo.file.name.clone(),
+                                            style: Style::FileNameStyle,
+                                        }, StyledString {
+                                            text: format!(":{}:{}: {}:{} ", lo.line, lo.col.0 + 1,
+                                                hi.line, hi.col.0+1),
+                                            style: Style::LineAndColumn,
+                                        }, StyledString {
+                                            text: format!("note: "),
+                                            style: Style::OldSkoolNote,
+                                        }, StyledString {
+                                            text: format!("{}", ann),
+                                            style: Style::OldSkoolNoteText,
+                                        }],
+                                        kind: RenderedLineKind::Annotations,
+                                    });
+                                }
+                            }
+                            rendered_lines[0].text.insert(0, StyledString {
+                                text: format!(":{} ", lo.line),
+                                style: Style::LineAndColumn,
+                            });
+                            rendered_lines[0].text.insert(0, StyledString {
+                                text: lo.file.name.clone(),
+                                style: Style::FileNameStyle,
+                            });
+                            let gap_amount =
+                                rendered_lines[0].text[0].text.len() +
+                                rendered_lines[0].text[1].text.len();
+                            assert!(rendered_lines.len() >= 2,
+                                    "no annotations resulted from: {:?}",
+                                    line);
+                            for i in 1..rendered_lines.len() {
+                                rendered_lines[i].text.insert(0, StyledString {
+                                    text: vec![" "; gap_amount].join(""),
+                                    style: Style::NoStyle
+                                });
+                            }
+                        }
+                        _ =>()
+                    }
+                }
+                output.append(&mut rendered_lines);
+                next_line = lines_iter.next();
+            }
+
+            // Emit lines without annotations, but only if they are
+            // followed by a line with an annotation.
+            let unannotated_line = next_line;
+            let mut unannotated_lines = 0;
+            while let Some(line) = next_line {
+                if !line.annotations.is_empty() { break; }
+                unannotated_lines += 1;
+                next_line = lines_iter.next();
+            }
+            if unannotated_lines > 1 {
+                output.push(RenderedLine::from((String::new(),
+                                                Style::NoStyle,
+                                                RenderedLineKind::Elision)));
+            } else if let Some(line) = unannotated_line {
+                output.append(&mut self.render_line(line));
+            }
+        }
+
+        output
+    }
+
+    fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
+        let old_school = match self.format_mode {
+            FormatMode::OriginalErrorFormat => true,
+            FormatMode::NewErrorFormat => false,
+            FormatMode::EnvironmentSelected => check_old_skool()
+        };
+
+        let source_string = self.file.get_line(line.line_index)
+                                     .unwrap_or("");
+        let source_kind = RenderedLineKind::SourceText {
+            file: self.file.clone(),
+            line_index: line.line_index,
+        };
+
+        let mut styled_buffer = StyledBuffer::new();
+
+        // First create the source line we will highlight.
+        styled_buffer.append(0, &source_string, Style::Quotation);
+
+        if line.annotations.is_empty() {
+            return styled_buffer.render(source_kind);
+        }
+
+        // We want to display like this:
+        //
+        //      vec.push(vec.pop().unwrap());
+        //      ---      ^^^               _ previous borrow ends here
+        //      |        |
+        //      |        error occurs here
+        //      previous borrow of `vec` occurs here
+        //
+        // But there are some weird edge cases to be aware of:
+        //
+        //      vec.push(vec.pop().unwrap());
+        //      --------                    - previous borrow ends here
+        //      ||
+        //      |this makes no sense
+        //      previous borrow of `vec` occurs here
+        //
+        // For this reason, we group the lines into "highlight lines"
+        // and "annotations lines", where the highlight lines have the `~`.
+
+        //let mut highlight_line = Self::whitespace(&source_string);
+
+        // Sort the annotations by (start, end col)
+        let mut annotations = line.annotations.clone();
+        annotations.sort();
+
+        // Next, create the highlight line.
+        for annotation in &annotations {
+            if old_school {
+                for p in annotation.start_col .. annotation.end_col {
+                    if p == annotation.start_col {
+                        styled_buffer.putc(1, p, '^',
+                            if annotation.is_primary {
+                                Style::UnderlinePrimary
+                            } else {
+                                Style::OldSkoolNote
+                            });
+                    }
+                    else {
+                        styled_buffer.putc(1, p, '~',
+                            if annotation.is_primary {
+                                Style::UnderlinePrimary
+                            } else {
+                                Style::OldSkoolNote
+                            });
+                    }
+                }
+            }
+            else {
+                for p in annotation.start_col .. annotation.end_col {
+                    if annotation.is_primary {
+                        styled_buffer.putc(1, p, '^', Style::UnderlinePrimary);
+                        if !annotation.is_minimized {
+                            styled_buffer.set_style(0, p, Style::UnderlinePrimary);
+                        }
+                    } else {
+                        styled_buffer.putc(1, p, '-', Style::UnderlineSecondary);
+                        if !annotation.is_minimized {
+                            styled_buffer.set_style(0, p, Style::UnderlineSecondary);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Now we are going to write labels in. To start, we'll exclude
+        // the annotations with no labels.
+        let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) =
+            annotations.into_iter()
+                       .partition(|a| a.label.is_some());
+
+        // If there are no annotations that need text, we're done.
+        if labeled_annotations.is_empty() {
+            return styled_buffer.render(source_kind);
+        }
+        if old_school {
+            return styled_buffer.render(source_kind);
+        }
+
+        // Now add the text labels. We try, when possible, to stick the rightmost
+        // annotation at the end of the highlight line:
+        //
+        //      vec.push(vec.pop().unwrap());
+        //      ---      ---               - previous borrow ends here
+        //
+        // But sometimes that's not possible because one of the other
+        // annotations overlaps it. For example, from the test
+        // `span_overlap_label`, we have the following annotations
+        // (written on distinct lines for clarity):
+        //
+        //      fn foo(x: u32) {
+        //      --------------
+        //             -
+        //
+        // In this case, we can't stick the rightmost-most label on
+        // the highlight line, or we would get:
+        //
+        //      fn foo(x: u32) {
+        //      -------- x_span
+        //      |
+        //      fn_span
+        //
+        // which is totally weird. Instead we want:
+        //
+        //      fn foo(x: u32) {
+        //      --------------
+        //      |      |
+        //      |      x_span
+        //      fn_span
+        //
+        // which is...less weird, at least. In fact, in general, if
+        // the rightmost span overlaps with any other span, we should
+        // use the "hang below" version, so we can at least make it
+        // clear where the span *starts*.
+        let mut labeled_annotations = &labeled_annotations[..];
+        match labeled_annotations.split_last().unwrap() {
+            (last, previous) => {
+                if previous.iter()
+                           .chain(&unlabeled_annotations)
+                           .all(|a| !overlaps(a, last))
+                {
+                    // append the label afterwards; we keep it in a separate
+                    // string
+                    let highlight_label: String = format!(" {}", last.label.as_ref().unwrap());
+                    if last.is_primary {
+                        styled_buffer.append(1, &highlight_label, Style::LabelPrimary);
+                    } else {
+                        styled_buffer.append(1, &highlight_label, Style::LabelSecondary);
+                    }
+                    labeled_annotations = previous;
+                }
+            }
+        }
+
+        // If that's the last annotation, we're done
+        if labeled_annotations.is_empty() {
+            return styled_buffer.render(source_kind);
+        }
+
+        for (index, annotation) in labeled_annotations.iter().enumerate() {
+            // Leave:
+            // - 1 extra line
+            // - One line for each thing that comes after
+            let comes_after = labeled_annotations.len() - index - 1;
+            let blank_lines = 3 + comes_after;
+
+            // For each blank line, draw a `|` at our column. The
+            // text ought to be long enough for this.
+            for index in 2..blank_lines {
+                if annotation.is_primary {
+                    styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlinePrimary);
+                } else {
+                    styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlineSecondary);
+                }
+            }
+
+            if annotation.is_primary {
+                styled_buffer.puts(blank_lines, annotation.start_col,
+                    annotation.label.as_ref().unwrap(), Style::LabelPrimary);
+            } else {
+                styled_buffer.puts(blank_lines, annotation.start_col,
+                    annotation.label.as_ref().unwrap(), Style::LabelSecondary);
+            }
+        }
+
+        styled_buffer.render(source_kind)
+    }
+}
+
+fn prepend_prefixes(rendered_lines: &mut [RenderedLine], format_mode: &FormatMode) {
+    let old_school = match *format_mode {
+        FormatMode::OriginalErrorFormat => true,
+        FormatMode::NewErrorFormat => false,
+        FormatMode::EnvironmentSelected => check_old_skool()
+    };
+    if old_school {
+        return;
+    }
+
+    let prefixes: Vec<_> =
+        rendered_lines.iter()
+                      .map(|rl| rl.kind.prefix())
+                      .collect();
+
+    // find the max amount of spacing we need; add 1 to
+    // p.text.len() to leave space between the prefix and the
+    // source text
+    let padding_len =
+        prefixes.iter()
+                .map(|p| if p.text.len() == 0 { 0 } else { p.text.len() + 1 })
+                .max()
+                .unwrap_or(0);
+
+    // Ensure we insert at least one character of padding, so that the
+    // `-->` arrows can fit etc.
+    let padding_len = cmp::max(padding_len, 1);
+
+    for (mut prefix, line) in prefixes.into_iter().zip(rendered_lines) {
+        let extra_spaces = (prefix.text.len() .. padding_len).map(|_| ' ');
+        prefix.text.extend(extra_spaces);
+        match line.kind {
+            RenderedLineKind::Elision => {
+                line.text.insert(0, prefix);
+            }
+            RenderedLineKind::PrimaryFileName => {
+                //   --> filename
+                // 22 |>
+                //   ^
+                //   padding_len
+                let dashes = (0..padding_len - 1).map(|_| ' ')
+                                                 .chain(Some('-'))
+                                                 .chain(Some('-'))
+                                                 .chain(Some('>'))
+                                                 .chain(Some(' '));
+                line.text.insert(0, StyledString {text: dashes.collect(),
+                                                  style: Style::LineNumber})
+            }
+            RenderedLineKind::OtherFileName => {
+                //   ::: filename
+                // 22 |>
+                //   ^
+                //   padding_len
+                let dashes = (0..padding_len - 1).map(|_| ' ')
+                                                 .chain(Some(':'))
+                                                 .chain(Some(':'))
+                                                 .chain(Some(':'))
+                                                 .chain(Some(' '));
+                line.text.insert(0, StyledString {text: dashes.collect(),
+                                                  style: Style::LineNumber})
+            }
+            _ => {
+                line.text.insert(0, prefix);
+                line.text.insert(1, StyledString {text: String::from("|> "),
+                                                  style: Style::LineNumber})
+            }
+        }
+    }
+}
+
+fn trim_lines(rendered_lines: &mut [RenderedLine]) {
+    for line in rendered_lines {
+        while !line.text.is_empty() {
+            line.trim_last();
+            if line.text.last().unwrap().text.is_empty() {
+                line.text.pop();
+            } else {
+                break;
+            }
+        }
+    }
+}
+
+impl Line {
+    fn new(line_index: usize) -> Line {
+        Line {
+            line_index: line_index,
+            annotations: vec![]
+        }
+    }
+
+    fn push_annotation(&mut self,
+                       start: CharPos,
+                       end: CharPos,
+                       is_primary: bool,
+                       is_minimized: bool,
+                       label: Option<String>) {
+        self.annotations.push(Annotation {
+            start_col: start.0,
+            end_col: end.0,
+            is_primary: is_primary,
+            is_minimized: is_minimized,
+            label: label,
+        });
+    }
+}
+
+fn overlaps(a1: &Annotation,
+            a2: &Annotation)
+            -> bool
+{
+    (a2.start_col .. a2.end_col).contains(a1.start_col) ||
+        (a1.start_col .. a1.end_col).contains(a2.start_col)
+}
index 927e4126472f637cfb402e80b6291443b891eb45..7db1a6348b27c9cb737370f602c8eefcb4bee470 100644 (file)
@@ -16,3 +16,4 @@ rustc_data_structures = { path = "../librustc_data_structures" }
 serialize = { path = "../libserialize" }
 log = { path = "../liblog" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
\ No newline at end of file
index 9dc50a6306406d8684afcab039c98331e75f4417..d38f979e33c5ac154b5277ecb0bee115dc234cc4 100644 (file)
@@ -57,8 +57,8 @@ use std::fs::File;
 use std::io::Write;
 use syntax::ast;
 use syntax::attr::AttrMetaMethods;
-use syntax::codemap::Span;
 use syntax::parse::token::InternedString;
+use syntax_pos::Span;
 
 const IF_THIS_CHANGED: &'static str = "rustc_if_this_changed";
 const THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need";
@@ -195,7 +195,7 @@ fn check_paths<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             }
         };
 
-        for &(_, source_def_id, source_dep_node) in sources {
+        for &(_, source_def_id, ref source_dep_node) in sources {
             let dependents = query.transitive_successors(source_dep_node);
             for &(target_span, ref target_pass, _, ref target_dep_node) in targets {
                 if !dependents.contains(&target_dep_node) {
@@ -239,7 +239,7 @@ fn dump_graph(tcx: TyCtxt) {
     { // dump a .txt file with just the edges:
         let txt_path = format!("{}.txt", path);
         let mut file = File::create(&txt_path).unwrap();
-        for &(source, target) in &edges {
+        for &(ref source, ref target) in &edges {
             write!(file, "{:?} -> {:?}\n", source, target).unwrap();
         }
     }
@@ -252,34 +252,34 @@ fn dump_graph(tcx: TyCtxt) {
     }
 }
 
-pub struct GraphvizDepGraph(FnvHashSet<DepNode<DefId>>,
-                            Vec<(DepNode<DefId>, DepNode<DefId>)>);
+pub struct GraphvizDepGraph<'q>(FnvHashSet<&'q DepNode<DefId>>,
+                                Vec<(&'q DepNode<DefId>, &'q DepNode<DefId>)>);
 
-impl<'a, 'tcx> dot::GraphWalk<'a> for GraphvizDepGraph {
-    type Node = DepNode<DefId>;
-    type Edge = (DepNode<DefId>, DepNode<DefId>);
-    fn nodes(&self) -> dot::Nodes<DepNode<DefId>> {
+impl<'a, 'tcx, 'q> dot::GraphWalk<'a> for GraphvizDepGraph<'q> {
+    type Node = &'q DepNode<DefId>;
+    type Edge = (&'q DepNode<DefId>, &'q DepNode<DefId>);
+    fn nodes(&self) -> dot::Nodes<&'q DepNode<DefId>> {
         let nodes: Vec<_> = self.0.iter().cloned().collect();
         nodes.into_cow()
     }
-    fn edges(&self) -> dot::Edges<(DepNode<DefId>, DepNode<DefId>)> {
+    fn edges(&self) -> dot::Edges<(&'q DepNode<DefId>, &'q DepNode<DefId>)> {
         self.1[..].into_cow()
     }
-    fn source(&self, edge: &(DepNode<DefId>, DepNode<DefId>)) -> DepNode<DefId> {
+    fn source(&self, edge: &(&'q DepNode<DefId>, &'q DepNode<DefId>)) -> &'q DepNode<DefId> {
         edge.0
     }
-    fn target(&self, edge: &(DepNode<DefId>, DepNode<DefId>)) -> DepNode<DefId> {
+    fn target(&self, edge: &(&'q DepNode<DefId>, &'q DepNode<DefId>)) -> &'q DepNode<DefId> {
         edge.1
     }
 }
 
-impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
-    type Node = DepNode<DefId>;
-    type Edge = (DepNode<DefId>, DepNode<DefId>);
+impl<'a, 'tcx, 'q> dot::Labeller<'a> for GraphvizDepGraph<'q> {
+    type Node = &'q DepNode<DefId>;
+    type Edge = (&'q DepNode<DefId>, &'q DepNode<DefId>);
     fn graph_id(&self) -> dot::Id {
         dot::Id::new("DependencyGraph").unwrap()
     }
-    fn node_id(&self, n: &DepNode<DefId>) -> dot::Id {
+    fn node_id(&self, n: &&'q DepNode<DefId>) -> dot::Id {
         let s: String =
             format!("{:?}", n).chars()
                               .map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' })
@@ -287,7 +287,7 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
         debug!("n={:?} s={:?}", n, s);
         dot::Id::new(s).unwrap()
     }
-    fn node_label(&self, n: &DepNode<DefId>) -> dot::LabelText {
+    fn node_label(&self, n: &&'q DepNode<DefId>) -> dot::LabelText {
         dot::LabelText::label(format!("{:?}", n))
     }
 }
@@ -295,8 +295,8 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
 // Given an optional filter like `"x,y,z"`, returns either `None` (no
 // filter) or the set of nodes whose labels contain all of those
 // substrings.
-fn node_set(query: &DepGraphQuery<DefId>, filter: &DepNodeFilter)
-            -> Option<FnvHashSet<DepNode<DefId>>>
+fn node_set<'q>(query: &'q DepGraphQuery<DefId>, filter: &DepNodeFilter)
+                -> Option<FnvHashSet<&'q DepNode<DefId>>>
 {
     debug!("node_set(filter={:?})", filter);
 
@@ -307,10 +307,10 @@ fn node_set(query: &DepGraphQuery<DefId>, filter: &DepNodeFilter)
     Some(query.nodes().into_iter().filter(|n| filter.test(n)).collect())
 }
 
-fn filter_nodes(query: &DepGraphQuery<DefId>,
-                sources: &Option<FnvHashSet<DepNode<DefId>>>,
-                targets: &Option<FnvHashSet<DepNode<DefId>>>)
-                -> FnvHashSet<DepNode<DefId>>
+fn filter_nodes<'q>(query: &'q DepGraphQuery<DefId>,
+                    sources: &Option<FnvHashSet<&'q DepNode<DefId>>>,
+                    targets: &Option<FnvHashSet<&'q DepNode<DefId>>>)
+                    -> FnvHashSet<&'q DepNode<DefId>>
 {
     if let &Some(ref sources) = sources {
         if let &Some(ref targets) = targets {
@@ -325,21 +325,21 @@ fn filter_nodes(query: &DepGraphQuery<DefId>,
     }
 }
 
-fn walk_nodes(query: &DepGraphQuery<DefId>,
-              starts: &FnvHashSet<DepNode<DefId>>,
-              direction: Direction)
-              -> FnvHashSet<DepNode<DefId>>
+fn walk_nodes<'q>(query: &'q DepGraphQuery<DefId>,
+                  starts: &FnvHashSet<&'q DepNode<DefId>>,
+                  direction: Direction)
+                  -> FnvHashSet<&'q DepNode<DefId>>
 {
     let mut set = FnvHashSet();
-    for start in starts {
+    for &start in starts {
         debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING);
-        if set.insert(*start) {
+        if set.insert(start) {
             let mut stack = vec![query.indices[start]];
             while let Some(index) = stack.pop() {
                 for (_, edge) in query.graph.adjacent_edges(index, direction) {
                     let neighbor_index = edge.source_or_target(direction);
                     let neighbor = query.graph.node_data(neighbor_index);
-                    if set.insert(*neighbor) {
+                    if set.insert(neighbor) {
                         stack.push(neighbor_index);
                     }
                 }
@@ -349,10 +349,10 @@ fn walk_nodes(query: &DepGraphQuery<DefId>,
     set
 }
 
-fn walk_between(query: &DepGraphQuery<DefId>,
-                sources: &FnvHashSet<DepNode<DefId>>,
-                targets: &FnvHashSet<DepNode<DefId>>)
-                -> FnvHashSet<DepNode<DefId>>
+fn walk_between<'q>(query: &'q DepGraphQuery<DefId>,
+                    sources: &FnvHashSet<&'q DepNode<DefId>>,
+                    targets: &FnvHashSet<&'q DepNode<DefId>>)
+                    -> FnvHashSet<&'q DepNode<DefId>>
 {
     // This is a bit tricky. We want to include a node only if it is:
     // (a) reachable from a source and (b) will reach a target. And we
@@ -365,16 +365,16 @@ fn walk_between(query: &DepGraphQuery<DefId>,
     let mut node_states = vec![State::Undecided; query.graph.len_nodes()];
 
     for &target in targets {
-        node_states[query.indices[&target].0] = State::Included;
+        node_states[query.indices[target].0] = State::Included;
     }
 
-    for source in sources.iter().map(|n| query.indices[n]) {
+    for source in sources.iter().map(|&n| query.indices[n]) {
         recurse(query, &mut node_states, source);
     }
 
     return query.nodes()
                 .into_iter()
-                .filter(|n| {
+                .filter(|&n| {
                     let index = query.indices[n];
                     node_states[index.0] == State::Included
                 })
@@ -417,12 +417,12 @@ fn walk_between(query: &DepGraphQuery<DefId>,
     }
 }
 
-fn filter_edges(query: &DepGraphQuery<DefId>,
-                nodes: &FnvHashSet<DepNode<DefId>>)
-                -> Vec<(DepNode<DefId>, DepNode<DefId>)>
+fn filter_edges<'q>(query: &'q DepGraphQuery<DefId>,
+                    nodes: &FnvHashSet<&'q DepNode<DefId>>)
+                    -> Vec<(&'q DepNode<DefId>, &'q DepNode<DefId>)>
 {
     query.edges()
          .into_iter()
-         .filter(|&(source, target)| nodes.contains(&source) && nodes.contains(&target))
+         .filter(|&(source, target)| nodes.contains(source) && nodes.contains(target))
          .collect()
 }
index 24ecce1148745428ce56ba0feb22a4cd91e15ba3..cbc246ac2a11bfc6c902c72e38df0774c2557c93 100644 (file)
@@ -114,8 +114,8 @@ mod svh_visitor {
     pub use self::SawStmtComponent::*;
     use self::SawAbiComponent::*;
     use syntax::ast::{self, Name, NodeId};
-    use syntax::codemap::Span;
     use syntax::parse::token;
+    use syntax_pos::Span;
     use rustc::ty::TyCtxt;
     use rustc::hir;
     use rustc::hir::*;
@@ -251,7 +251,7 @@ mod svh_visitor {
             ExprType(..)             => SawExprType,
             ExprIf(..)               => SawExprIf,
             ExprWhile(..)            => SawExprWhile,
-            ExprLoop(_, id)          => SawExprLoop(id.map(|id| id.as_str())),
+            ExprLoop(_, id)          => SawExprLoop(id.map(|id| id.node.as_str())),
             ExprMatch(..)            => SawExprMatch,
             ExprClosure(..)          => SawExprClosure,
             ExprBlock(..)            => SawExprBlock,
@@ -401,10 +401,6 @@ mod svh_visitor {
             SawPath.hash(self.st); visit::walk_path(self, path)
         }
 
-        fn visit_path_list_item(&mut self, prefix: &'a Path, item: &'a PathListItem) {
-            SawPath.hash(self.st); visit::walk_path_list_item(self, prefix, item)
-        }
-
         fn visit_block(&mut self, b: &'a Block) {
             SawBlock.hash(self.st); visit::walk_block(self, b)
         }
index 005146d91eae46d7862b639e9669eecdc954b5d6..ed31e0ba51056a34fe41af2e76a32b2a58b481ba 100644 (file)
@@ -30,6 +30,7 @@ extern crate serialize as rustc_serialize;
 
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
+extern crate syntax_pos;
 
 mod assert_dep_graph;
 mod calculate_svh;
index f9e90f393219dcc7d6462d16628a55d576e46004..2fd6973909a8ec59c33a146658b60913049be8a2 100644 (file)
@@ -57,7 +57,7 @@ impl RetracedDefIdDirectory {
         self.ids[index.index as usize]
     }
 
-    pub fn map(&self, node: DepNode<DefPathIndex>) -> Option<DepNode<DefId>> {
+    pub fn map(&self, node: &DepNode<DefPathIndex>) -> Option<DepNode<DefId>> {
         node.map_def(|&index| self.def_id(index))
     }
 }
@@ -91,7 +91,7 @@ impl<'a,'tcx> DefIdDirectoryBuilder<'a,'tcx> {
                  .clone()
     }
 
-    pub fn map(&mut self, node: DepNode<DefId>) -> DepNode<DefPathIndex> {
+    pub fn map(&mut self, node: &DepNode<DefId>) -> DepNode<DefPathIndex> {
         node.map_def(|&def_id| Some(self.add(def_id))).unwrap()
     }
 
index b729f25b873d4f05ec8df2b45e7239c4cb02fa7f..99119dd184c8b3929045d5e75afa08134e3a776a 100644 (file)
@@ -39,8 +39,8 @@ impl<'a, 'tcx> HashContext<'a, 'tcx> {
         }
     }
 
-    pub fn hash(&mut self, dep_node: DepNode<DefId>) -> Option<u64> {
-        match dep_node {
+    pub fn hash(&mut self, dep_node: &DepNode<DefId>) -> Option<u64> {
+        match *dep_node {
             // HIR nodes (which always come from our crate) are an input:
             DepNode::Hir(def_id) => {
                 assert!(def_id.is_local());
index e3fd290443c11bf38ceb002c0bb23d2400b0f748..0ac1018462ee7a7eb06a2e6cc2b75a01eacda4db 100644 (file)
@@ -114,15 +114,15 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let clean_nodes =
         serialized_dep_graph.nodes
                             .iter()
-                            .filter_map(|&node| retraced.map(node))
+                            .filter_map(|node| retraced.map(node))
                             .filter(|node| !dirty_nodes.contains(node))
-                            .map(|node| (node, node));
+                            .map(|node| (node.clone(), node));
 
     // Add nodes and edges that are not dirty into our main graph.
     let dep_graph = tcx.dep_graph.clone();
     for (source, target) in clean_edges.into_iter().chain(clean_nodes) {
-        let _task = dep_graph.in_task(target);
-        dep_graph.read(source);
+        let _task = dep_graph.in_task(target.clone());
+        dep_graph.read(source.clone());
 
         debug!("decode_dep_graph: clean edge: {:?} -> {:?}", source, target);
     }
@@ -140,7 +140,7 @@ fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     for hash in hashes {
         match hash.node.map_def(|&i| retraced.def_id(i)) {
             Some(dep_node) => {
-                let current_hash = hcx.hash(dep_node).unwrap();
+                let current_hash = hcx.hash(&dep_node).unwrap();
                 debug!("initial_dirty_nodes: hash of {:?} is {:?}, was {:?}",
                        dep_node, current_hash, hash.hash);
                 if current_hash != hash.hash {
@@ -171,7 +171,7 @@ fn compute_clean_edges(serialized_edges: &[(SerializedEdge)],
     // target) if neither node has been removed. If the source has
     // been removed, add target to the list of dirty nodes.
     let mut clean_edges = Vec::with_capacity(serialized_edges.len());
-    for &(serialized_source, serialized_target) in serialized_edges {
+    for &(ref serialized_source, ref serialized_target) in serialized_edges {
         if let Some(target) = retraced.map(serialized_target) {
             if let Some(source) = retraced.map(serialized_source) {
                 clean_edges.push((source, target))
index 7deb1ca36dbded0451760ab5e15ac870434bbcc7..99f4d4f3072989b6b74a4c561bd9f32f086a0871 100644 (file)
@@ -99,7 +99,7 @@ pub fn encode_dep_graph<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
         query.nodes()
              .into_iter()
              .filter_map(|dep_node| {
-                 hcx.hash(dep_node)
+                 hcx.hash(&dep_node)
                     .map(|hash| {
                         let node = builder.map(dep_node);
                         SerializedHash { node: node, hash: hash }
@@ -147,7 +147,7 @@ pub fn encode_metadata_hashes<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
         let meta_data_def_ids =
             query.nodes()
                  .into_iter()
-                 .filter_map(|dep_node| match dep_node {
+                 .filter_map(|dep_node| match *dep_node {
                      DepNode::MetaData(def_id) if def_id.is_local() => Some(def_id),
                      _ => None,
                  });
@@ -165,8 +165,8 @@ pub fn encode_metadata_hashes<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
                 let dep_node = DepNode::MetaData(def_id);
                 let mut state = SipHasher::new();
                 debug!("save: computing metadata hash for {:?}", dep_node);
-                for node in query.transitive_predecessors(dep_node) {
-                    if let Some(hash) = hcx.hash(node) {
+                for node in query.transitive_predecessors(&dep_node) {
+                    if let Some(hash) = hcx.hash(&node) {
                         debug!("save: predecessor {:?} has hash {}", node, hash);
                         state.write_u64(hash.to_le());
                     } else {
index 7674cc529bef81e85b1348b47634fdb6a9ccc6d8..4d5c0d7ba0ae1130f253a8e05113c9078ed202e3 100644 (file)
@@ -15,3 +15,4 @@ rustc = { path = "../librustc" }
 rustc_back = { path = "../librustc_back" }
 rustc_const_eval = { path = "../librustc_const_eval" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
index 27a6e433c7399c200fbf2d1b1ca7b17c07d94a6a..7e9b6f561b9846f1d0b4f09c72b31918ebd63a64 100644 (file)
@@ -15,7 +15,7 @@ use lint::{LintPass, LateLintPass};
 
 use syntax::ast;
 use syntax::attr::{self, AttrMetaMethods};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use rustc::hir::{self, PatKind};
 use rustc::hir::intravisit::FnKind;
@@ -274,9 +274,9 @@ impl LateLintPass for NonSnakeCase {
     }
 
     fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) {
-        if let &PatKind::Ident(_, ref path1, _) = &p.node {
-            let def = cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def());
-            if let Some(Def::Local(..)) = def {
+        if let &PatKind::Binding(_, ref path1, _) = &p.node {
+            // Exclude parameter names from foreign functions (they have no `Def`)
+            if cx.tcx.expect_def_or_none(p.id).is_some() {
                 self.check_snake_case(cx, "variable", &path1.node.as_str(), Some(p.span));
             }
         }
@@ -360,12 +360,13 @@ impl LateLintPass for NonUpperCaseGlobals {
 
     fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) {
         // Lint for constants that look like binding identifiers (#7526)
-        match (&p.node, cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def())) {
-            (&PatKind::Ident(_, ref path1, _), Some(Def::Const(..))) => {
-                NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern",
-                                                      path1.node, p.span);
+        if let PatKind::Path(ref path) = p.node {
+            if !path.global && path.segments.len() == 1 && path.segments[0].parameters.is_empty() {
+                if let Def::Const(..) = cx.tcx.expect_def(p.id) {
+                    NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern",
+                                                          path.segments[0].name, path.span);
+                }
             }
-            _ => {}
         }
     }
 }
index 49b59aea46ee7136005b88ed69cbab2478fb58f5..18f9733040e0f46b8e6f6449efb76e2efbe6f2ef 100644 (file)
@@ -45,7 +45,7 @@ use std::collections::HashSet;
 
 use syntax::{ast};
 use syntax::attr::{self, AttrMetaMethods};
-use syntax::codemap::{self, Span};
+use syntax_pos::{self, Span};
 
 use rustc::hir::{self, PatKind};
 use rustc::hir::intravisit::FnKind;
@@ -157,22 +157,13 @@ impl LintPass for NonShorthandFieldPatterns {
 
 impl LateLintPass for NonShorthandFieldPatterns {
     fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
-        let def_map = cx.tcx.def_map.borrow();
-        if let PatKind::Struct(_, ref v, _) = pat.node {
-            let field_pats = v.iter().filter(|fieldpat| {
+        if let PatKind::Struct(_, ref field_pats, _) = pat.node {
+            for fieldpat in field_pats {
                 if fieldpat.node.is_shorthand {
-                    return false;
-                }
-                let def = def_map.get(&fieldpat.node.pat.id).map(|d| d.full_def());
-                if let Some(def_id) = cx.tcx.map.opt_local_def_id(fieldpat.node.pat.id) {
-                    def == Some(Def::Local(def_id, fieldpat.node.pat.id))
-                } else {
-                    false
+                    continue;
                 }
-            });
-            for fieldpat in field_pats {
-                if let PatKind::Ident(_, ident, None) = fieldpat.node.pat.node {
-                    if ident.node.unhygienize() == fieldpat.node.name {
+                if let PatKind::Binding(_, ident, None) = fieldpat.node.pat.node {
+                    if ident.node == fieldpat.node.name {
                         cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span,
                                      &format!("the `{}:` in this pattern is redundant and can \
                                               be removed", ident.node))
@@ -377,7 +368,7 @@ impl LateLintPass for MissingDoc {
             hir::ItemImpl(_, _, _, Some(ref trait_ref), _, ref impl_items) => {
                 // If the trait is private, add the impl items to private_traits so they don't get
                 // reported for missing docs.
-                let real_trait = cx.tcx.trait_ref_to_def_id(trait_ref);
+                let real_trait = cx.tcx.expect_def(trait_ref.ref_id).def_id();
                 if let Some(node_id) = cx.tcx.map.as_local_node_id(real_trait) {
                     match cx.tcx.map.find(node_id) {
                         Some(hir_map::NodeItem(item)) => if item.vis == hir::Visibility::Inherited {
@@ -780,11 +771,9 @@ impl LateLintPass for UnconditionalRecursion {
                                   id: ast::NodeId) -> bool {
             match tcx.map.get(id) {
                 hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => {
-                    tcx.def_map
-                       .borrow()
-                       .get(&callee.id)
-                       .map_or(false,
-                               |def| def.def_id() == tcx.map.local_def_id(fn_id))
+                    tcx.expect_def_or_none(callee.id).map_or(false, |def| {
+                        def.def_id() == tcx.map.local_def_id(fn_id)
+                    })
                 }
                 _ => false
             }
@@ -820,7 +809,9 @@ impl LateLintPass for UnconditionalRecursion {
             // Check for calls to methods via explicit paths (e.g. `T::method()`).
             match tcx.map.get(id) {
                 hir_map::NodeExpr(&hir::Expr { node: hir::ExprCall(ref callee, _), .. }) => {
-                    match tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def()) {
+                    // The callee is an arbitrary expression,
+                    // it doesn't necessarily have a definition.
+                    match tcx.expect_def_or_none(callee.id) {
                         Some(Def::Method(def_id)) => {
                             let item_substs = tcx.node_id_item_substs(callee.id);
                             method_call_refers_to_method(
@@ -1057,7 +1048,7 @@ impl LateLintPass for MutableTransmutes {
                 hir::ExprPath(..) => (),
                 _ => return None
             }
-            if let Def::Fn(did) = cx.tcx.resolve_expr(expr) {
+            if let Def::Fn(did) = cx.tcx.expect_def(expr.id) {
                 if !def_id_is_transmute(cx, did) {
                     return None;
                 }
@@ -1149,9 +1140,9 @@ impl LateLintPass for DropWithReprExtern {
                     if hints.iter().any(|attr| *attr == attr::ReprExtern) &&
                         self_type_def.dtor_kind().has_drop_flag() {
                         let drop_impl_span = ctx.tcx.map.def_id_span(drop_impl_did,
-                                                                     codemap::DUMMY_SP);
+                                                                     syntax_pos::DUMMY_SP);
                         let self_defn_span = ctx.tcx.map.def_id_span(self_type_did,
-                                                                     codemap::DUMMY_SP);
+                                                                     syntax_pos::DUMMY_SP);
                         ctx.span_lint_note(DROP_WITH_REPR_EXTERN,
                                            drop_impl_span,
                                            "implementing Drop adds hidden state to types, \
index 9fca6d3d201390b93f51e7a233061eb4525a3d8a..4ae5b3afdba19f4bf1d119173c00d92d98da66cd 100644 (file)
@@ -45,6 +45,7 @@ extern crate rustc;
 extern crate log;
 extern crate rustc_back;
 extern crate rustc_const_eval;
+extern crate syntax_pos;
 
 pub use rustc::lint as lint;
 pub use rustc::middle as middle;
@@ -163,7 +164,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
         },
         FutureIncompatibleInfo {
             id: LintId::of(INVALID_TYPE_PARAM_DEFAULT),
-            reference: "PR 30742 <https://github.com/rust-lang/rust/pull/30724>",
+            reference: "PR 30724 <https://github.com/rust-lang/rust/pull/30724>",
         },
         FutureIncompatibleInfo {
             id: LintId::of(SUPER_OR_SELF_IN_GLOBAL_PATH),
@@ -202,6 +203,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
             id: LintId::of(HR_LIFETIME_IN_ASSOC_TYPE),
             reference: "issue #33685 <https://github.com/rust-lang/rust/issues/33685>",
         },
+        FutureIncompatibleInfo {
+            id: LintId::of(LIFETIME_UNDERSCORE),
+            reference: "RFC 1177 <https://github.com/rust-lang/rfcs/pull/1177>",
+        },
         ]);
 
     // We have one lint pass defined specially
index 892924db6fad86c57fab9f90265b8eb4ff615e21..97f97a889edc30431de9fe399b0b4afb679fbea0 100644 (file)
@@ -16,6 +16,7 @@ use rustc::ty::{self, Ty, TyCtxt};
 use middle::const_val::ConstVal;
 use rustc_const_eval::eval_const_expr_partial;
 use rustc_const_eval::EvalHint::ExprTypeChecked;
+use util::common::slice_pat;
 use util::nodemap::{FnvHashSet};
 use lint::{LateContext, LintContext, LintArray};
 use lint::{LintPass, LateLintPass};
@@ -26,7 +27,8 @@ use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
 use syntax::ast;
 use syntax::abi::Abi;
 use syntax::attr;
-use syntax::codemap::{self, Span};
+use syntax_pos::Span;
+use syntax::codemap;
 
 use rustc::hir;
 
@@ -459,8 +461,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 // Check for a repr() attribute to specify the size of the
                 // discriminant.
                 let repr_hints = cx.lookup_repr_hints(def.did);
-                match &**repr_hints {
-                    [] => {
+                match slice_pat(&&**repr_hints) {
+                    &[] => {
                         // Special-case types like `Option<extern fn()>`.
                         if !is_repr_nullable_ptr(cx, def, substs) {
                             return FfiUnsafe(
@@ -470,7 +472,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                                  the type")
                         }
                     }
-                    [ref hint] => {
+                    &[ref hint] => {
                         if !hint.is_ffi_safe() {
                             // FIXME: This shouldn't be reachable: we should check
                             // this earlier.
index d1595f243c9bfba65f14b3170757f463a0bf8f11..4dc1a5e4f5e9f6dda3e8bc63fa9ffab1af28c15e 100644 (file)
@@ -19,9 +19,9 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
 
 use syntax::ast;
 use syntax::attr::{self, AttrMetaMethods};
-use syntax::codemap::Span;
 use syntax::feature_gate::{KNOWN_ATTRIBUTES, AttributeType};
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 use rustc_back::slice;
 use rustc::hir;
@@ -43,7 +43,7 @@ impl UnusedMut {
 
         let mut mutables = FnvHashMap();
         for p in pats {
-            pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path1| {
+            pat_util::pat_bindings(p, |mode, id, _, path1| {
                 let name = path1.node;
                 if let hir::BindByValue(hir::MutMutable) = mode {
                     if !name.as_str().starts_with("_") {
@@ -150,12 +150,9 @@ impl LateLintPass for UnusedResults {
                 if attr.check_name("must_use") {
                     let mut msg = "unused result which must be used".to_string();
                     // check for #[must_use="..."]
-                    match attr.value_str() {
-                        None => {}
-                        Some(s) => {
-                            msg.push_str(": ");
-                            msg.push_str(&s);
-                        }
+                    if let Some(s) = attr.value_str() {
+                        msg.push_str(": ");
+                        msg.push_str(&s);
                     }
                     cx.span_lint(UNUSED_MUST_USE, sp, &msg);
                     return true;
@@ -365,12 +362,9 @@ impl EarlyLintPass for UnusedParens {
 
     fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
         let (value, msg) = match s.node {
-            ast::StmtKind::Decl(ref decl, _) => match decl.node {
-                ast::DeclKind::Local(ref local) => match local.init {
-                    Some(ref value) => (value, "assigned value"),
-                    None => return
-                },
-                _ => return
+            ast::StmtKind::Local(ref local) => match local.init {
+                Some(ref value) => (value, "assigned value"),
+                None => return
             },
             _ => return
         };
index be7f0ed6a0298dd8b46e80a683684f9f7f7e2998..b3f5f8e536052c09769d8bb246dcd68f8586d6ad 100644 (file)
@@ -18,7 +18,9 @@ use std::path::Path;
 use std::slice;
 use std::str;
 
-pub struct ArchiveRO { ptr: ArchiveRef }
+pub struct ArchiveRO {
+    ptr: ArchiveRef,
+}
 
 pub struct Iter<'a> {
     archive: &'a ArchiveRO,
@@ -61,11 +63,16 @@ impl ArchiveRO {
         }
     }
 
-    pub fn raw(&self) -> ArchiveRef { self.ptr }
+    pub fn raw(&self) -> ArchiveRef {
+        self.ptr
+    }
 
     pub fn iter(&self) -> Iter {
         unsafe {
-            Iter { ptr: ::LLVMRustArchiveIteratorNew(self.ptr), archive: self }
+            Iter {
+                ptr: ::LLVMRustArchiveIteratorNew(self.ptr),
+                archive: self,
+            }
         }
     }
 }
@@ -86,7 +93,10 @@ impl<'a> Iterator for Iter<'a> {
         if ptr.is_null() {
             ::last_error().map(Err)
         } else {
-            Some(Ok(Child { ptr: ptr, _data: marker::PhantomData }))
+            Some(Ok(Child {
+                ptr: ptr,
+                _data: marker::PhantomData,
+            }))
         }
     }
 }
@@ -107,8 +117,7 @@ impl<'a> Child<'a> {
             if name_ptr.is_null() {
                 None
             } else {
-                let name = slice::from_raw_parts(name_ptr as *const u8,
-                                                 name_len as usize);
+                let name = slice::from_raw_parts(name_ptr as *const u8, name_len as usize);
                 str::from_utf8(name).ok().map(|s| s.trim())
             }
         }
@@ -125,11 +134,15 @@ impl<'a> Child<'a> {
         }
     }
 
-    pub fn raw(&self) -> ::ArchiveChildRef { self.ptr }
+    pub fn raw(&self) -> ::ArchiveChildRef {
+        self.ptr
+    }
 }
 
 impl<'a> Drop for Child<'a> {
     fn drop(&mut self) {
-        unsafe { ::LLVMRustArchiveChildFree(self.ptr); }
+        unsafe {
+            ::LLVMRustArchiveChildFree(self.ptr);
+        }
     }
 }
index 0c6db2cb8ba8d63c39ebf6796b6ad213e0a48dc9..a2c808cbcb6b6dae988880d354dc5528b01ea85c 100644 (file)
@@ -21,20 +21,23 @@ fn main() {
     println!("cargo:rustc-cfg=cargobuild");
 
     let target = env::var("TARGET").unwrap();
-    let llvm_config = env::var_os("LLVM_CONFIG").map(PathBuf::from)
-                           .unwrap_or_else(|| {
-        match env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
-            Some(dir) => {
-                let to_test = dir.parent().unwrap().parent().unwrap()
-                                 .join(&target).join("llvm/bin/llvm-config");
-                if Command::new(&to_test).output().is_ok() {
-                    return to_test
-                }
-            }
-            None => {}
-        }
-        PathBuf::from("llvm-config")
-    });
+    let llvm_config = env::var_os("LLVM_CONFIG")
+                          .map(PathBuf::from)
+                          .unwrap_or_else(|| {
+                              if let Some(dir) = env::var_os("CARGO_TARGET_DIR")
+                                      .map(PathBuf::from) {
+                                  let to_test = dir.parent()
+                                                   .unwrap()
+                                                   .parent()
+                                                   .unwrap()
+                                                   .join(&target)
+                                                   .join("llvm/bin/llvm-config");
+                                  if Command::new(&to_test).output().is_ok() {
+                                      return to_test;
+                                  }
+                              }
+                              PathBuf::from("llvm-config")
+                          });
 
     println!("cargo:rerun-if-changed={}", llvm_config.display());
 
@@ -63,20 +66,22 @@ fn main() {
     let host = env::var("HOST").unwrap();
     let is_crossed = target != host;
 
-    let optional_components = ["x86", "arm", "aarch64", "mips", "powerpc",
-                               "pnacl"];
+    let optional_components = ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl"];
 
     // FIXME: surely we don't need all these components, right? Stuff like mcjit
     //        or interpreter the compiler itself never uses.
-    let required_components = &["ipo", "bitreader", "bitwriter", "linker",
-                                "asmparser", "mcjit", "interpreter",
+    let required_components = &["ipo",
+                                "bitreader",
+                                "bitwriter",
+                                "linker",
+                                "asmparser",
+                                "mcjit",
+                                "interpreter",
                                 "instrumentation"];
 
     let components = output(Command::new(&llvm_config).arg("--components"));
     let mut components = components.split_whitespace().collect::<Vec<_>>();
-    components.retain(|c| {
-        optional_components.contains(c) || required_components.contains(c)
-    });
+    components.retain(|c| optional_components.contains(c) || required_components.contains(c));
 
     for component in required_components {
         if !components.contains(component) {
@@ -96,7 +101,7 @@ fn main() {
     for flag in cxxflags.split_whitespace() {
         // Ignore flags like `-m64` when we're doing a cross build
         if is_crossed && flag.starts_with("-m") {
-            continue
+            continue;
         }
         cfg.flag(flag);
     }
@@ -131,7 +136,7 @@ fn main() {
         } else if lib.starts_with("-") {
             &lib[1..]
         } else {
-            continue
+            continue;
         };
 
         // Don't need or want this library, but LLVM's CMake build system
@@ -140,10 +145,14 @@ fn main() {
         // library and it otherwise may just pull in extra dependencies on
         // libedit which we don't want
         if name == "LLVMLineEditor" {
-            continue
+            continue;
         }
 
-        let kind = if name.starts_with("LLVM") {"static"} else {"dylib"};
+        let kind = if name.starts_with("LLVM") {
+            "static"
+        } else {
+            "dylib"
+        };
         println!("cargo:rustc-link-lib={}={}", kind, name);
     }
 
index acb47516150cfbbad518f8ab63cf9834a356c374..44e0156146402778af1269c26e9e339a6a7b792e 100644 (file)
@@ -16,7 +16,7 @@ pub use self::Diagnostic::*;
 use libc::{c_char, c_uint};
 use std::ptr;
 
-use {ValueRef, TwineRef, DebugLocRef, DiagnosticInfoRef};
+use {DebugLocRef, DiagnosticInfoRef, TwineRef, ValueRef};
 
 #[derive(Copy, Clone)]
 pub enum OptimizationDiagnosticKind {
@@ -46,8 +46,9 @@ pub struct OptimizationDiagnostic {
 }
 
 impl OptimizationDiagnostic {
-    unsafe fn unpack(kind: OptimizationDiagnosticKind, di: DiagnosticInfoRef)
-            -> OptimizationDiagnostic {
+    unsafe fn unpack(kind: OptimizationDiagnosticKind,
+                     di: DiagnosticInfoRef)
+                     -> OptimizationDiagnostic {
 
         let mut opt = OptimizationDiagnostic {
             kind: kind,
@@ -58,10 +59,10 @@ impl OptimizationDiagnostic {
         };
 
         super::LLVMUnpackOptimizationDiagnostic(di,
-            &mut opt.pass_name,
-            &mut opt.function,
-            &mut opt.debug_loc,
-            &mut opt.message);
+                                                &mut opt.pass_name,
+                                                &mut opt.function,
+                                                &mut opt.debug_loc,
+                                                &mut opt.message);
 
         opt
     }
@@ -75,8 +76,7 @@ pub struct InlineAsmDiagnostic {
 }
 
 impl InlineAsmDiagnostic {
-    unsafe fn unpack(di: DiagnosticInfoRef)
-            -> InlineAsmDiagnostic {
+    unsafe fn unpack(di: DiagnosticInfoRef) -> InlineAsmDiagnostic {
 
         let mut opt = InlineAsmDiagnostic {
             cookie: 0,
@@ -85,9 +85,9 @@ impl InlineAsmDiagnostic {
         };
 
         super::LLVMUnpackInlineAsmDiagnostic(di,
-            &mut opt.cookie,
-            &mut opt.message,
-            &mut opt.instruction);
+                                             &mut opt.cookie,
+                                             &mut opt.message,
+                                             &mut opt.instruction);
 
         opt
     }
@@ -106,22 +106,25 @@ impl Diagnostic {
         let kind = super::LLVMGetDiagInfoKind(di);
 
         match kind {
-            super::DK_InlineAsm
-                => InlineAsm(InlineAsmDiagnostic::unpack(di)),
+            super::DK_InlineAsm => InlineAsm(InlineAsmDiagnostic::unpack(di)),
 
-            super::DK_OptimizationRemark
-                => Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)),
+            super::DK_OptimizationRemark => {
+                Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di))
+            }
 
-            super::DK_OptimizationRemarkMissed
-                => Optimization(OptimizationDiagnostic::unpack(OptimizationMissed, di)),
+            super::DK_OptimizationRemarkMissed => {
+                Optimization(OptimizationDiagnostic::unpack(OptimizationMissed, di))
+            }
 
-            super::DK_OptimizationRemarkAnalysis
-                => Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysis, di)),
+            super::DK_OptimizationRemarkAnalysis => {
+                Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysis, di))
+            }
 
-            super::DK_OptimizationFailure
-                => Optimization(OptimizationDiagnostic::unpack(OptimizationFailure, di)),
+            super::DK_OptimizationFailure => {
+                Optimization(OptimizationDiagnostic::unpack(OptimizationFailure, di))
+            }
 
-            _ => UnknownDiagnostic(di)
+            _ => UnknownDiagnostic(di),
         }
     }
 }
index ea0d8eae75d750c39cc5ea4872147aad5780fcd5..e757201c8863383a5065420713c7f847dc6770a3 100644 (file)
@@ -54,6 +54,7 @@ pub use self::DiagnosticSeverity::*;
 pub use self::Linkage::*;
 pub use self::DLLStorageClassTypes::*;
 
+use std::str::FromStr;
 use std::ffi::{CString, CStr};
 use std::cell::RefCell;
 use std::slice;
@@ -426,6 +427,20 @@ pub enum ArchiveKind {
     K_COFF,
 }
 
+impl FromStr for ArchiveKind {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "gnu" => Ok(ArchiveKind::K_GNU),
+            "mips64" => Ok(ArchiveKind::K_MIPS64),
+            "bsd" => Ok(ArchiveKind::K_BSD),
+            "coff" => Ok(ArchiveKind::K_COFF),
+            _ => Err(()),
+        }
+    }
+}
+
 /// Represents the different LLVM passes Rust supports
 #[derive(Copy, Clone, PartialEq, Debug)]
 #[repr(C)]
index 697cbe39c67fcb61c69eb02372ec8c866727fe2f..2d3302c2eef3a8078a4c6683a94f546770ed19b0 100644 (file)
@@ -16,6 +16,9 @@ rustc = { path = "../librustc" }
 rustc_back = { path = "../librustc_back" }
 rustc_bitflags = { path = "../librustc_bitflags" }
 rustc_const_math = { path = "../librustc_const_math" }
+rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
 rustc_llvm = { path = "../librustc_llvm" }
 serialize = { path = "../libserialize" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
\ No newline at end of file
index ad0e8e1c37623a9f03de6fabf74e1ba84df2977b..2e8c5a7c23418ba137017befcbd86b4a57613481 100644 (file)
@@ -37,9 +37,9 @@ use middle::region;
 use rustc::ty::subst;
 use rustc::ty::{self, Ty, TyCtxt};
 
-use syntax::{ast, codemap};
-use syntax::ast::NodeIdAssigner;
+use syntax::ast;
 use syntax::ptr::P;
+use syntax_pos;
 
 use std::cell::Cell;
 use std::io::SeekFrom;
@@ -55,13 +55,12 @@ use rustc_serialize::{Encodable, EncoderHelpers};
 
 #[cfg(test)] use std::io::Cursor;
 #[cfg(test)] use syntax::parse;
-#[cfg(test)] use syntax::ast::NodeId;
 #[cfg(test)] use rustc::hir::print as pprust;
 #[cfg(test)] use rustc::hir::lowering::{LoweringContext, DummyResolver};
 
 struct DecodeContext<'a, 'b, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    cdata: &'b cstore::crate_metadata,
+    cdata: &'b cstore::CrateMetadata,
     from_id_range: IdRange,
     to_id_range: IdRange,
     // Cache the last used filemap for translating spans as an optimization.
@@ -115,14 +114,14 @@ impl<'a, 'b, 'c, 'tcx> ast_map::FoldOps for &'a DecodeContext<'b, 'c, 'tcx> {
     fn new_def_id(&self, def_id: DefId) -> DefId {
         self.tr_def_id(def_id)
     }
-    fn new_span(&self, span: codemap::Span) -> codemap::Span {
+    fn new_span(&self, span: syntax_pos::Span) -> syntax_pos::Span {
         self.tr_span(span)
     }
 }
 
 /// Decodes an item from its AST in the cdata's metadata and adds it to the
 /// ast-map.
-pub fn decode_inlined_item<'a, 'tcx>(cdata: &cstore::crate_metadata,
+pub fn decode_inlined_item<'a, 'tcx>(cdata: &cstore::CrateMetadata,
                                      tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                      parent_def_path: ast_map::DefPath,
                                      parent_did: DefId,
@@ -206,7 +205,7 @@ impl<'a, 'b, 'tcx> DecodeContext<'a, 'b, 'tcx> {
 
     /// Translates a `Span` from an extern crate to the corresponding `Span`
     /// within the local crate's codemap.
-    pub fn tr_span(&self, span: codemap::Span) -> codemap::Span {
+    pub fn tr_span(&self, span: syntax_pos::Span) -> syntax_pos::Span {
         decoder::translate_span(self.cdata,
                                 self.tcx.sess.codemap(),
                                 &self.last_filemap_index,
@@ -226,8 +225,8 @@ impl tr for Option<DefId> {
     }
 }
 
-impl tr for codemap::Span {
-    fn tr(&self, dcx: &DecodeContext) -> codemap::Span {
+impl tr for syntax_pos::Span {
+    fn tr(&self, dcx: &DecodeContext) -> syntax_pos::Span {
         dcx.tr_span(*self)
     }
 }
@@ -247,7 +246,7 @@ impl<S:serialize::Encoder> def_id_encoder_helpers for S
 trait def_id_decoder_helpers {
     fn read_def_id(&mut self, dcx: &DecodeContext) -> DefId;
     fn read_def_id_nodcx(&mut self,
-                         cdata: &cstore::crate_metadata) -> DefId;
+                         cdata: &cstore::CrateMetadata) -> DefId;
 }
 
 impl<D:serialize::Decoder> def_id_decoder_helpers for D
@@ -259,7 +258,7 @@ impl<D:serialize::Decoder> def_id_decoder_helpers for D
     }
 
     fn read_def_id_nodcx(&mut self,
-                         cdata: &cstore::crate_metadata)
+                         cdata: &cstore::CrateMetadata)
                          -> DefId {
         let did: DefId = Decodable::decode(self).unwrap();
         decoder::translate_def_id(cdata, did)
@@ -719,7 +718,7 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
 
     debug!("Encoding side tables for id {}", id);
 
-    if let Some(def) = tcx.def_map.borrow().get(&id).map(|d| d.full_def()) {
+    if let Some(def) = tcx.expect_def_or_none(id) {
         rbml_w.tag(c::tag_table_def, |rbml_w| {
             rbml_w.id(id);
             def.encode(rbml_w).unwrap();
@@ -859,17 +858,17 @@ trait rbml_decoder_decoder_helpers<'tcx> {
     // Versions of the type reading functions that don't need the full
     // DecodeContext.
     fn read_ty_nodcx<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                         cdata: &cstore::crate_metadata) -> Ty<'tcx>;
+                         cdata: &cstore::CrateMetadata) -> Ty<'tcx>;
     fn read_tys_nodcx<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                          cdata: &cstore::crate_metadata) -> Vec<Ty<'tcx>>;
+                          cdata: &cstore::CrateMetadata) -> Vec<Ty<'tcx>>;
     fn read_substs_nodcx<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                             cdata: &cstore::crate_metadata)
+                             cdata: &cstore::CrateMetadata)
                              -> subst::Substs<'tcx>;
 }
 
 impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
     fn read_ty_nodcx<'b>(&mut self, tcx: TyCtxt<'b, 'tcx, 'tcx>,
-                         cdata: &cstore::crate_metadata)
+                         cdata: &cstore::CrateMetadata)
                          -> Ty<'tcx> {
         self.read_opaque(|_, doc| {
             Ok(
@@ -880,7 +879,7 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
     }
 
     fn read_tys_nodcx<'b>(&mut self, tcx: TyCtxt<'b, 'tcx, 'tcx>,
-                          cdata: &cstore::crate_metadata) -> Vec<Ty<'tcx>> {
+                          cdata: &cstore::CrateMetadata) -> Vec<Ty<'tcx>> {
         self.read_to_vec(|this| Ok(this.read_ty_nodcx(tcx, cdata)) )
             .unwrap()
             .into_iter()
@@ -888,7 +887,7 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
     }
 
     fn read_substs_nodcx<'b>(&mut self, tcx: TyCtxt<'b, 'tcx, 'tcx>,
-                             cdata: &cstore::crate_metadata)
+                             cdata: &cstore::CrateMetadata)
                              -> subst::Substs<'tcx>
     {
         self.read_opaque(|_, doc| {
@@ -1133,10 +1132,7 @@ fn decode_side_tables(dcx: &DecodeContext,
                 match value {
                     c::tag_table_def => {
                         let def = decode_def(dcx, val_dsr);
-                        dcx.tcx.def_map.borrow_mut().insert(id, def::PathResolution {
-                            base_def: def,
-                            depth: 0
-                        });
+                        dcx.tcx.def_map.borrow_mut().insert(id, def::PathResolution::new(def));
                     }
                     c::tag_table_node_type => {
                         let ty = val_dsr.read_ty(dcx);
@@ -1271,7 +1267,7 @@ fn decode_item_ast(item_doc: rbml::Doc) -> hir::Item {
 
 #[cfg(test)]
 trait FakeExtCtxt {
-    fn call_site(&self) -> codemap::Span;
+    fn call_site(&self) -> syntax_pos::Span;
     fn cfg(&self) -> ast::CrateConfig;
     fn ident_of(&self, st: &str) -> ast::Ident;
     fn name_of(&self, st: &str) -> ast::Name;
@@ -1280,11 +1276,11 @@ trait FakeExtCtxt {
 
 #[cfg(test)]
 impl FakeExtCtxt for parse::ParseSess {
-    fn call_site(&self) -> codemap::Span {
-        codemap::Span {
-            lo: codemap::BytePos(0),
-            hi: codemap::BytePos(0),
-            expn_id: codemap::NO_EXPANSION,
+    fn call_site(&self) -> syntax_pos::Span {
+        syntax_pos::Span {
+            lo: syntax_pos::BytePos(0),
+            hi: syntax_pos::BytePos(0),
+            expn_id: syntax_pos::NO_EXPANSION,
         }
     }
     fn cfg(&self) -> ast::CrateConfig { Vec::new() }
@@ -1297,22 +1293,6 @@ impl FakeExtCtxt for parse::ParseSess {
     fn parse_sess(&self) -> &parse::ParseSess { self }
 }
 
-#[cfg(test)]
-struct FakeNodeIdAssigner;
-
-#[cfg(test)]
-// It should go without saying that this may give unexpected results. Avoid
-// lowering anything which needs new nodes.
-impl NodeIdAssigner for FakeNodeIdAssigner {
-    fn next_node_id(&self) -> NodeId {
-        0
-    }
-
-    fn peek_node_id(&self) -> NodeId {
-        0
-    }
-}
-
 #[cfg(test)]
 fn mk_ctxt() -> parse::ParseSess {
     parse::ParseSess::new()
@@ -1320,9 +1300,8 @@ fn mk_ctxt() -> parse::ParseSess {
 
 #[cfg(test)]
 fn with_testing_context<T, F: FnOnce(&mut LoweringContext) -> T>(f: F) -> T {
-    let assigner = FakeNodeIdAssigner;
     let mut resolver = DummyResolver;
-    let mut lcx = LoweringContext::testing_context(&assigner, &mut resolver);
+    let mut lcx = LoweringContext::testing_context(&mut resolver);
     f(&mut lcx)
 }
 
index b3ca399132bc782b4a48ab4cab1420a4f32c8a28..ff072cce5db9667ccd5cb998ee0cdcb6384cdd78 100644 (file)
@@ -33,7 +33,7 @@ pub const tag_items_data_item_family: usize = 0x24;
 
 pub const tag_items_data_item_type: usize = 0x25;
 
-pub const tag_items_data_item_symbol: usize = 0x26;
+// GAP 0x26
 
 pub const tag_items_data_item_variant: usize = 0x27;
 
index 90f4ebc1a1ebe718cdbc00e852601472553d0749..269e284b22d6cb268d3d5aceca737da1fc90be13 100644 (file)
@@ -16,13 +16,14 @@ use cstore::{self, CStore, CrateSource, MetadataBlob};
 use decoder;
 use loader::{self, CratePaths};
 
+use rustc::hir::def_id::DefIndex;
 use rustc::hir::svh::Svh;
 use rustc::dep_graph::{DepGraph, DepNode};
 use rustc::session::{config, Session};
 use rustc::session::config::PanicStrategy;
 use rustc::session::search_paths::PathKind;
 use rustc::middle::cstore::{CrateStore, validate_crate_name, ExternCrate};
-use rustc::util::nodemap::FnvHashMap;
+use rustc::util::nodemap::{FnvHashMap, FnvHashSet};
 use rustc::hir::map as hir_map;
 
 use std::cell::{RefCell, Cell};
@@ -32,12 +33,13 @@ use std::fs;
 
 use syntax::ast;
 use syntax::abi::Abi;
-use syntax::codemap::{self, Span, mk_sp, Pos};
+use syntax::codemap;
 use syntax::parse;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::parse::token::InternedString;
 use syntax::visit;
+use syntax_pos::{self, Span, mk_sp, Pos};
 use log;
 
 struct LocalCrateReader<'a> {
@@ -56,8 +58,8 @@ pub struct CrateReader<'a> {
     local_crate_name: String,
 }
 
-impl<'a, 'ast> visit::Visitor<'ast> for LocalCrateReader<'a> {
-    fn visit_item(&mut self, a: &'ast ast::Item) {
+impl<'a> visit::Visitor for LocalCrateReader<'a> {
+    fn visit_item(&mut self, a: &ast::Item) {
         self.process_item(a);
         visit::walk_item(self, a);
     }
@@ -129,7 +131,7 @@ struct ExtensionCrate {
 }
 
 enum PMDSource {
-    Registered(Rc<cstore::crate_metadata>),
+    Registered(Rc<cstore::CrateMetadata>),
     Owned(MetadataBlob),
 }
 
@@ -272,7 +274,7 @@ impl<'a> CrateReader<'a> {
                       span: Span,
                       lib: loader::Library,
                       explicitly_linked: bool)
-                      -> (ast::CrateNum, Rc<cstore::crate_metadata>,
+                      -> (ast::CrateNum, Rc<cstore::CrateMetadata>,
                           cstore::CrateSource) {
         self.verify_no_symbol_conflicts(span, &lib.metadata);
 
@@ -295,10 +297,10 @@ impl<'a> CrateReader<'a> {
 
         let loader::Library { dylib, rlib, metadata } = lib;
 
-        let cnum_map = self.resolve_crate_deps(root, metadata.as_slice(), span);
+        let cnum_map = self.resolve_crate_deps(root, metadata.as_slice(), cnum, span);
         let staged_api = self.is_staged_api(metadata.as_slice());
 
-        let cmeta = Rc::new(cstore::crate_metadata {
+        let cmeta = Rc::new(cstore::CrateMetadata {
             name: name.to_string(),
             extern_crate: Cell::new(None),
             index: decoder::load_index(metadata.as_slice()),
@@ -341,7 +343,7 @@ impl<'a> CrateReader<'a> {
                      span: Span,
                      kind: PathKind,
                      explicitly_linked: bool)
-                     -> (ast::CrateNum, Rc<cstore::crate_metadata>, cstore::CrateSource) {
+                     -> (ast::CrateNum, Rc<cstore::CrateMetadata>, cstore::CrateSource) {
         let result = match self.existing_match(name, hash, kind) {
             Some(cnum) => LoadResult::Previous(cnum),
             None => {
@@ -416,8 +418,11 @@ impl<'a> CrateReader<'a> {
 
     fn update_extern_crate(&mut self,
                            cnum: ast::CrateNum,
-                           mut extern_crate: ExternCrate)
+                           mut extern_crate: ExternCrate,
+                           visited: &mut FnvHashSet<(ast::CrateNum, bool)>)
     {
+        if !visited.insert((cnum, extern_crate.direct)) { return }
+
         let cmeta = self.cstore.get_crate_data(cnum);
         let old_extern_crate = cmeta.extern_crate.get();
 
@@ -436,11 +441,10 @@ impl<'a> CrateReader<'a> {
         }
 
         cmeta.extern_crate.set(Some(extern_crate));
-
         // Propagate the extern crate info to dependencies.
         extern_crate.direct = false;
-        for &dep_cnum in cmeta.cnum_map.borrow().values() {
-            self.update_extern_crate(dep_cnum, extern_crate);
+        for &dep_cnum in cmeta.cnum_map.borrow().iter() {
+            self.update_extern_crate(dep_cnum, extern_crate, visited);
         }
     }
 
@@ -448,12 +452,13 @@ impl<'a> CrateReader<'a> {
     fn resolve_crate_deps(&mut self,
                           root: &Option<CratePaths>,
                           cdata: &[u8],
-                          span : Span)
-                          -> cstore::cnum_map {
+                          krate: ast::CrateNum,
+                          span: Span)
+                          -> cstore::CrateNumMap {
         debug!("resolving deps of external crate");
         // The map from crate numbers in the crate we're resolving to local crate
         // numbers
-        decoder::get_crate_deps(cdata).iter().map(|dep| {
+        let map: FnvHashMap<_, _> = decoder::get_crate_deps(cdata).iter().map(|dep| {
             debug!("resolving dep crate {} hash: `{}`", dep.name, dep.hash);
             let (local_cnum, _, _) = self.resolve_crate(root,
                                                         &dep.name,
@@ -463,7 +468,13 @@ impl<'a> CrateReader<'a> {
                                                         PathKind::Dependency,
                                                         dep.explicitly_linked);
             (dep.cnum, local_cnum)
-        }).collect()
+        }).collect();
+
+        let max_cnum = map.values().cloned().max().unwrap_or(0);
+
+        // we map 0 and all other holes in the map to our parent crate. The "additional"
+        // self-dependencies should be harmless.
+        (0..max_cnum+1).map(|cnum| map.get(&cnum).cloned().unwrap_or(krate)).collect()
     }
 
     fn read_extension_crate(&mut self, span: Span, info: &CrateInfo) -> ExtensionCrate {
@@ -591,9 +602,10 @@ impl<'a> CrateReader<'a> {
         macros
     }
 
-    /// Look for a plugin registrar. Returns library path and symbol name.
+    /// Look for a plugin registrar. Returns library path, crate
+    /// SVH and DefIndex of the registrar function.
     pub fn find_plugin_registrar(&mut self, span: Span, name: &str)
-                                 -> Option<(PathBuf, String)> {
+                                 -> Option<(PathBuf, Svh, DefIndex)> {
         let ekrate = self.read_extension_crate(span, &CrateInfo {
              name: name.to_string(),
              ident: name.to_string(),
@@ -611,12 +623,14 @@ impl<'a> CrateReader<'a> {
             span_fatal!(self.sess, span, E0456, "{}", &message[..]);
         }
 
+        let svh = decoder::get_crate_hash(ekrate.metadata.as_slice());
         let registrar =
-            decoder::get_plugin_registrar_fn(ekrate.metadata.as_slice())
-            .map(|id| decoder::get_symbol_from_buf(ekrate.metadata.as_slice(), id));
+            decoder::get_plugin_registrar_fn(ekrate.metadata.as_slice());
 
         match (ekrate.dylib.as_ref(), registrar) {
-            (Some(dylib), Some(reg)) => Some((dylib.to_path_buf(), reg)),
+            (Some(dylib), Some(reg)) => {
+                Some((dylib.to_path_buf(), svh, reg))
+            }
             (None, Some(_)) => {
                 span_err!(self.sess, span, E0457,
                           "plugin `{}` only found in rlib format, but must be available \
@@ -703,7 +717,7 @@ impl<'a> CrateReader<'a> {
         info!("panic runtime not found -- loading {}", name);
 
         let (cnum, data, _) = self.resolve_crate(&None, name, name, None,
-                                                 codemap::DUMMY_SP,
+                                                 syntax_pos::DUMMY_SP,
                                                  PathKind::Crate, false);
 
         // Sanity check the loaded crate to ensure it is indeed a panic runtime
@@ -784,7 +798,7 @@ impl<'a> CrateReader<'a> {
             &self.sess.target.target.options.exe_allocation_crate
         };
         let (cnum, data, _) = self.resolve_crate(&None, name, name, None,
-                                                 codemap::DUMMY_SP,
+                                                 syntax_pos::DUMMY_SP,
                                                  PathKind::Crate, false);
 
         // Sanity check the crate we loaded to ensure that it is indeed an
@@ -802,7 +816,7 @@ impl<'a> CrateReader<'a> {
     fn inject_dependency_if(&self,
                             krate: ast::CrateNum,
                             what: &str,
-                            needs_dep: &Fn(&cstore::crate_metadata) -> bool) {
+                            needs_dep: &Fn(&cstore::CrateMetadata) -> bool) {
         // don't perform this validation if the session has errors, as one of
         // those errors may indicate a circular dependency which could cause
         // this to stack overflow.
@@ -813,7 +827,17 @@ impl<'a> CrateReader<'a> {
         // Before we inject any dependencies, make sure we don't inject a
         // circular dependency by validating that this crate doesn't
         // transitively depend on any crates satisfying `needs_dep`.
-        validate(self, krate, krate, what, needs_dep);
+        for dep in self.cstore.crate_dependencies_in_rpo(krate) {
+            let data = self.cstore.get_crate_data(dep);
+            if needs_dep(&data) {
+                self.sess.err(&format!("the crate `{}` cannot depend \
+                                        on a crate that needs {}, but \
+                                        it depends on `{}`",
+                                       self.cstore.get_crate_data(krate).name(),
+                                       what,
+                                       data.name()));
+            }
+        }
 
         // All crates satisfying `needs_dep` do not explicitly depend on the
         // crate provided for this compile, but in order for this compilation to
@@ -825,32 +849,8 @@ impl<'a> CrateReader<'a> {
             }
 
             info!("injecting a dep from {} to {}", cnum, krate);
-            let mut cnum_map = data.cnum_map.borrow_mut();
-            let remote_cnum = cnum_map.len() + 1;
-            let prev = cnum_map.insert(remote_cnum as ast::CrateNum, krate);
-            assert!(prev.is_none());
+            data.cnum_map.borrow_mut().push(krate);
         });
-
-        fn validate(me: &CrateReader,
-                    krate: ast::CrateNum,
-                    root: ast::CrateNum,
-                    what: &str,
-                    needs_dep: &Fn(&cstore::crate_metadata) -> bool) {
-            let data = me.cstore.get_crate_data(krate);
-            if needs_dep(&data) {
-                let krate_name = data.name();
-                let data = me.cstore.get_crate_data(root);
-                let root_name = data.name();
-                me.sess.err(&format!("the crate `{}` cannot depend \
-                                      on a crate that needs {}, but \
-                                      it depends on `{}`", root_name, what,
-                                      krate_name));
-            }
-
-            for (_, &dep) in data.cnum_map.borrow().iter() {
-                validate(me, dep, root, what, needs_dep);
-            }
-        }
     }
 }
 
@@ -906,29 +906,27 @@ impl<'a> LocalCrateReader<'a> {
                     return;
                 }
 
-                match self.creader.extract_crate_info(i) {
-                    Some(info) => {
-                        let (cnum, _, _) = self.creader.resolve_crate(&None,
-                                                                      &info.ident,
-                                                                      &info.name,
-                                                                      None,
-                                                                      i.span,
-                                                                      PathKind::Crate,
-                                                                      true);
-
-                        let def_id = self.definitions.opt_local_def_id(i.id).unwrap();
-                        let len = self.definitions.def_path(def_id.index).data.len();
-
-                        self.creader.update_extern_crate(cnum,
-                                                         ExternCrate {
-                                                             def_id: def_id,
-                                                             span: i.span,
-                                                             direct: true,
-                                                             path_len: len,
-                                                         });
-                        self.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
-                    }
-                    None => ()
+                if let Some(info) = self.creader.extract_crate_info(i) {
+                    let (cnum, _, _) = self.creader.resolve_crate(&None,
+                                                                  &info.ident,
+                                                                  &info.name,
+                                                                  None,
+                                                                  i.span,
+                                                                  PathKind::Crate,
+                                                                  true);
+
+                    let def_id = self.definitions.opt_local_def_id(i.id).unwrap();
+                    let len = self.definitions.def_path(def_id.index).data.len();
+
+                    self.creader.update_extern_crate(cnum,
+                                                     ExternCrate {
+                                                         def_id: def_id,
+                                                         span: i.span,
+                                                         direct: true,
+                                                         path_len: len,
+                                                     },
+                                                     &mut FnvHashSet());
+                    self.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
                 }
             }
             ast::ItemKind::ForeignMod(ref fm) => self.process_foreign_mod(i, fm),
@@ -1056,8 +1054,9 @@ pub fn import_codemap(local_codemap: &codemap::CodeMap,
             None => {
                 // We can't reuse an existing FileMap, so allocate a new one
                 // containing the information we need.
-                let codemap::FileMap {
+                let syntax_pos::FileMap {
                     name,
+                    abs_path,
                     start_pos,
                     end_pos,
                     lines,
@@ -1082,6 +1081,7 @@ pub fn import_codemap(local_codemap: &codemap::CodeMap,
                 }
 
                 let local_version = local_codemap.new_imported_filemap(name,
+                                                                       abs_path,
                                                                        source_length,
                                                                        lines,
                                                                        multibyte_chars);
@@ -1096,8 +1096,8 @@ pub fn import_codemap(local_codemap: &codemap::CodeMap,
 
     return imported_filemaps;
 
-    fn are_equal_modulo_startpos(fm1: &codemap::FileMap,
-                                 fm2: &codemap::FileMap)
+    fn are_equal_modulo_startpos(fm1: &syntax_pos::FileMap,
+                                 fm2: &syntax_pos::FileMap)
                                  -> bool {
         if fm1.name != fm2.name {
             return false;
index d1f6f7e1ff223781fbde8c3096074fa8214f3267..95e3c53ecb40245e420bff18e81a92d8c9bb6b2e 100644 (file)
@@ -26,7 +26,7 @@ use rustc::hir::map as hir_map;
 use rustc::hir::map::DefKey;
 use rustc::mir::repr::Mir;
 use rustc::mir::mir_map::MirMap;
-use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap};
+use rustc::util::nodemap::{FnvHashMap, NodeSet, DefIdMap};
 use rustc::session::config::PanicStrategy;
 
 use std::cell::RefCell;
@@ -116,13 +116,6 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::get_item_attrs(&cdata, def_id.index)
     }
 
-    fn item_symbol(&self, def: DefId) -> String
-    {
-        self.dep_graph.read(DepNode::MetaData(def));
-        let cdata = self.get_crate_data(def.krate);
-        decoder::get_symbol(&cdata, def.index)
-    }
-
     fn trait_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::TraitDef<'tcx>
     {
         self.dep_graph.read(DepNode::MetaData(def));
@@ -150,6 +143,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::get_item_name(&self.intr, &cdata, def.index)
     }
 
+    fn opt_item_name(&self, def: DefId) -> Option<ast::Name> {
+        self.dep_graph.read(DepNode::MetaData(def));
+        let cdata = self.get_crate_data(def.krate);
+        decoder::maybe_get_item_name(&self.intr, &cdata, def.index)
+    }
 
     fn inherent_implementations_for_type(&self, def_id: DefId) -> Vec<DefId>
     {
@@ -285,6 +283,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::is_extern_item(&cdata, did.index, tcx)
     }
 
+    fn is_foreign_item(&self, did: DefId) -> bool {
+        let cdata = self.get_crate_data(did.krate);
+        decoder::is_foreign_item(&cdata, did.index)
+    }
+
     fn is_static_method(&self, def: DefId) -> bool
     {
         self.dep_graph.read(DepNode::MetaData(def));
@@ -565,7 +568,6 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
 
     fn encode_metadata<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
                            reexports: &def::ExportMap,
-                           item_symbols: &RefCell<NodeMap<String>>,
                            link_meta: &LinkMeta,
                            reachable: &NodeSet,
                            mir_map: &MirMap<'tcx>,
@@ -575,7 +577,6 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
             diag: tcx.sess.diagnostic(),
             tcx: tcx,
             reexports: reexports,
-            item_symbols: item_symbols,
             link_meta: link_meta,
             cstore: self,
             reachable: reachable,
index 5464f7e295c8baa95d08304a03ec2d828d8989b3..774d0f7ea188608820c21df7a014c87ecdac6cfb 100644 (file)
@@ -27,6 +27,7 @@ use rustc::hir::map::DefKey;
 use rustc::hir::svh::Svh;
 use rustc::middle::cstore::{ExternCrate};
 use rustc::session::config::PanicStrategy;
+use rustc_data_structures::indexed_vec::IndexVec;
 use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap};
 
 use std::cell::{RefCell, Ref, Cell};
@@ -37,6 +38,7 @@ use syntax::ast;
 use syntax::attr;
 use syntax::codemap;
 use syntax::parse::token::IdentInterner;
+use syntax_pos;
 
 pub use middle::cstore::{NativeLibraryKind, LinkagePreference};
 pub use middle::cstore::{NativeStatic, NativeFramework, NativeUnknown};
@@ -46,25 +48,25 @@ pub use middle::cstore::{CrateSource, LinkMeta};
 // local crate numbers (as generated during this session). Each external
 // crate may refer to types in other external crates, and each has their
 // own crate numbers.
-pub type cnum_map = FnvHashMap<ast::CrateNum, ast::CrateNum>;
+pub type CrateNumMap = IndexVec<ast::CrateNum, ast::CrateNum>;
 
 pub enum MetadataBlob {
     MetadataVec(Bytes),
     MetadataArchive(loader::ArchiveMetadata),
 }
 
-/// Holds information about a codemap::FileMap imported from another crate.
+/// Holds information about a syntax_pos::FileMap imported from another crate.
 /// See creader::import_codemap() for more information.
 pub struct ImportedFileMap {
     /// This FileMap's byte-offset within the codemap of its original crate
-    pub original_start_pos: codemap::BytePos,
+    pub original_start_pos: syntax_pos::BytePos,
     /// The end of this FileMap within the codemap of its original crate
-    pub original_end_pos: codemap::BytePos,
+    pub original_end_pos: syntax_pos::BytePos,
     /// The imported FileMap's representation within the local codemap
-    pub translated_filemap: Rc<codemap::FileMap>
+    pub translated_filemap: Rc<syntax_pos::FileMap>
 }
 
-pub struct crate_metadata {
+pub struct CrateMetadata {
     pub name: String,
 
     /// Information about the extern crate that caused this crate to
@@ -73,7 +75,7 @@ pub struct crate_metadata {
     pub extern_crate: Cell<Option<ExternCrate>>,
 
     pub data: MetadataBlob,
-    pub cnum_map: RefCell<cnum_map>,
+    pub cnum_map: RefCell<CrateNumMap>,
     pub cnum: ast::CrateNum,
     pub codemap_import_info: RefCell<Vec<ImportedFileMap>>,
     pub staged_api: bool,
@@ -97,7 +99,7 @@ pub struct crate_metadata {
 
 pub struct CStore {
     pub dep_graph: DepGraph,
-    metas: RefCell<FnvHashMap<ast::CrateNum, Rc<crate_metadata>>>,
+    metas: RefCell<FnvHashMap<ast::CrateNum, Rc<CrateMetadata>>>,
     /// Map from NodeId's of local extern crate statements to crate numbers
     extern_mod_crate_map: RefCell<NodeMap<ast::CrateNum>>,
     used_crate_sources: RefCell<Vec<CrateSource>>,
@@ -128,7 +130,7 @@ impl CStore {
         self.metas.borrow().len() as ast::CrateNum + 1
     }
 
-    pub fn get_crate_data(&self, cnum: ast::CrateNum) -> Rc<crate_metadata> {
+    pub fn get_crate_data(&self, cnum: ast::CrateNum) -> Rc<CrateMetadata> {
         self.metas.borrow().get(&cnum).unwrap().clone()
     }
 
@@ -137,12 +139,12 @@ impl CStore {
         decoder::get_crate_hash(cdata.data())
     }
 
-    pub fn set_crate_data(&self, cnum: ast::CrateNum, data: Rc<crate_metadata>) {
+    pub fn set_crate_data(&self, cnum: ast::CrateNum, data: Rc<CrateMetadata>) {
         self.metas.borrow_mut().insert(cnum, data);
     }
 
     pub fn iter_crate_data<I>(&self, mut i: I) where
-        I: FnMut(ast::CrateNum, &Rc<crate_metadata>),
+        I: FnMut(ast::CrateNum, &Rc<CrateMetadata>),
     {
         for (&k, v) in self.metas.borrow().iter() {
             i(k, v);
@@ -151,7 +153,7 @@ impl CStore {
 
     /// Like `iter_crate_data`, but passes source paths (if available) as well.
     pub fn iter_crate_data_origins<I>(&self, mut i: I) where
-        I: FnMut(ast::CrateNum, &crate_metadata, Option<CrateSource>),
+        I: FnMut(ast::CrateNum, &CrateMetadata, Option<CrateSource>),
     {
         for (&k, v) in self.metas.borrow().iter() {
             let origin = self.opt_used_crate_source(k);
@@ -182,6 +184,30 @@ impl CStore {
         self.statically_included_foreign_items.borrow_mut().clear();
     }
 
+    pub fn crate_dependencies_in_rpo(&self, krate: ast::CrateNum) -> Vec<ast::CrateNum>
+    {
+        let mut ordering = Vec::new();
+        self.push_dependencies_in_postorder(&mut ordering, krate);
+        ordering.reverse();
+        ordering
+    }
+
+    pub fn push_dependencies_in_postorder(&self,
+                                          ordering: &mut Vec<ast::CrateNum>,
+                                          krate: ast::CrateNum)
+    {
+        if ordering.contains(&krate) { return }
+
+        let data = self.get_crate_data(krate);
+        for &dep in data.cnum_map.borrow().iter() {
+            if dep != krate {
+                self.push_dependencies_in_postorder(ordering, dep);
+            }
+        }
+
+        ordering.push(krate);
+    }
+
     // This method is used when generating the command line to pass through to
     // system linker. The linker expects undefined symbols on the left of the
     // command line to be defined in libraries on the right, not the other way
@@ -194,17 +220,8 @@ impl CStore {
     pub fn do_get_used_crates(&self, prefer: LinkagePreference)
                               -> Vec<(ast::CrateNum, Option<PathBuf>)> {
         let mut ordering = Vec::new();
-        fn visit(cstore: &CStore, cnum: ast::CrateNum,
-                 ordering: &mut Vec<ast::CrateNum>) {
-            if ordering.contains(&cnum) { return }
-            let meta = cstore.get_crate_data(cnum);
-            for (_, &dep) in meta.cnum_map.borrow().iter() {
-                visit(cstore, dep, ordering);
-            }
-            ordering.push(cnum);
-        }
         for (&num, _) in self.metas.borrow().iter() {
-            visit(self, num, &mut ordering);
+            self.push_dependencies_in_postorder(&mut ordering, num);
         }
         info!("topological ordering: {:?}", ordering);
         ordering.reverse();
@@ -264,7 +281,7 @@ impl CStore {
     }
 }
 
-impl crate_metadata {
+impl CrateMetadata {
     pub fn data<'a>(&'a self) -> &'a [u8] { self.data.as_slice() }
     pub fn name(&self) -> &str { decoder::get_crate_name(self.data()) }
     pub fn hash(&self) -> Svh { decoder::get_crate_hash(self.data()) }
index b6f35074b7dc701cf7a8f0d79339311561e84e57..eada2a9cd7a63332f6b30aeb10712a785d047f3e 100644 (file)
@@ -15,7 +15,7 @@
 use self::Family::*;
 
 use astencode::decode_inlined_item;
-use cstore::{self, crate_metadata};
+use cstore::{self, CrateMetadata};
 use common::*;
 use def_key;
 use encoder::def_to_u64;
@@ -30,7 +30,7 @@ use rustc::util::nodemap::FnvHashMap;
 use rustc::hir;
 use rustc::session::config::PanicStrategy;
 
-use middle::cstore::{LOCAL_CRATE, FoundAst, InlinedItem, LinkagePreference};
+use middle::cstore::{FoundAst, InlinedItem, LinkagePreference};
 use middle::cstore::{DefLike, DlDef, DlField, DlImpl, tls};
 use rustc::hir::def::Def;
 use rustc::hir::def_id::{DefId, DefIndex};
@@ -56,14 +56,14 @@ use syntax::attr;
 use syntax::parse::token::{self, IdentInterner};
 use syntax::ast;
 use syntax::abi::Abi;
-use syntax::codemap::{self, Span, BytePos, NO_EXPANSION};
+use syntax::codemap;
 use syntax::print::pprust;
 use syntax::ptr::P;
+use syntax_pos::{self, Span, BytePos, NO_EXPANSION};
 
+pub type Cmd<'a> = &'a CrateMetadata;
 
-pub type Cmd<'a> = &'a crate_metadata;
-
-impl crate_metadata {
+impl CrateMetadata {
     fn get_item(&self, item_id: DefIndex) -> Option<rbml::Doc> {
         self.index.lookup_item(self.data(), item_id).map(|pos| {
             reader::doc_at(self.data(), pos as usize).unwrap().doc
@@ -213,10 +213,6 @@ fn item_sort(item: rbml::Doc) -> Option<char> {
     })
 }
 
-fn item_symbol(item: rbml::Doc) -> String {
-    reader::get_doc(item, tag_items_data_item_symbol).as_str().to_string()
-}
-
 fn untranslated_def_id(d: rbml::Doc) -> DefId {
     let id = reader::doc_as_u64(d);
     let index = DefIndex::new((id & 0xFFFF_FFFF) as usize);
@@ -289,12 +285,17 @@ fn item_trait_ref<'a, 'tcx>(doc: rbml::Doc, tcx: TyCtxt<'a, 'tcx, 'tcx>, cdata:
 }
 
 fn item_name(intr: &IdentInterner, item: rbml::Doc) -> ast::Name {
-    let name = reader::get_doc(item, tag_paths_data_name);
-    let string = name.as_str_slice();
-    match intr.find(string) {
-        None => token::intern(string),
-        Some(val) => val,
-    }
+    maybe_item_name(intr, item).expect("no item in item_name")
+}
+
+fn maybe_item_name(intr: &IdentInterner, item: rbml::Doc) -> Option<ast::Name> {
+    reader::maybe_get_doc(item, tag_paths_data_name).map(|name| {
+        let string = name.as_str_slice();
+        match intr.find(string) {
+            None => token::intern(string),
+            Some(val) => val,
+        }
+    })
 }
 
 fn family_to_variant_kind<'tcx>(family: Family) -> Option<ty::VariantKind> {
@@ -640,18 +641,6 @@ pub fn get_impl_trait<'a, 'tcx>(cdata: Cmd,
     }
 }
 
-pub fn get_symbol(cdata: Cmd, id: DefIndex) -> String {
-    return item_symbol(cdata.lookup_item(id));
-}
-
-/// If you have a crate_metadata, call get_symbol instead
-pub fn get_symbol_from_buf(data: &[u8], id: DefIndex) -> String {
-    let index = load_index(data);
-    let pos = index.lookup_item(data, id).unwrap();
-    let doc = reader::doc_at(data, pos as usize).unwrap().doc;
-    item_symbol(doc)
-}
-
 /// Iterates over the language items in the given crate.
 pub fn each_lang_item<F>(cdata: Cmd, mut f: F) -> bool where
     F: FnMut(DefIndex, usize) -> bool,
@@ -674,7 +663,7 @@ fn each_child_of_item_or_crate<F, G>(intr: Rc<IdentInterner>,
                                      mut get_crate_data: G,
                                      mut callback: F) where
     F: FnMut(DefLike, ast::Name, ty::Visibility),
-    G: FnMut(ast::CrateNum) -> Rc<crate_metadata>,
+    G: FnMut(ast::CrateNum) -> Rc<CrateMetadata>,
 {
     // Iterate over all children.
     for child_info_doc in reader::tagged_docs(item_doc, tag_mod_child) {
@@ -693,15 +682,12 @@ fn each_child_of_item_or_crate<F, G>(intr: Rc<IdentInterner>,
         };
 
         // Get the item.
-        match crate_data.get_item(child_def_id.index) {
-            None => {}
-            Some(child_item_doc) => {
-                // Hand off the item to the callback.
-                let child_name = item_name(&intr, child_item_doc);
-                let def_like = item_to_def_like(crate_data, child_item_doc, child_def_id);
-                let visibility = item_visibility(child_item_doc);
-                callback(def_like, child_name, visibility);
-            }
+        if let Some(child_item_doc) = crate_data.get_item(child_def_id.index) {
+            // Hand off the item to the callback.
+            let child_name = item_name(&intr, child_item_doc);
+            let def_like = item_to_def_like(crate_data, child_item_doc, child_def_id);
+            let visibility = item_visibility(child_item_doc);
+            callback(def_like, child_name, visibility);
         }
     }
 
@@ -769,7 +755,7 @@ pub fn each_child_of_item<F, G>(intr: Rc<IdentInterner>,
                                get_crate_data: G,
                                callback: F) where
     F: FnMut(DefLike, ast::Name, ty::Visibility),
-    G: FnMut(ast::CrateNum) -> Rc<crate_metadata>,
+    G: FnMut(ast::CrateNum) -> Rc<CrateMetadata>,
 {
     // Find the item.
     let item_doc = match cdata.get_item(id) {
@@ -790,7 +776,7 @@ pub fn each_top_level_item_of_crate<F, G>(intr: Rc<IdentInterner>,
                                           get_crate_data: G,
                                           callback: F) where
     F: FnMut(DefLike, ast::Name, ty::Visibility),
-    G: FnMut(ast::CrateNum) -> Rc<crate_metadata>,
+    G: FnMut(ast::CrateNum) -> Rc<CrateMetadata>,
 {
     let root_doc = rbml::Doc::new(cdata.data());
     let misc_info_doc = reader::get_doc(root_doc, tag_misc_info);
@@ -808,6 +794,11 @@ pub fn get_item_name(intr: &IdentInterner, cdata: Cmd, id: DefIndex) -> ast::Nam
     item_name(intr, cdata.lookup_item(id))
 }
 
+pub fn maybe_get_item_name(intr: &IdentInterner, cdata: Cmd, id: DefIndex)
+                         -> Option<ast::Name> {
+    maybe_item_name(intr, cdata.lookup_item(id))
+}
+
 pub fn maybe_get_item_ast<'a, 'tcx>(cdata: Cmd, tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefIndex)
                                     -> FoundAst<'tcx> {
     debug!("Looking up item: {:?}", id);
@@ -1236,7 +1227,7 @@ fn get_attributes(md: rbml::Doc) -> Vec<ast::Attribute> {
                         value: meta_item,
                         is_sugared_doc: is_sugared_doc,
                     },
-                    span: codemap::DUMMY_SP
+                    span: syntax_pos::DUMMY_SP
                 }
             }).collect()
         },
@@ -1354,25 +1345,16 @@ pub fn translate_def_id(cdata: Cmd, did: DefId) -> DefId {
         return DefId { krate: cdata.cnum, index: did.index };
     }
 
-    match cdata.cnum_map.borrow().get(&did.krate) {
-        Some(&n) => {
-            DefId {
-                krate: n,
-                index: did.index,
-            }
-        }
-        None => bug!("didn't find a crate in the cnum_map")
+    DefId {
+        krate: cdata.cnum_map.borrow()[did.krate],
+        index: did.index
     }
 }
 
 // Translate a DefId from the current compilation environment to a DefId
 // for an external crate.
 fn reverse_translate_def_id(cdata: Cmd, did: DefId) -> Option<DefId> {
-    if did.krate == cdata.cnum {
-        return Some(DefId { krate: LOCAL_CRATE, index: did.index });
-    }
-
-    for (&local, &global) in cdata.cnum_map.borrow().iter() {
+    for (local, &global) in cdata.cnum_map.borrow().iter_enumerated() {
         if global == did.krate {
             return Some(DefId { krate: local, index: did.index });
         }
@@ -1386,8 +1368,8 @@ fn reverse_translate_def_id(cdata: Cmd, did: DefId) -> Option<DefId> {
 pub fn translate_span(cdata: Cmd,
                       codemap: &codemap::CodeMap,
                       last_filemap_index_hint: &Cell<usize>,
-                      span: codemap::Span)
-                      -> codemap::Span {
+                      span: syntax_pos::Span)
+                      -> syntax_pos::Span {
     let span = if span.lo > span.hi {
         // Currently macro expansion sometimes produces invalid Span values
         // where lo > hi. In order not to crash the compiler when trying to
@@ -1396,7 +1378,7 @@ pub fn translate_span(cdata: Cmd,
         // least some of the time).
         // This workaround is only necessary as long as macro expansion is
         // not fixed. FIXME(#23480)
-        codemap::mk_sp(span.lo, span.lo)
+        syntax_pos::mk_sp(span.lo, span.lo)
     } else {
         span
     };
@@ -1436,7 +1418,7 @@ pub fn translate_span(cdata: Cmd,
     let hi = (span.hi - filemap.original_start_pos) +
               filemap.translated_filemap.start_pos;
 
-    codemap::mk_sp(lo, hi)
+    syntax_pos::mk_sp(lo, hi)
 }
 
 pub fn each_inherent_implementation_for_type<F>(cdata: Cmd,
@@ -1551,10 +1533,7 @@ pub fn get_dylib_dependency_formats(cdata: Cmd)
         let cnum = spec.split(':').nth(0).unwrap();
         let link = spec.split(':').nth(1).unwrap();
         let cnum: ast::CrateNum = cnum.parse().unwrap();
-        let cnum = match cdata.cnum_map.borrow().get(&cnum) {
-            Some(&n) => n,
-            None => bug!("didn't find a crate in the cnum_map")
-        };
+        let cnum = cdata.cnum_map.borrow()[cnum];
         result.push((cnum, if link == "d" {
             LinkagePreference::RequireDynamic
         } else {
@@ -1642,6 +1621,16 @@ pub fn is_extern_item<'a, 'tcx>(cdata: Cmd,
     }
 }
 
+pub fn is_foreign_item(cdata: Cmd, id: DefIndex) -> bool {
+    let item_doc = cdata.lookup_item(id);
+    let parent_item_id = match item_parent_item(cdata, item_doc) {
+        None => return false,
+        Some(item_id) => item_id,
+    };
+    let parent_item_doc = cdata.lookup_item(parent_item_id.index);
+    item_family(parent_item_doc) == ForeignMod
+}
+
 pub fn is_impl(cdata: Cmd, id: DefIndex) -> bool {
     let item_doc = cdata.lookup_item(id);
     match item_family(item_doc) {
@@ -1668,31 +1657,12 @@ fn doc_generics<'a, 'tcx>(base_doc: rbml::Doc,
     }
 
     let mut regions = subst::VecPerParamSpace::empty();
-    for rp_doc in reader::tagged_docs(doc, tag_region_param_def) {
-        let ident_str_doc = reader::get_doc(rp_doc,
-                                            tag_region_param_def_ident);
-        let name = item_name(&token::get_ident_interner(), ident_str_doc);
-        let def_id_doc = reader::get_doc(rp_doc,
-                                         tag_region_param_def_def_id);
-        let def_id = translated_def_id(cdata, def_id_doc);
-
-        let doc = reader::get_doc(rp_doc, tag_region_param_def_space);
-        let space = subst::ParamSpace::from_uint(reader::doc_as_u64(doc) as usize);
-
-        let doc = reader::get_doc(rp_doc, tag_region_param_def_index);
-        let index = reader::doc_as_u64(doc) as u32;
-
-        let bounds = reader::tagged_docs(rp_doc, tag_items_data_region).map(|p| {
+    for p in reader::tagged_docs(doc, tag_region_param_def) {
+        let bd =
             TyDecoder::with_doc(tcx, cdata.cnum, p,
                                 &mut |did| translate_def_id(cdata, did))
-            .parse_region()
-        }).collect();
-
-        regions.push(space, ty::RegionParameterDef { name: name,
-                                                     def_id: def_id,
-                                                     space: space,
-                                                     index: index,
-                                                     bounds: bounds });
+            .parse_region_param_def();
+        regions.push(bd.space, bd);
     }
 
     ty::Generics { types: types, regions: regions }
@@ -1748,7 +1718,7 @@ pub fn is_default_impl(cdata: Cmd, impl_id: DefIndex) -> bool {
     item_family(impl_doc) == Family::DefaultImpl
 }
 
-pub fn get_imported_filemaps(metadata: &[u8]) -> Vec<codemap::FileMap> {
+pub fn get_imported_filemaps(metadata: &[u8]) -> Vec<syntax_pos::FileMap> {
     let crate_doc = rbml::Doc::new(metadata);
     let cm_doc = reader::get_doc(crate_doc, tag_codemap);
 
index e862dbb173020fb0bfd3a000d217a54f11731d49..b6f49569958d68189effffc864b47396c1029410 100644 (file)
@@ -34,7 +34,7 @@ use rustc::ty::util::IntTypeExt;
 use rustc::hir::svh::Svh;
 use rustc::mir::mir_map::MirMap;
 use rustc::session::config::{self, PanicStrategy};
-use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet};
+use rustc::util::nodemap::{FnvHashMap, NodeSet};
 
 use rustc_serialize::Encodable;
 use std::cell::RefCell;
@@ -44,10 +44,10 @@ use std::rc::Rc;
 use std::u32;
 use syntax::abi::Abi;
 use syntax::ast::{self, NodeId, Name, CRATE_NODE_ID, CrateNum};
-use syntax::codemap::BytePos;
 use syntax::attr;
-use syntax::errors::Handler;
+use errors::Handler;
 use syntax;
+use syntax_pos::BytePos;
 use rbml::writer::Encoder;
 
 use rustc::hir::{self, PatKind};
@@ -59,7 +59,6 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
     pub diag: &'a Handler,
     pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
     pub reexports: &'a def::ExportMap,
-    pub item_symbols: &'a RefCell<NodeMap<String>>,
     pub link_meta: &'a LinkMeta,
     pub cstore: &'a cstore::CStore,
     pub type_abbrevs: tyencode::abbrev_map<'tcx>,
@@ -204,29 +203,6 @@ fn encode_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     rbml_w.end_tag();
 }
 
-fn encode_region(ecx: &EncodeContext,
-                 rbml_w: &mut Encoder,
-                 r: ty::Region) {
-    rbml_w.start_tag(tag_items_data_region);
-    tyencode::enc_region(rbml_w.writer, &ecx.ty_str_ctxt(), r);
-    rbml_w.mark_stable_position();
-    rbml_w.end_tag();
-}
-
-fn encode_symbol(ecx: &EncodeContext,
-                 rbml_w: &mut Encoder,
-                 id: NodeId) {
-    match ecx.item_symbols.borrow().get(&id) {
-        Some(x) => {
-            debug!("encode_symbol(id={}, str={})", id, *x);
-            rbml_w.wr_tagged_str(tag_items_data_item_symbol, x);
-        }
-        None => {
-            bug!("encode_symbol: id not found {}", id);
-        }
-    }
-}
-
 fn encode_disr_val(_: &EncodeContext,
                    rbml_w: &mut Encoder,
                    disr_val: ty::Disr) {
@@ -518,10 +494,6 @@ fn encode_info_for_struct_ctor<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_name(rbml_w, name);
     encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(struct_id));
 
-    if ecx.item_symbols.borrow().contains_key(&ctor_id) {
-        encode_symbol(ecx, rbml_w, ctor_id);
-    }
-
     let stab = ecx.tcx.lookup_stability(ecx.tcx.map.local_def_id(ctor_id));
     let depr= ecx.tcx.lookup_deprecation(ecx.tcx.map.local_def_id(ctor_id));
     encode_stability(rbml_w, stab);
@@ -554,24 +526,8 @@ fn encode_generics<'a, 'tcx>(rbml_w: &mut Encoder,
     // Region parameters
     for param in &generics.regions {
         rbml_w.start_tag(tag_region_param_def);
-
-        rbml_w.start_tag(tag_region_param_def_ident);
-        encode_name(rbml_w, param.name);
-        rbml_w.end_tag();
-
-        rbml_w.wr_tagged_u64(tag_region_param_def_def_id,
-                             def_to_u64(param.def_id));
-
-        rbml_w.wr_tagged_u64(tag_region_param_def_space,
-                             param.space.to_uint() as u64);
-
-        rbml_w.wr_tagged_u64(tag_region_param_def_index,
-                             param.index as u64);
-
-        for &bound_region in &param.bounds {
-            encode_region(ecx, rbml_w, bound_region);
-        }
-
+        tyencode::enc_region_param_def(rbml_w.writer, &ecx.ty_str_ctxt(), param);
+        rbml_w.mark_stable_position();
         rbml_w.end_tag();
     }
 
@@ -710,10 +666,6 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
             }
             encode_constness(rbml_w, sig.constness);
             encode_defaultness(rbml_w, impl_item.defaultness);
-            if !any_types {
-                let m_id = ecx.local_id(m.def_id);
-                encode_symbol(ecx, rbml_w, m_id);
-            }
             encode_method_argument_names(rbml_w, &sig.decl);
         }
     }
@@ -767,7 +719,7 @@ fn encode_method_argument_names(rbml_w: &mut Encoder,
     rbml_w.start_tag(tag_method_argument_names);
     for arg in &decl.inputs {
         let tag = tag_method_argument_name;
-        if let PatKind::Ident(_, ref path1, _) = arg.pat.node {
+        if let PatKind::Binding(_, ref path1, _) = arg.pat.node {
             let name = path1.node.as_str();
             rbml_w.wr_tagged_bytes(tag, name.as_bytes());
         } else {
@@ -894,7 +846,6 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
             encode_family(rbml_w, 'c');
         }
         encode_bounds_and_type_for_item(rbml_w, ecx, index, item.id);
-        encode_symbol(ecx, rbml_w, item.id);
         encode_name(rbml_w, item.name);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
@@ -931,9 +882,6 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
             encode_inlined_item(ecx, rbml_w, InlinedItemRef::Item(item));
             encode_mir(ecx, rbml_w, item.id);
         }
-        if tps_len == 0 {
-            encode_symbol(ecx, rbml_w, item.id);
-        }
         encode_constness(rbml_w, constness);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
@@ -1354,6 +1302,8 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     let _task = index.record(def_id, rbml_w);
     rbml_w.start_tag(tag_items_data_item);
     encode_def_id_and_key(ecx, rbml_w, def_id);
+    let parent_id = ecx.tcx.map.get_parent(nitem.id);
+    encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(parent_id));
     encode_visibility(rbml_w, &nitem.vis);
     match nitem.node {
       hir::ForeignItemFn(ref fndecl, _) => {
@@ -1363,8 +1313,6 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
             encode_inlined_item(ecx, rbml_w, InlinedItemRef::Foreign(nitem));
             encode_mir(ecx, rbml_w, nitem.id);
-        } else {
-            encode_symbol(ecx, rbml_w, nitem.id);
         }
         encode_attributes(rbml_w, &nitem.attrs);
         let stab = ecx.tcx.lookup_stability(ecx.tcx.map.local_def_id(nitem.id));
@@ -1385,7 +1333,6 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         let depr = ecx.tcx.lookup_deprecation(ecx.tcx.map.local_def_id(nitem.id));
         encode_stability(rbml_w, stab);
         encode_deprecation(rbml_w, depr);
-        encode_symbol(ecx, rbml_w, nitem.id);
         encode_name(rbml_w, nitem.name);
       }
     }
@@ -1404,6 +1351,7 @@ fn my_visit_expr(expr: &hir::Expr,
 
             rbml_w.start_tag(tag_items_data_item);
             encode_def_id_and_key(ecx, rbml_w, def_id);
+            encode_name(rbml_w, syntax::parse::token::intern("<closure>"));
 
             rbml_w.start_tag(tag_items_closure_ty);
             write_closure_type(ecx, rbml_w, &ecx.tcx.tables.borrow().closure_tys[&def_id]);
@@ -1558,7 +1506,7 @@ fn encode_polarity(rbml_w: &mut Encoder, polarity: hir::ImplPolarity) {
 
 fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) {
     fn get_ordered_deps(cstore: &cstore::CStore)
-                        -> Vec<(CrateNum, Rc<cstore::crate_metadata>)> {
+                        -> Vec<(CrateNum, Rc<cstore::CrateMetadata>)> {
         // Pull the cnums and name,vers,hash out of cstore
         let mut deps = Vec::new();
         cstore.iter_crate_data(|cnum, val| {
@@ -1789,7 +1737,7 @@ fn encode_reachable(ecx: &EncodeContext, rbml_w: &mut Encoder) {
 }
 
 fn encode_crate_dep(rbml_w: &mut Encoder,
-                    dep: &cstore::crate_metadata) {
+                    dep: &cstore::CrateMetadata) {
     rbml_w.start_tag(tag_crate_dep);
     rbml_w.wr_tagged_str(tag_crate_dep_crate_name, &dep.name());
     let hash = decoder::get_crate_hash(dep.data());
index f7ea60c4078dc6379c506ed63b62b8571913d469..cd92493e3db703adc8e7931d51357fbc6292406f 100644 (file)
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
 #[macro_use] #[no_link] extern crate rustc_bitflags;
-
+extern crate syntax_pos;
 extern crate flate;
 extern crate rbml;
 extern crate serialize as rustc_serialize; // used by deriving
+extern crate rustc_errors as errors;
 
 #[macro_use]
 extern crate rustc;
+extern crate rustc_data_structures;
 extern crate rustc_back;
 extern crate rustc_llvm;
 extern crate rustc_const_math;
index dc10391b6a88e7abddc0ae3cdb96852eb48eba87..48c8bcff1ec560a12db7bdba3f24b9be15b922ba 100644 (file)
@@ -225,8 +225,8 @@ use rustc::util::common;
 use rustc_llvm as llvm;
 use rustc_llvm::{False, ObjectFile, mk_section_iter};
 use rustc_llvm::archive_ro::ArchiveRO;
-use syntax::codemap::Span;
-use syntax::errors::DiagnosticBuilder;
+use errors::DiagnosticBuilder;
+use syntax_pos::Span;
 use rustc_back::target::Target;
 
 use std::cmp;
@@ -503,19 +503,11 @@ impl<'a> Context<'a> {
                                                self.crate_name);
                 err.note("candidates:");
                 for (_, lib) in libraries {
-                    match lib.dylib {
-                        Some((ref p, _)) => {
-                            err.note(&format!("path: {}",
-                                              p.display()));
-                        }
-                        None => {}
+                    if let Some((ref p, _)) = lib.dylib {
+                        err.note(&format!("path: {}", p.display()));
                     }
-                    match lib.rlib {
-                        Some((ref p, _)) => {
-                            err.note(&format!("path: {}",
-                                              p.display()));
-                        }
-                        None => {}
+                    if let Some((ref p, _)) = lib.rlib {
+                        err.note(&format!("path: {}", p.display()));
                     }
                     let data = lib.metadata.as_slice();
                     let name = decoder::get_crate_name(data);
index 911ca7e315c1f0147cc21110409495793d5a1dd1..7dadf8d108a715f5e4dbf4c0b0e64499d5fc53d1 100644 (file)
@@ -16,28 +16,23 @@ use cstore::CStore;
 use rustc::session::Session;
 
 use std::collections::{HashSet, HashMap};
-use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::ast;
 use syntax::attr;
-use syntax::visit;
-use syntax::visit::Visitor;
 use syntax::attr::AttrMetaMethods;
+use syntax::ext;
+use syntax_pos::Span;
 
-struct MacroLoader<'a> {
+pub struct MacroLoader<'a> {
     sess: &'a Session,
-    span_whitelist: HashSet<Span>,
     reader: CrateReader<'a>,
-    macros: Vec<ast::MacroDef>,
 }
 
 impl<'a> MacroLoader<'a> {
-    fn new(sess: &'a Session, cstore: &'a CStore, crate_name: &str) -> MacroLoader<'a> {
+    pub fn new(sess: &'a Session, cstore: &'a CStore, crate_name: &str) -> MacroLoader<'a> {
         MacroLoader {
             sess: sess,
-            span_whitelist: HashSet::new(),
             reader: CrateReader::new(sess, cstore, crate_name),
-            macros: vec![],
         }
     }
 }
@@ -46,48 +41,15 @@ pub fn call_bad_macro_reexport(a: &Session, b: Span) {
     span_err!(a, b, E0467, "bad macro reexport");
 }
 
-/// Read exported macros.
-pub fn read_macro_defs(sess: &Session,
-                       cstore: &CStore,
-                       krate: &ast::Crate,
-                       crate_name: &str)
-                       -> Vec<ast::MacroDef>
-{
-    let mut loader = MacroLoader::new(sess, cstore, crate_name);
-
-    // We need to error on `#[macro_use] extern crate` when it isn't at the
-    // crate root, because `$crate` won't work properly. Identify these by
-    // spans, because the crate map isn't set up yet.
-    for item in &krate.module.items {
-        if let ast::ItemKind::ExternCrate(_) = item.node {
-            loader.span_whitelist.insert(item.span);
-        }
-    }
-
-    visit::walk_crate(&mut loader, krate);
-
-    loader.macros
-}
-
 pub type MacroSelection = HashMap<token::InternedString, Span>;
 
-// note that macros aren't expanded yet, and therefore macros can't add macro imports.
-impl<'a, 'v> Visitor<'v> for MacroLoader<'a> {
-    fn visit_item(&mut self, item: &ast::Item) {
-        // We're only interested in `extern crate`.
-        match item.node {
-            ast::ItemKind::ExternCrate(_) => {}
-            _ => {
-                visit::walk_item(self, item);
-                return;
-            }
-        }
-
+impl<'a> ext::base::MacroLoader for MacroLoader<'a> {
+    fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<ast::MacroDef> {
         // Parse the attributes relating to macros.
         let mut import = Some(HashMap::new());  // None => load all
         let mut reexport = HashMap::new();
 
-        for attr in &item.attrs {
+        for attr in &extern_crate.attrs {
             let mut used = true;
             match &attr.name()[..] {
                 "macro_use" => {
@@ -130,36 +92,33 @@ impl<'a, 'v> Visitor<'v> for MacroLoader<'a> {
             }
         }
 
-        self.load_macros(item, import, reexport)
-    }
-
-    fn visit_mac(&mut self, _: &ast::Mac) {
-        // bummer... can't see macro imports inside macros.
-        // do nothing.
+        self.load_macros(extern_crate, allows_macros, import, reexport)
     }
 }
 
 impl<'a> MacroLoader<'a> {
     fn load_macros<'b>(&mut self,
                        vi: &ast::Item,
+                       allows_macros: bool,
                        import: Option<MacroSelection>,
-                       reexport: MacroSelection) {
+                       reexport: MacroSelection)
+                       -> Vec<ast::MacroDef> {
         if let Some(sel) = import.as_ref() {
             if sel.is_empty() && reexport.is_empty() {
-                return;
+                return Vec::new();
             }
         }
 
-        if !self.span_whitelist.contains(&vi.span) {
+        if !allows_macros {
             span_err!(self.sess, vi.span, E0468,
                       "an `extern crate` loading macros must be at the crate root");
-            return;
+            return Vec::new();
         }
 
-        let macros = self.reader.read_exported_macros(vi);
+        let mut macros = Vec::new();
         let mut seen = HashSet::new();
 
-        for mut def in macros {
+        for mut def in self.reader.read_exported_macros(vi) {
             let name = def.ident.name.as_str();
 
             def.use_locally = match import.as_ref() {
@@ -170,7 +129,7 @@ impl<'a> MacroLoader<'a> {
             def.allow_internal_unstable = attr::contains_name(&def.attrs,
                                                               "allow_internal_unstable");
             debug!("load_macros: loaded: {:?}", def);
-            self.macros.push(def);
+            macros.push(def);
             seen.insert(name);
         }
 
@@ -189,5 +148,7 @@ impl<'a> MacroLoader<'a> {
                           "reexported macro not found");
             }
         }
+
+        macros
     }
 }
index c94af9c5b3ae318d64332c35cc05650a275d3d45..119640af463aa8bf768dddfc23ede46bd996df88 100644 (file)
@@ -158,8 +158,21 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
             }
             '[' => {
                 let def = self.parse_def();
-                let name = token::intern(&self.parse_str(']'));
-                ty::BrNamed(def, name)
+                let name = token::intern(&self.parse_str('|'));
+                let issue32330 = match self.next() {
+                    'n' => {
+                        assert_eq!(self.next(), ']');
+                        ty::Issue32330::WontChange
+                    }
+                    'y' => {
+                        ty::Issue32330::WillChange {
+                            fn_def_id: self.parse_def(),
+                            region_name: token::intern(&self.parse_str(']')),
+                        }
+                    }
+                    c => panic!("expected n or y not {}", c)
+                };
+                ty::BrNamed(def, name, issue32330)
             }
             'f' => {
                 let id = self.parse_u32();
@@ -208,12 +221,9 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
                 assert_eq!(self.next(), '|');
                 ty::ReScope(scope)
             }
-            't' => {
-                ty::ReStatic
-            }
-            'e' => {
-                ty::ReStatic
-            }
+            't' => ty::ReStatic,
+            'e' => ty::ReEmpty,
+            'E' => ty::ReErased,
             _ => bug!("parse_region: bad input")
         }
     }
@@ -386,16 +396,13 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
 
                 let pos = self.parse_vuint();
                 let key = ty::CReaderCacheKey { cnum: self.krate, pos: pos };
-                match tcx.rcache.borrow().get(&key).cloned() {
-                    Some(tt) => {
-                        // If there is a closure buried in the type some where, then we
-                        // need to re-convert any def ids (see case 'k', below). That means
-                        // we can't reuse the cached version.
-                        if !tt.has_closure_types() {
-                            return tt;
-                        }
+                if let Some(tt) = tcx.rcache.borrow().get(&key).cloned() {
+                    // If there is a closure buried in the type some where, then we
+                    // need to re-convert any def ids (see case 'k', below). That means
+                    // we can't reuse the cached version.
+                    if !tt.has_closure_types() {
+                        return tt;
                     }
-                    None => {}
                 }
 
                 let mut substate = TyDecoder::new(self.data,
@@ -623,7 +630,7 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
             def_id: def_id,
             space: space,
             index: index,
-            bounds: bounds
+            bounds: bounds,
         }
     }
 
index 343c452f89165aa3a12085cd3ff7e768cec7ca6e..3484a8b75dded7105c12e44d98d5e3b11ad7ccd2 100644 (file)
@@ -29,7 +29,7 @@ use rustc::hir;
 
 use syntax::abi::Abi;
 use syntax::ast;
-use syntax::errors::Handler;
+use errors::Handler;
 
 use rbml::leb128;
 use encoder;
@@ -64,9 +64,9 @@ pub struct ty_abbrev {
 pub type abbrev_map<'tcx> = RefCell<FnvHashMap<Ty<'tcx>, ty_abbrev>>;
 
 pub fn enc_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx>) {
-    match cx.abbrevs.borrow_mut().get(&t) {
-        Some(a) => { w.write_all(&a.s); return; }
-        None => {}
+    if let Some(a) = cx.abbrevs.borrow_mut().get(&t) {
+        w.write_all(&a.s);
+        return;
     }
 
     let pos = w.position();
@@ -283,6 +283,9 @@ pub fn enc_region(w: &mut Cursor<Vec<u8>>, cx: &ctxt, r: ty::Region) {
         ty::ReEmpty => {
             write!(w, "e");
         }
+        ty::ReErased => {
+            write!(w, "E");
+        }
         ty::ReVar(_) | ty::ReSkolemized(..) => {
             // these should not crop up after typeck
             bug!("cannot encode region variables");
@@ -308,10 +311,17 @@ fn enc_bound_region(w: &mut Cursor<Vec<u8>>, cx: &ctxt, br: ty::BoundRegion) {
         ty::BrAnon(idx) => {
             write!(w, "a{}|", idx);
         }
-        ty::BrNamed(d, name) => {
-            write!(w, "[{}|{}]",
-                     (cx.ds)(cx.tcx, d),
-                     name);
+        ty::BrNamed(d, name, issue32330) => {
+            write!(w, "[{}|{}|",
+                   (cx.ds)(cx.tcx, d),
+                   name);
+
+            match issue32330 {
+                ty::Issue32330::WontChange =>
+                    write!(w, "n]"),
+                ty::Issue32330::WillChange { fn_def_id, region_name } =>
+                    write!(w, "y{}|{}]", (cx.ds)(cx.tcx, fn_def_id), region_name),
+            };
         }
         ty::BrFresh(id) => {
             write!(w, "f{}|", id);
@@ -397,7 +407,14 @@ pub fn enc_existential_bounds<'a,'tcx>(w: &mut Cursor<Vec<u8>>,
 
     enc_region(w, cx, bs.region_bound);
 
-    for tp in &bs.projection_bounds {
+    // Encode projection_bounds in a stable order
+    let mut projection_bounds: Vec<_> = bs.projection_bounds
+                                          .iter()
+                                          .map(|b| (b.item_name().as_str(), b))
+                                          .collect();
+    projection_bounds.sort_by_key(|&(ref name, _)| name.clone());
+
+    for tp in projection_bounds.iter().map(|&(_, tp)| tp) {
         write!(w, "P");
         enc_projection_predicate(w, cx, &tp.0);
     }
index 77dccb7e0d48347b96500dfb62bd408563aa7d16..2a1a815330675ad9b155a9045f7eb4262c38649f 100644 (file)
@@ -18,3 +18,4 @@ rustc_const_math = { path = "../librustc_const_math" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_bitflags = { path = "../librustc_bitflags" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
index c1626b93f0c4181f1f9c18d4729fd04a2c9e01c6..7e650c5bd3d06c03f34c3f9494ff1e8ec4df2a8f 100644 (file)
@@ -22,7 +22,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                      ast_block: &'tcx hir::Block)
                      -> BlockAnd<()> {
         let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block);
-        self.in_scope(extent, block, move |this, _| {
+        self.in_scope(extent, block, move |this| {
             // This convoluted structure is to avoid using recursion as we walk down a list
             // of statements. Basically, the structure we get back is something like:
             //
@@ -40,27 +40,40 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             //
             // First we build all the statements in the block.
             let mut let_extent_stack = Vec::with_capacity(8);
+            let outer_visibility_scope = this.visibility_scope;
             for stmt in stmts {
                 let Stmt { span: _, kind } = this.hir.mirror(stmt);
                 match kind {
                     StmtKind::Expr { scope, expr } => {
-                        unpack!(block = this.in_scope(scope, block, |this, _| {
+                        unpack!(block = this.in_scope(scope, block, |this| {
                             let expr = this.hir.mirror(expr);
                             this.stmt_expr(block, expr)
                         }));
                     }
                     StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
-                        let remainder_scope_id = this.push_scope(remainder_scope, block);
+                        let tcx = this.hir.tcx();
+
+                        // Enter the remainder scope, i.e. the bindings' destruction scope.
+                        this.push_scope(remainder_scope, block);
                         let_extent_stack.push(remainder_scope);
-                        unpack!(block = this.in_scope(init_scope, block, move |this, _| {
-                            // FIXME #30046                              ^~~~
-                            if let Some(init) = initializer {
-                                this.expr_into_pattern(block, remainder_scope_id, pattern, init)
-                            } else {
-                                this.declare_bindings(remainder_scope_id, &pattern);
-                                block.unit()
-                            }
-                        }));
+
+                        // Declare the bindings, which may create a visibility scope.
+                        let remainder_span = remainder_scope.span(&tcx.region_maps, &tcx.map);
+                        let remainder_span = remainder_span.unwrap_or(span);
+                        let scope = this.declare_bindings(None, remainder_span, &pattern);
+
+                        // Evaluate the initializer, if present.
+                        if let Some(init) = initializer {
+                            unpack!(block = this.in_scope(init_scope, block, move |this| {
+                                // FIXME #30046                              ^~~~
+                                this.expr_into_pattern(block, pattern, init)
+                            }));
+                        }
+
+                        // Enter the visibility scope, after evaluating the initializer.
+                        if let Some(visibility_scope) = scope {
+                            this.visibility_scope = visibility_scope;
+                        }
                     }
                 }
             }
@@ -70,14 +83,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 unpack!(block = this.into(destination, block, expr));
             } else if dest_is_unit {
                 // FIXME(#31472)
-                let scope_id = this.innermost_scope_id();
-                this.cfg.push_assign_unit(block, scope_id, span, destination);
+                let source_info = this.source_info(span);
+                this.cfg.push_assign_unit(block, source_info, destination);
             }
             // Finally, we pop all the let scopes before exiting out from the scope of block
             // itself.
             for extent in let_extent_stack.into_iter().rev() {
                 unpack!(block = this.pop_scope(extent, block));
             }
+            // Restore the original visibility scope.
+            this.visibility_scope = outer_visibility_scope;
             block.unit()
         })
     }
index 4859257f291c9767f694b9d4171ddcfeaabb0805..83f8c3b42c850a1eb52d28f78daed875f71a0749 100644 (file)
 
 use build::{CFG, Location};
 use rustc::mir::repr::*;
-use syntax::codemap::Span;
 
 impl<'tcx> CFG<'tcx> {
     pub fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> {
-        &self.basic_blocks[blk.index()]
+        &self.basic_blocks[blk]
     }
 
     pub fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<'tcx> {
-        &mut self.basic_blocks[blk.index()]
+        &mut self.basic_blocks[blk]
     }
 
     pub fn start_new_block(&mut self) -> BasicBlock {
-        let node_index = self.basic_blocks.len();
-        self.basic_blocks.push(BasicBlockData::new(None));
-        BasicBlock::new(node_index)
+        self.basic_blocks.push(BasicBlockData::new(None))
     }
 
     pub fn start_new_cleanup_block(&mut self) -> BasicBlock {
@@ -50,47 +47,44 @@ impl<'tcx> CFG<'tcx> {
 
     pub fn push_assign(&mut self,
                        block: BasicBlock,
-                       scope: ScopeId,
-                       span: Span,
+                       source_info: SourceInfo,
                        lvalue: &Lvalue<'tcx>,
                        rvalue: Rvalue<'tcx>) {
         self.push(block, Statement {
-            scope: scope,
-            span: span,
+            source_info: source_info,
             kind: StatementKind::Assign(lvalue.clone(), rvalue)
         });
     }
 
     pub fn push_assign_constant(&mut self,
                                 block: BasicBlock,
-                                scope: ScopeId,
-                                span: Span,
+                                source_info: SourceInfo,
                                 temp: &Lvalue<'tcx>,
                                 constant: Constant<'tcx>) {
-        self.push_assign(block, scope, span, temp,
+        self.push_assign(block, source_info, temp,
                          Rvalue::Use(Operand::Constant(constant)));
     }
 
     pub fn push_assign_unit(&mut self,
                             block: BasicBlock,
-                            scope: ScopeId,
-                            span: Span,
+                            source_info: SourceInfo,
                             lvalue: &Lvalue<'tcx>) {
-        self.push_assign(block, scope, span, lvalue, Rvalue::Aggregate(
+        self.push_assign(block, source_info, lvalue, Rvalue::Aggregate(
             AggregateKind::Tuple, vec![]
         ));
     }
 
     pub fn terminate(&mut self,
                      block: BasicBlock,
-                     scope: ScopeId,
-                     span: Span,
+                     source_info: SourceInfo,
                      kind: TerminatorKind<'tcx>) {
+        debug!("terminating block {:?} <- {:?}", block, kind);
         debug_assert!(self.block_data(block).terminator.is_none(),
-                      "terminate: block {:?} already has a terminator set", block);
+                      "terminate: block {:?}={:?} already has a terminator set",
+                      block,
+                      self.block_data(block));
         self.block_data_mut(block).terminator = Some(Terminator {
-            span: span,
-            scope: scope,
+            source_info: source_info,
             kind: kind,
         });
     }
index 15ea3f0e6e8675ce065c509277ce58cb01cdb398..dd6c9c02f56446e802a792f5490ba0cc1faf504b 100644 (file)
@@ -15,6 +15,8 @@ use build::expr::category::Category;
 use hair::*;
 use rustc::mir::repr::*;
 
+use rustc_data_structures::indexed_vec::Idx;
+
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// Compile `expr`, yielding an lvalue that we can move from etc.
     pub fn as_lvalue<M>(&mut self,
@@ -34,11 +36,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         debug!("expr_as_lvalue(block={:?}, expr={:?})", block, expr);
 
         let this = self;
-        let scope_id = this.innermost_scope_id();
         let expr_span = expr.span;
+        let source_info = this.source_info(expr_span);
         match expr.kind {
             ExprKind::Scope { extent, value } => {
-                this.in_scope(extent, block, |this, _| this.as_lvalue(block, value))
+                this.in_scope(extent, block, |this| this.as_lvalue(block, value))
             }
             ExprKind::Field { lhs, name } => {
                 let lvalue = unpack!(block = this.as_lvalue(block, lhs));
@@ -59,26 +61,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                 // bounds check:
                 let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty));
-                this.cfg.push_assign(block, scope_id, expr_span, // len = len(slice)
+                this.cfg.push_assign(block, source_info, // len = len(slice)
                                      &len, Rvalue::Len(slice.clone()));
-                this.cfg.push_assign(block, scope_id, expr_span, // lt = idx < len
+                this.cfg.push_assign(block, source_info, // lt = idx < len
                                      &lt, Rvalue::BinaryOp(BinOp::Lt,
                                                            idx.clone(),
                                                            Operand::Consume(len.clone())));
 
-                let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block());
-                this.cfg.terminate(block,
-                                   scope_id,
-                                   expr_span,
-                                   TerminatorKind::If {
-                                       cond: Operand::Consume(lt),
-                                       targets: (success, failure),
-                                   });
-                this.panic_bounds_check(failure, idx.clone(), Operand::Consume(len), expr_span);
+                let msg = AssertMessage::BoundsCheck {
+                    len: Operand::Consume(len),
+                    index: idx.clone()
+                };
+                let success = this.assert(block, Operand::Consume(lt), true,
+                                          msg, expr_span);
                 success.and(slice.index(idx))
             }
             ExprKind::SelfRef => {
-                block.and(Lvalue::Arg(0))
+                block.and(Lvalue::Arg(Arg::new(0)))
             }
             ExprKind::VarRef { id } => {
                 let index = this.var_indices[&id];
index a059f2bdde9c733608dd56dd708feea84e8775fd..beb9ca256abfd9248a93b3a4805838f1853be284 100644 (file)
@@ -35,7 +35,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let this = self;
 
         if let ExprKind::Scope { extent, value } = expr.kind {
-            return this.in_scope(extent, block, |this, _| this.as_operand(block, value));
+            return this.in_scope(extent, block, |this| this.as_operand(block, value));
         }
 
         let category = Category::of(&expr.kind).unwrap();
index 2a73346240898fc89282223fb801d7edbb94c158..f1487992cb52285365e549e458484be1765592ce 100644 (file)
 
 //! See docs in build/expr/mod.rs
 
+use std;
+
+use rustc_const_math::{ConstMathErr, Op};
 use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::indexed_vec::Idx;
 
 use build::{BlockAnd, BlockAndExtension, Builder};
 use build::expr::category::{Category, RvalueFunc};
 use hair::*;
+use rustc_const_math::{ConstInt, ConstIsize};
+use rustc::middle::const_val::ConstVal;
+use rustc::ty;
 use rustc::mir::repr::*;
+use syntax::ast;
+use syntax_pos::Span;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// Compile `expr`, yielding an rvalue.
@@ -33,12 +42,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         debug!("expr_as_rvalue(block={:?}, expr={:?})", block, expr);
 
         let this = self;
-        let scope_id = this.innermost_scope_id();
         let expr_span = expr.span;
+        let source_info = this.source_info(expr_span);
 
         match expr.kind {
             ExprKind::Scope { extent, value } => {
-                this.in_scope(extent, block, |this, _| this.as_rvalue(block, value))
+                this.in_scope(extent, block, |this| this.as_rvalue(block, value))
             }
             ExprKind::InlineAsm { asm, outputs, inputs } => {
                 let outputs = outputs.into_iter().map(|output| {
@@ -66,18 +75,33 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             ExprKind::Binary { op, lhs, rhs } => {
                 let lhs = unpack!(block = this.as_operand(block, lhs));
                 let rhs = unpack!(block = this.as_operand(block, rhs));
-                block.and(Rvalue::BinaryOp(op, lhs, rhs))
+                this.build_binary_op(block, op, expr_span, expr.ty,
+                                     lhs, rhs)
             }
             ExprKind::Unary { op, arg } => {
                 let arg = unpack!(block = this.as_operand(block, arg));
+                // Check for -MIN on signed integers
+                if this.hir.check_overflow() && op == UnOp::Neg && expr.ty.is_signed() {
+                    let bool_ty = this.hir.bool_ty();
+
+                    let minval = this.minval_literal(expr_span, expr.ty);
+                    let is_min = this.temp(bool_ty);
+
+                    this.cfg.push_assign(block, source_info, &is_min,
+                                         Rvalue::BinaryOp(BinOp::Eq, arg.clone(), minval));
+
+                    let err = ConstMathErr::Overflow(Op::Neg);
+                    block = this.assert(block, Operand::Consume(is_min), false,
+                                        AssertMessage::Math(err), expr_span);
+                }
                 block.and(Rvalue::UnaryOp(op, arg))
             }
             ExprKind::Box { value, value_extents } => {
                 let value = this.hir.mirror(value);
                 let result = this.temp(expr.ty);
                 // to start, malloc some memory of suitable type (thus far, uninitialized):
-                this.cfg.push_assign(block, scope_id, expr_span, &result, Rvalue::Box(value.ty));
-                this.in_scope(value_extents, block, |this, _| {
+                this.cfg.push_assign(block, source_info, &result, Rvalue::Box(value.ty));
+                this.in_scope(value_extents, block, |this| {
                     // schedule a shallow free of that memory, lest we unwind:
                     this.schedule_box_free(expr_span, value_extents, &result, value.ty);
                     // initialize the box contents:
@@ -218,4 +242,149 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
         }
     }
+
+    pub fn build_binary_op(&mut self, mut block: BasicBlock,
+                           op: BinOp, span: Span, ty: ty::Ty<'tcx>,
+                           lhs: Operand<'tcx>, rhs: Operand<'tcx>) -> BlockAnd<Rvalue<'tcx>> {
+        let source_info = self.source_info(span);
+        let bool_ty = self.hir.bool_ty();
+        if self.hir.check_overflow() && op.is_checkable() && ty.is_integral() {
+            let result_tup = self.hir.tcx().mk_tup(vec![ty, bool_ty]);
+            let result_value = self.temp(result_tup);
+
+            self.cfg.push_assign(block, source_info,
+                                 &result_value, Rvalue::CheckedBinaryOp(op,
+                                                                        lhs,
+                                                                        rhs));
+            let val_fld = Field::new(0);
+            let of_fld = Field::new(1);
+
+            let val = result_value.clone().field(val_fld, ty);
+            let of = result_value.field(of_fld, bool_ty);
+
+            let err = ConstMathErr::Overflow(match op {
+                BinOp::Add => Op::Add,
+                BinOp::Sub => Op::Sub,
+                BinOp::Mul => Op::Mul,
+                BinOp::Shl => Op::Shl,
+                BinOp::Shr => Op::Shr,
+                _ => {
+                    bug!("MIR build_binary_op: {:?} is not checkable", op)
+                }
+            });
+
+            block = self.assert(block, Operand::Consume(of), false,
+                                AssertMessage::Math(err), span);
+
+            block.and(Rvalue::Use(Operand::Consume(val)))
+        } else {
+            if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) {
+                // Checking division and remainder is more complex, since we 1. always check
+                // and 2. there are two possible failure cases, divide-by-zero and overflow.
+
+                let (zero_err, overflow_err) = if op == BinOp::Div {
+                    (ConstMathErr::DivisionByZero,
+                     ConstMathErr::Overflow(Op::Div))
+                } else {
+                    (ConstMathErr::RemainderByZero,
+                     ConstMathErr::Overflow(Op::Rem))
+                };
+
+                // Check for / 0
+                let is_zero = self.temp(bool_ty);
+                let zero = self.zero_literal(span, ty);
+                self.cfg.push_assign(block, source_info, &is_zero,
+                                     Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), zero));
+
+                block = self.assert(block, Operand::Consume(is_zero), false,
+                                    AssertMessage::Math(zero_err), span);
+
+                // We only need to check for the overflow in one case:
+                // MIN / -1, and only for signed values.
+                if ty.is_signed() {
+                    let neg_1 = self.neg_1_literal(span, ty);
+                    let min = self.minval_literal(span, ty);
+
+                    let is_neg_1 = self.temp(bool_ty);
+                    let is_min   = self.temp(bool_ty);
+                    let of       = self.temp(bool_ty);
+
+                    // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead
+
+                    self.cfg.push_assign(block, source_info, &is_neg_1,
+                                         Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), neg_1));
+                    self.cfg.push_assign(block, source_info, &is_min,
+                                         Rvalue::BinaryOp(BinOp::Eq, lhs.clone(), min));
+
+                    let is_neg_1 = Operand::Consume(is_neg_1);
+                    let is_min = Operand::Consume(is_min);
+                    self.cfg.push_assign(block, source_info, &of,
+                                         Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min));
+
+                    block = self.assert(block, Operand::Consume(of), false,
+                                        AssertMessage::Math(overflow_err), span);
+                }
+            }
+
+            block.and(Rvalue::BinaryOp(op, lhs, rhs))
+        }
+    }
+
+    // Helper to get a `-1` value of the appropriate type
+    fn neg_1_literal(&mut self, span: Span, ty: ty::Ty<'tcx>) -> Operand<'tcx> {
+        let literal = match ty.sty {
+            ty::TyInt(ity) => {
+                let val = match ity {
+                    ast::IntTy::I8  => ConstInt::I8(-1),
+                    ast::IntTy::I16 => ConstInt::I16(-1),
+                    ast::IntTy::I32 => ConstInt::I32(-1),
+                    ast::IntTy::I64 => ConstInt::I64(-1),
+                    ast::IntTy::Is => {
+                        let int_ty = self.hir.tcx().sess.target.int_type;
+                        let val = ConstIsize::new(-1, int_ty).unwrap();
+                        ConstInt::Isize(val)
+                    }
+                };
+
+                Literal::Value { value: ConstVal::Integral(val) }
+            }
+            _ => {
+                span_bug!(span, "Invalid type for neg_1_literal: `{:?}`", ty)
+            }
+        };
+
+        self.literal_operand(span, ty, literal)
+    }
+
+    // Helper to get the minimum value of the appropriate type
+    fn minval_literal(&mut self, span: Span, ty: ty::Ty<'tcx>) -> Operand<'tcx> {
+        let literal = match ty.sty {
+            ty::TyInt(ity) => {
+                let val = match ity {
+                    ast::IntTy::I8  => ConstInt::I8(std::i8::MIN),
+                    ast::IntTy::I16 => ConstInt::I16(std::i16::MIN),
+                    ast::IntTy::I32 => ConstInt::I32(std::i32::MIN),
+                    ast::IntTy::I64 => ConstInt::I64(std::i64::MIN),
+                    ast::IntTy::Is => {
+                        let int_ty = self.hir.tcx().sess.target.int_type;
+                        let min = match int_ty {
+                            ast::IntTy::I16 => std::i16::MIN as i64,
+                            ast::IntTy::I32 => std::i32::MIN as i64,
+                            ast::IntTy::I64 => std::i64::MIN,
+                            _ => unreachable!()
+                        };
+                        let val = ConstIsize::new(min, int_ty).unwrap();
+                        ConstInt::Isize(val)
+                    }
+                };
+
+                Literal::Value { value: ConstVal::Integral(val) }
+            }
+            _ => {
+                span_bug!(span, "Invalid type for minval_literal: `{:?}`", ty)
+            }
+        };
+
+        self.literal_operand(span, ty, literal)
+    }
 }
index f33d3dd5190d9fc7eb28d5930b446ea35379139e..da128b8dd56266ada2214ddb9c5241505d11643d 100644 (file)
@@ -30,7 +30,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let this = self;
 
         if let ExprKind::Scope { extent, value } = expr.kind {
-            return this.in_scope(extent, block, |this, _| this.as_temp(block, value));
+            return this.in_scope(extent, block, |this| this.as_temp(block, value));
         }
 
         let expr_ty = expr.ty.clone();
@@ -49,8 +49,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             Category::Lvalue => {
                 let lvalue = unpack!(block = this.as_lvalue(block, expr));
                 let rvalue = Rvalue::Use(Operand::Consume(lvalue));
-                let scope_id = this.innermost_scope_id();
-                this.cfg.push_assign(block, scope_id, expr_span, &temp, rvalue);
+                let source_info = this.source_info(expr_span);
+                this.cfg.push_assign(block, source_info, &temp, rvalue);
             }
             _ => {
                 unpack!(block = this.into(&temp, block, expr));
index 41610c90377b52af4119c22ffae705d7e4b3d164..fd9ddc05ab5c4cfb3533fc42aadc4b2fce37d07d 100644 (file)
@@ -33,11 +33,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         // just use the name `this` uniformly
         let this = self;
         let expr_span = expr.span;
-        let scope_id = this.innermost_scope_id();
+        let source_info = this.source_info(expr_span);
 
         match expr.kind {
             ExprKind::Scope { extent, value } => {
-                this.in_scope(extent, block, |this, _| this.into(destination, block, value))
+                this.in_scope(extent, block, |this| this.into(destination, block, value))
             }
             ExprKind::Block { body: ast_block } => {
                 this.ast_block(destination, expr.ty.is_nil(), block, ast_block)
@@ -50,7 +50,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                 let mut then_block = this.cfg.start_new_block();
                 let mut else_block = this.cfg.start_new_block();
-                this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::If {
+                this.cfg.terminate(block, source_info, TerminatorKind::If {
                     cond: operand,
                     targets: (then_block, else_block)
                 });
@@ -61,19 +61,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 } else {
                     // Body of the `if` expression without an `else` clause must return `()`, thus
                     // we implicitly generate a `else {}` if it is not specified.
-                    let scope_id = this.innermost_scope_id();
-                    this.cfg.push_assign_unit(else_block, scope_id, expr_span, destination);
+                    this.cfg.push_assign_unit(else_block, source_info, destination);
                     else_block
                 };
 
                 let join_block = this.cfg.start_new_block();
-                this.cfg.terminate(then_block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(then_block, source_info,
                                    TerminatorKind::Goto { target: join_block });
-                this.cfg.terminate(else_block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(else_block, source_info,
                                    TerminatorKind::Goto { target: join_block });
 
                 join_block.unit()
@@ -100,19 +95,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     LogicalOp::And => (else_block, false_block),
                     LogicalOp::Or => (true_block, else_block),
                 };
-                this.cfg.terminate(block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(block, source_info,
                                    TerminatorKind::If { cond: lhs, targets: blocks });
 
                 let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
-                this.cfg.terminate(else_block, scope_id, expr_span, TerminatorKind::If {
+                this.cfg.terminate(else_block, source_info, TerminatorKind::If {
                     cond: rhs,
                     targets: (true_block, false_block)
                 });
 
                 this.cfg.push_assign_constant(
-                    true_block, scope_id, expr_span, destination,
+                    true_block, source_info, destination,
                     Constant {
                         span: expr_span,
                         ty: this.hir.bool_ty(),
@@ -120,20 +113,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     });
 
                 this.cfg.push_assign_constant(
-                    false_block, scope_id, expr_span, destination,
+                    false_block, source_info, destination,
                     Constant {
                         span: expr_span,
                         ty: this.hir.bool_ty(),
                         literal: this.hir.false_literal(),
                     });
 
-                this.cfg.terminate(true_block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(true_block, source_info,
                                    TerminatorKind::Goto { target: join_block });
-                this.cfg.terminate(false_block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(false_block, source_info,
                                    TerminatorKind::Goto { target: join_block });
 
                 join_block.unit()
@@ -158,9 +147,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let exit_block = this.cfg.start_new_block();
 
                 // start the loop
-                this.cfg.terminate(block,
-                                   scope_id,
-                                   expr_span,
+                this.cfg.terminate(block, source_info,
                                    TerminatorKind::Goto { target: loop_block });
 
                 let might_break = this.in_loop_scope(loop_block, exit_block, move |this| {
@@ -173,9 +160,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         let loop_block_end;
                         let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr));
                         body_block = this.cfg.start_new_block();
-                        this.cfg.terminate(loop_block_end,
-                                           scope_id,
-                                           expr_span,
+                        this.cfg.terminate(loop_block_end, source_info,
                                            TerminatorKind::If {
                                                cond: cond,
                                                targets: (body_block, exit_block)
@@ -192,15 +177,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     let tmp = this.get_unit_temp();
                     // Execute the body, branching back to the test.
                     let body_block_end = unpack!(this.into(&tmp, body_block, body));
-                    this.cfg.terminate(body_block_end,
-                                       scope_id,
-                                       expr_span,
+                    this.cfg.terminate(body_block_end, source_info,
                                        TerminatorKind::Goto { target: loop_block });
                 });
                 // If the loop may reach its exit_block, we assign an empty tuple to the
                 // destination to keep the MIR well-formed.
                 if might_break {
-                    this.cfg.push_assign_unit(exit_block, scope_id, expr_span, destination);
+                    this.cfg.push_assign_unit(exit_block, source_info, destination);
                 }
                 exit_block.unit()
             }
@@ -219,7 +202,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                 let success = this.cfg.start_new_block();
                 let cleanup = this.diverge_cleanup();
-                this.cfg.terminate(block, scope_id, expr_span, TerminatorKind::Call {
+                this.cfg.terminate(block, source_info, TerminatorKind::Call {
                     func: fun,
                     args: args,
                     cleanup: cleanup,
@@ -269,7 +252,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 });
 
                 let rvalue = unpack!(block = this.as_rvalue(block, expr));
-                this.cfg.push_assign(block, scope_id, expr_span, destination, rvalue);
+                this.cfg.push_assign(block, source_info, destination, rvalue);
                 block.unit()
             }
         }
index 9629396f48b50f1274b364e7021ade9a835d20af..8ae23c9103b02ca7c51d7925b05715724e1cda83 100644 (file)
@@ -13,50 +13,45 @@ use build::scope::LoopScope;
 use hair::*;
 use rustc::middle::region::CodeExtent;
 use rustc::mir::repr::*;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
     pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<()> {
         let this = self;
         let expr_span = expr.span;
-        let scope_id = this.innermost_scope_id();
+        let source_info = this.source_info(expr.span);
         // Handle a number of expressions that don't need a destination at all. This
         // avoids needing a mountain of temporary `()` variables.
         match expr.kind {
             ExprKind::Scope { extent, value } => {
                 let value = this.hir.mirror(value);
-                this.in_scope(extent, block, |this, _| this.stmt_expr(block, value))
+                this.in_scope(extent, block, |this| this.stmt_expr(block, value))
             }
             ExprKind::Assign { lhs, rhs } => {
                 let lhs = this.hir.mirror(lhs);
                 let rhs = this.hir.mirror(rhs);
-                let scope_id = this.innermost_scope_id();
                 let lhs_span = lhs.span;
 
-                let lhs_ty = lhs.ty;
-                let rhs_ty = rhs.ty;
-
-                let lhs_needs_drop = this.hir.needs_drop(lhs_ty);
-                let rhs_needs_drop = this.hir.needs_drop(rhs_ty);
-
                 // Note: we evaluate assignments right-to-left. This
                 // is better for borrowck interaction with overloaded
                 // operators like x[j] = x[i].
 
                 // Generate better code for things that don't need to be
                 // dropped.
-                let rhs = if lhs_needs_drop || rhs_needs_drop {
-                    let op = unpack!(block = this.as_operand(block, rhs));
-                    Rvalue::Use(op)
+                if this.hir.needs_drop(lhs.ty) {
+                    let rhs = unpack!(block = this.as_operand(block, rhs));
+                    let lhs = unpack!(block = this.as_lvalue(block, lhs));
+                    unpack!(block = this.build_drop_and_replace(
+                        block, lhs_span, lhs, rhs
+                    ));
+                    block.unit()
                 } else {
-                    unpack!(block = this.as_rvalue(block, rhs))
-                };
-
-                let lhs = unpack!(block = this.as_lvalue(block, lhs));
-                unpack!(block = this.build_drop(block, lhs_span, lhs.clone(), lhs_ty));
-                this.cfg.push_assign(block, scope_id, expr_span, &lhs, rhs);
-                block.unit()
+                    let rhs = unpack!(block = this.as_rvalue(block, rhs));
+                    let lhs = unpack!(block = this.as_lvalue(block, lhs));
+                    this.cfg.push_assign(block, source_info, &lhs, rhs);
+                    block.unit()
+                }
             }
             ExprKind::AssignOp { op, lhs, rhs } => {
                 // FIXME(#28160) there is an interesting semantics
@@ -67,6 +62,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // only affects weird things like `x += {x += 1; x}`
                 // -- is that equal to `x + (x + 1)` or `2*(x+1)`?
 
+                let lhs = this.hir.mirror(lhs);
+                let lhs_ty = lhs.ty;
+
                 // As above, RTL.
                 let rhs = unpack!(block = this.as_operand(block, rhs));
                 let lhs = unpack!(block = this.as_lvalue(block, lhs));
@@ -74,10 +72,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // we don't have to drop prior contents or anything
                 // because AssignOp is only legal for Copy types
                 // (overloaded ops should be desugared into a call).
-                this.cfg.push_assign(block, scope_id, expr_span, &lhs,
-                                     Rvalue::BinaryOp(op,
-                                                      Operand::Consume(lhs.clone()),
-                                                      rhs));
+                let result = unpack!(block = this.build_binary_op(block, op, expr_span, lhs_ty,
+                                                  Operand::Consume(lhs.clone()), rhs));
+                this.cfg.push_assign(block, source_info, &lhs, result);
 
                 block.unit()
             }
@@ -95,8 +92,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 block = match value {
                     Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
                     None => {
-                        this.cfg.push_assign_unit(block, scope_id,
-                                                  expr_span, &Lvalue::ReturnPointer);
+                        this.cfg.push_assign_unit(block, source_info, &Lvalue::ReturnPointer);
                         block
                     }
                 };
@@ -106,7 +102,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 this.cfg.start_new_block().unit()
             }
             _ => {
-                let expr_span = expr.span;
                 let expr_ty = expr.ty;
                 let temp = this.temp(expr.ty.clone());
                 unpack!(block = this.into(&temp, block, expr));
index c1a0e1f9a6900b622122e2b153cce60180a30eed..a94adafa802138f1c3e7409cb6da3aad36bf9028 100644 (file)
 
 use build::{BlockAnd, BlockAndExtension, Builder};
 use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::bitvec::BitVector;
 use rustc::middle::const_val::ConstVal;
 use rustc::ty::{AdtDef, Ty};
 use rustc::mir::repr::*;
 use hair::*;
 use syntax::ast::{Name, NodeId};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 // helper functions, broken out by category:
 mod simplify;
@@ -43,21 +44,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         .collect(),
         };
 
-        // Get the body expressions and their scopes, while declaring bindings.
-        let arm_bodies: Vec<_> = arms.iter().enumerate().map(|(i, arm)| {
-            // Assume that all expressions are wrapped in Scope.
+        // Get the arm bodies and their scopes, while declaring bindings.
+        let arm_bodies: Vec<_> = arms.iter().map(|arm| {
             let body = self.hir.mirror(arm.body.clone());
-            match body.kind {
-                ExprKind::Scope { extent, value } => {
-                    let scope_id = self.push_scope(extent, arm_blocks.blocks[i]);
-                    self.declare_bindings(scope_id, &arm.patterns[0]);
-                    (extent, self.scopes.pop().unwrap(), value)
-                }
-                _ => {
-                    span_bug!(body.span, "arm body is not wrapped in Scope {:?}",
-                              body.kind);
-                }
-            }
+            let scope = self.declare_bindings(None, body.span, &arm.patterns[0]);
+            (body, scope.unwrap_or(self.visibility_scope))
         }).collect();
 
         // assemble a list of candidates: there is one candidate per
@@ -87,74 +78,66 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         // branch to the appropriate arm block
         let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
 
-        // because all matches are exhaustive, in principle we expect
-        // an empty vector to be returned here, but the algorithm is
-        // not entirely precise
         if !otherwise.is_empty() {
-            let join_block = self.join_otherwise_blocks(span, otherwise);
-            self.panic(join_block, "something about matches algorithm not being precise", span);
+            // All matches are exhaustive. However, because some matches
+            // only have exponentially-large exhaustive decision trees, we
+            // sometimes generate an inexhaustive decision tree.
+            //
+            // In that case, the inexhaustive tips of the decision tree
+            // can't be reached - terminate them with an `unreachable`.
+            let source_info = self.source_info(span);
+
+            let mut otherwise = otherwise;
+            otherwise.sort();
+            otherwise.dedup(); // variant switches can introduce duplicate target blocks
+            for block in otherwise {
+                self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
+            }
         }
 
         // all the arm blocks will rejoin here
         let end_block = self.cfg.start_new_block();
 
-        let scope_id = self.innermost_scope_id();
-        for (arm_index, (extent, scope, body)) in arm_bodies.into_iter().enumerate() {
+        let outer_source_info = self.source_info(span);
+        for (arm_index, (body, visibility_scope)) in arm_bodies.into_iter().enumerate() {
             let mut arm_block = arm_blocks.blocks[arm_index];
-            // Re-enter the scope we created the bindings in.
-            self.scopes.push(scope);
+            // Re-enter the visibility scope we created the bindings in.
+            self.visibility_scope = visibility_scope;
             unpack!(arm_block = self.into(destination, arm_block, body));
-            unpack!(arm_block = self.pop_scope(extent, arm_block));
-            self.cfg.terminate(arm_block,
-                               scope_id,
-                               span,
+            self.cfg.terminate(arm_block, outer_source_info,
                                TerminatorKind::Goto { target: end_block });
         }
+        self.visibility_scope = outer_source_info.scope;
 
         end_block.unit()
     }
 
     pub fn expr_into_pattern(&mut self,
                              mut block: BasicBlock,
-                             var_scope_id: ScopeId, // lifetime of vars
                              irrefutable_pat: Pattern<'tcx>,
                              initializer: ExprRef<'tcx>)
                              -> BlockAnd<()> {
         // optimize the case of `let x = ...`
         match *irrefutable_pat.kind {
-            PatternKind::Binding { mutability,
-                                   name,
-                                   mode: BindingMode::ByValue,
+            PatternKind::Binding { mode: BindingMode::ByValue,
                                    var,
-                                   ty,
-                                   subpattern: None } => {
-                let index = self.declare_binding(var_scope_id,
-                                                 mutability,
-                                                 name,
-                                                 var,
-                                                 ty,
-                                                 irrefutable_pat.span);
-                let lvalue = Lvalue::Var(index);
+                                   subpattern: None, .. } => {
+                let lvalue = Lvalue::Var(self.var_indices[&var]);
                 return self.into(&lvalue, block, initializer);
             }
             _ => {}
         }
         let lvalue = unpack!(block = self.as_lvalue(block, initializer));
         self.lvalue_into_pattern(block,
-                                 var_scope_id,
                                  irrefutable_pat,
                                  &lvalue)
     }
 
     pub fn lvalue_into_pattern(&mut self,
                                mut block: BasicBlock,
-                               var_scope_id: ScopeId,
                                irrefutable_pat: Pattern<'tcx>,
                                initializer: &Lvalue<'tcx>)
                                -> BlockAnd<()> {
-        // first, creating the bindings
-        self.declare_bindings(var_scope_id, &irrefutable_pat);
-
         // create a dummy candidate
         let mut candidate = Candidate {
             span: irrefutable_pat.span,
@@ -181,32 +164,47 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         block.unit()
     }
 
-    pub fn declare_bindings(&mut self, var_scope_id: ScopeId, pattern: &Pattern<'tcx>) {
+    /// Declares the bindings of the given pattern and returns the visibility scope
+    /// for the bindings in this patterns, if such a scope had to be created.
+    /// NOTE: Declaring the bindings should always be done in their drop scope.
+    pub fn declare_bindings(&mut self,
+                            mut var_scope: Option<VisibilityScope>,
+                            scope_span: Span,
+                            pattern: &Pattern<'tcx>)
+                            -> Option<VisibilityScope> {
         match *pattern.kind {
             PatternKind::Binding { mutability, name, mode: _, var, ty, ref subpattern } => {
-                self.declare_binding(var_scope_id, mutability, name, var, ty, pattern.span);
+                if var_scope.is_none() {
+                    var_scope = Some(self.new_visibility_scope(scope_span));
+                }
+                let source_info = SourceInfo {
+                    span: pattern.span,
+                    scope: var_scope.unwrap()
+                };
+                self.declare_binding(source_info, mutability, name, var, ty);
                 if let Some(subpattern) = subpattern.as_ref() {
-                    self.declare_bindings(var_scope_id, subpattern);
+                    var_scope = self.declare_bindings(var_scope, scope_span, subpattern);
                 }
             }
             PatternKind::Array { ref prefix, ref slice, ref suffix } |
             PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
                 for subpattern in prefix.iter().chain(slice).chain(suffix) {
-                    self.declare_bindings(var_scope_id, subpattern);
+                    var_scope = self.declare_bindings(var_scope, scope_span, subpattern);
                 }
             }
             PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {
             }
             PatternKind::Deref { ref subpattern } => {
-                self.declare_bindings(var_scope_id, subpattern);
+                var_scope = self.declare_bindings(var_scope, scope_span, subpattern);
             }
             PatternKind::Leaf { ref subpatterns } |
             PatternKind::Variant { ref subpatterns, .. } => {
                 for subpattern in subpatterns {
-                    self.declare_bindings(var_scope_id, &subpattern.pattern);
+                    var_scope = self.declare_bindings(var_scope, scope_span, &subpattern.pattern);
                 }
             }
         }
+        var_scope
     }
 }
 
@@ -266,6 +264,7 @@ enum TestKind<'tcx> {
     // test the branches of enum
     Switch {
         adt_def: AdtDef<'tcx>,
+        variants: BitVector,
     },
 
     // test the branches of enum
@@ -391,18 +390,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
     fn join_otherwise_blocks(&mut self,
                              span: Span,
-                             otherwise: Vec<BasicBlock>)
+                             mut otherwise: Vec<BasicBlock>)
                              -> BasicBlock
     {
-        let scope_id = self.innermost_scope_id();
+        let source_info = self.source_info(span);
+        otherwise.sort();
+        otherwise.dedup(); // variant switches can introduce duplicate target blocks
         if otherwise.len() == 1 {
             otherwise[0]
         } else {
             let join_block = self.cfg.start_new_block();
             for block in otherwise {
-                self.cfg.terminate(block,
-                                   scope_id,
-                                   span,
+                self.cfg.terminate(block, source_info,
                                    TerminatorKind::Goto { target: join_block });
             }
             join_block
@@ -439,42 +438,87 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// simpler (and, in fact, irrefutable).
     ///
     /// But there may also be candidates that the test just doesn't
-    /// apply to. For example, consider the case of #29740:
+    /// apply to. The classical example involves wildcards:
     ///
     /// ```rust,ignore
+    /// match (x, y, z) {
+    ///     (true, _, true) => true,    // (0)
+    ///     (_, true, _) => true,       // (1)
+    ///     (false, false, _) => false, // (2)
+    ///     (true, _, false) => false,  // (3)
+    /// }
+    /// ```
+    ///
+    /// In that case, after we test on `x`, there are 2 overlapping candidate
+    /// sets:
+    ///
+    /// - If the outcome is that `x` is true, candidates 0, 1, and 3
+    /// - If the outcome is that `x` is false, candidates 1 and 2
+    ///
+    /// Here, the traditional "decision tree" method would generate 2
+    /// separate code-paths for the 2 separate cases.
+    ///
+    /// In some cases, this duplication can create an exponential amount of
+    /// code. This is most easily seen by noticing that this method terminates
+    /// with precisely the reachable arms being reachable - but that problem
+    /// is trivially NP-complete:
+    ///
+    /// ```rust
+    ///     match (var0, var1, var2, var3, ..) {
+    ///         (true, _, _, false, true, ...) => false,
+    ///         (_, true, true, false, _, ...) => false,
+    ///         (false, _, false, false, _, ...) => false,
+    ///         ...
+    ///         _ => true
+    ///     }
+    /// ```
+    ///
+    /// Here the last arm is reachable only if there is an assignment to
+    /// the variables that does not match any of the literals. Therefore,
+    /// compilation would take an exponential amount of time in some cases.
+    ///
+    /// That kind of exponential worst-case might not occur in practice, but
+    /// our simplistic treatment of constants and guards would make it occur
+    /// in very common situations - for example #29740:
+    ///
+    /// ```rust
     /// match x {
-    ///     "foo" => ...,
-    ///     "bar" => ...,
-    ///     "baz" => ...,
-    ///     _ => ...,
+    ///     "foo" if foo_guard => ...,
+    ///     "bar" if bar_guard => ...,
+    ///     "baz" if baz_guard => ...,
+    ///     ...
     /// }
     /// ```
     ///
-    /// Here the match-pair we are testing will be `x @ "foo"`, and we
-    /// will generate an `Eq` test. Because `"bar"` and `"baz"` are different
-    /// constants, we will decide that these later candidates are just not
-    /// informed by the eq test. So we'll wind up with three candidate sets:
+    /// Here we first test the match-pair `x @ "foo"`, which is an `Eq` test.
+    ///
+    /// It might seem that we would end up with 2 disjoint candidate
+    /// sets, consisting of the first candidate or the other 3, but our
+    /// algorithm doesn't reason about "foo" being distinct from the other
+    /// constants; it considers the latter arms to potentially match after
+    /// both outcomes, which obviously leads to an exponential amount
+    /// of tests.
     ///
-    /// - If outcome is that `x == "foo"` (one candidate, derived from `x @ "foo"`)
-    /// - If outcome is that `x != "foo"` (empty list of candidates)
-    /// - Otherwise (three candidates, `x @ "bar"`, `x @ "baz"`, `x @
-    ///   _`). Here we have the invariant that everything in the
-    ///   otherwise list is of **lower priority** than the stuff in the
-    ///   other lists.
+    /// To avoid these kinds of problems, our algorithm tries to ensure
+    /// the amount of generated tests is linear. When we do a k-way test,
+    /// we return an additional "unmatched" set alongside the obvious `k`
+    /// sets. When we encounter a candidate that would be present in more
+    /// than one of the sets, we put it and all candidates below it into the
+    /// "unmatched" set. This ensures these `k+1` sets are disjoint.
     ///
-    /// So we'll compile the test. For each outcome of the test, we
-    /// recursively call `match_candidates` with the corresponding set
-    /// of candidates. But note that this set is now inexhaustive: for
-    /// example, in the case where the test returns false, there are
-    /// NO candidates, even though there is stll a value to be
-    /// matched. So we'll collect the return values from
-    /// `match_candidates`, which are the blocks where control-flow
-    /// goes if none of the candidates matched. At this point, we can
-    /// continue with the "otherwise" list.
+    /// After we perform our test, we branch into the appropriate candidate
+    /// set and recurse with `match_candidates`. These sub-matches are
+    /// obviously inexhaustive - as we discarded our otherwise set - so
+    /// we set their continuation to do `match_candidates` on the
+    /// "unmatched" set (which is again inexhaustive).
     ///
     /// If you apply this to the above test, you basically wind up
     /// with an if-else-if chain, testing each candidate in turn,
     /// which is precisely what we want.
+    ///
+    /// In addition to avoiding exponential-time blowups, this algorithm
+    /// also has nice property that each guard and arm is only generated
+    /// once.
     fn test_candidates<'pat>(&mut self,
                              span: Span,
                              arm_blocks: &mut ArmBlocks,
@@ -502,6 +546,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     }
                 }
             }
+            TestKind::Switch { adt_def: _, ref mut variants} => {
+                for candidate in candidates.iter() {
+                    if !self.add_variants_to_switch(&match_pair.lvalue,
+                                                    candidate,
+                                                    variants) {
+                        break;
+                    }
+                }
+            }
             _ => { }
         }
 
@@ -525,6 +578,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                                           &mut target_candidates))
                       .count();
         assert!(tested_candidates > 0); // at least the last candidate ought to be tested
+        debug!("tested_candidates: {}", tested_candidates);
+        debug!("untested_candidates: {}", candidates.len() - tested_candidates);
 
         // For each outcome of test, process the candidates that still
         // apply. Collect a list of blocks where control flow will
@@ -570,24 +625,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
         let arm_block = arm_blocks.blocks[candidate.arm_index];
 
-        let scope_id = self.innermost_scope_id();
         if let Some(guard) = candidate.guard {
             // the block to branch to if the guard fails; if there is no
             // guard, this block is simply unreachable
             let guard = self.hir.mirror(guard);
-            let guard_span = guard.span;
+            let source_info = self.source_info(guard.span);
             let cond = unpack!(block = self.as_operand(block, guard));
             let otherwise = self.cfg.start_new_block();
-            self.cfg.terminate(block,
-                               scope_id,
-                               guard_span,
+            self.cfg.terminate(block, source_info,
                                TerminatorKind::If { cond: cond,
                                                     targets: (arm_block, otherwise)});
             Some(otherwise)
         } else {
-            self.cfg.terminate(block,
-                               scope_id,
-                               candidate.span,
+            let source_info = self.source_info(candidate.span);
+            self.cfg.terminate(block, source_info,
                                TerminatorKind::Goto { target: arm_block });
             None
         }
@@ -613,39 +664,35 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     Rvalue::Ref(region, borrow_kind, binding.source),
             };
 
-            let scope_id = self.innermost_scope_id();
-            self.cfg.push_assign(block, scope_id, binding.span,
+            let source_info = self.source_info(binding.span);
+            self.cfg.push_assign(block, source_info,
                                  &Lvalue::Var(var_index), rvalue);
         }
     }
 
     fn declare_binding(&mut self,
-                       var_scope_id: ScopeId,
+                       source_info: SourceInfo,
                        mutability: Mutability,
                        name: Name,
                        var_id: NodeId,
-                       var_ty: Ty<'tcx>,
-                       span: Span)
-                       -> u32
+                       var_ty: Ty<'tcx>)
+                       -> Var
     {
-        debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_scope_id={:?}, span={:?})",
-               var_id, name, var_ty, var_scope_id, span);
+        debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, source_info={:?})",
+               var_id, name, var_ty, source_info);
 
-        let index = self.var_decls.len();
-        self.var_decls.push(VarDecl::<'tcx> {
-            scope: var_scope_id,
+        let var = self.var_decls.push(VarDecl::<'tcx> {
+            source_info: source_info,
             mutability: mutability,
             name: name,
             ty: var_ty.clone(),
-            span: span,
         });
-        let index = index as u32;
-        let extent = self.scope_auxiliary[var_scope_id].extent;
-        self.schedule_drop(span, extent, &Lvalue::Var(index), var_ty);
-        self.var_indices.insert(var_id, index);
+        let extent = self.extent_of_innermost_scope();
+        self.schedule_drop(source_info.span, extent, &Lvalue::Var(var), var_ty);
+        self.var_indices.insert(var_id, var);
 
-        debug!("declare_binding: index={:?}", index);
+        debug!("declare_binding: var={:?}", var);
 
-        index
+        var
     }
 }
index c707bb8a27b6f53983e7eececd0c78bfc93cbf0d..8392248e3f22e2088ffe6e99e4e38cb4f47bbcbd 100644 (file)
@@ -31,7 +31,7 @@ use std::mem;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn simplify_candidate<'pat>(&mut self,
-                                    mut block: BasicBlock,
+                                    block: BasicBlock,
                                     candidate: &mut Candidate<'pat, 'tcx>)
                                     -> BlockAnd<()> {
         // repeatedly simplify match pairs until fixed point is reached
@@ -39,10 +39,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             let match_pairs = mem::replace(&mut candidate.match_pairs, vec![]);
             let mut progress = match_pairs.len(); // count how many were simplified
             for match_pair in match_pairs {
-                match self.simplify_match_pair(block, match_pair, candidate) {
-                    Ok(b) => {
-                        block = b;
-                    }
+                match self.simplify_match_pair(match_pair, candidate) {
+                    Ok(()) => {}
                     Err(match_pair) => {
                         candidate.match_pairs.push(match_pair);
                         progress -= 1; // this one was not simplified
@@ -61,14 +59,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// possible, Err is returned and no changes are made to
     /// candidate.
     fn simplify_match_pair<'pat>(&mut self,
-                                 mut block: BasicBlock,
                                  match_pair: MatchPair<'pat, 'tcx>,
                                  candidate: &mut Candidate<'pat, 'tcx>)
-                                 -> Result<BasicBlock, MatchPair<'pat, 'tcx>> {
+                                 -> Result<(), MatchPair<'pat, 'tcx>> {
         match *match_pair.pattern.kind {
             PatternKind::Wild => {
                 // nothing left to do
-                Ok(block)
+                Ok(())
             }
 
             PatternKind::Binding { name, mutability, mode, var, ty, ref subpattern } => {
@@ -87,7 +84,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     candidate.match_pairs.push(MatchPair::new(match_pair.lvalue, subpattern));
                 }
 
-                Ok(block)
+                Ok(())
             }
 
             PatternKind::Constant { .. } => {
@@ -96,37 +93,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
 
             PatternKind::Range { .. } |
-            PatternKind::Variant { .. } => {
-                // cannot simplify, test is required
-                Err(match_pair)
-            }
-
-            PatternKind::Slice { .. } if !match_pair.slice_len_checked => {
+            PatternKind::Variant { .. } |
+            PatternKind::Slice { .. } => {
                 Err(match_pair)
             }
 
-            PatternKind::Array { ref prefix, ref slice, ref suffix } |
-            PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
-                unpack!(block = self.prefix_suffix_slice(&mut candidate.match_pairs,
-                                                         block,
-                                                         match_pair.lvalue.clone(),
-                                                         prefix,
-                                                         slice.as_ref(),
-                                                         suffix));
-                Ok(block)
+            PatternKind::Array { ref prefix, ref slice, ref suffix } => {
+                self.prefix_slice_suffix(&mut candidate.match_pairs,
+                                         &match_pair.lvalue,
+                                         prefix,
+                                         slice.as_ref(),
+                                         suffix);
+                Ok(())
             }
 
             PatternKind::Leaf { ref subpatterns } => {
                 // tuple struct, match subpats (if any)
                 candidate.match_pairs
                          .extend(self.field_match_pairs(match_pair.lvalue, subpatterns));
-                Ok(block)
+                Ok(())
             }
 
             PatternKind::Deref { ref subpattern } => {
                 let lvalue = match_pair.lvalue.deref();
                 candidate.match_pairs.push(MatchPair::new(lvalue, subpattern));
-                Ok(block)
+                Ok(())
             }
         }
     }
index e53584a3f8b11ee42b733572caaa30f1b1da8631..8c9ed53c8ab4de2794b1c5ce499af946d38d29f7 100644 (file)
@@ -19,10 +19,12 @@ use build::Builder;
 use build::matches::{Candidate, MatchPair, Test, TestKind};
 use hair::*;
 use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::bitvec::BitVector;
 use rustc::middle::const_val::ConstVal;
 use rustc::ty::{self, Ty};
 use rustc::mir::repr::*;
-use syntax::codemap::Span;
+use syntax_pos::Span;
+use std::cmp::Ordering;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// Identifies what test is needed to decide if `match_pair` is applicable.
@@ -33,7 +35,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             PatternKind::Variant { ref adt_def, variant_index: _, subpatterns: _ } => {
                 Test {
                     span: match_pair.pattern.span,
-                    kind: TestKind::Switch { adt_def: adt_def.clone() },
+                    kind: TestKind::Switch {
+                        adt_def: adt_def.clone(),
+                        variants: BitVector::new(self.hir.num_variants(adt_def)),
+                    },
                 }
             }
 
@@ -125,9 +130,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                        });
                 true
             }
-
+            PatternKind::Variant { .. } => {
+                panic!("you should have called add_variants_to_switch instead!");
+            }
             PatternKind::Range { .. } |
-            PatternKind::Variant { .. } |
             PatternKind::Slice { .. } |
             PatternKind::Array { .. } |
             PatternKind::Wild |
@@ -140,20 +146,55 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         }
     }
 
+    pub fn add_variants_to_switch<'pat>(&mut self,
+                                        test_lvalue: &Lvalue<'tcx>,
+                                        candidate: &Candidate<'pat, 'tcx>,
+                                        variants: &mut BitVector)
+                                        -> bool
+    {
+        let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) {
+            Some(match_pair) => match_pair,
+            _ => { return false; }
+        };
+
+        match *match_pair.pattern.kind {
+            PatternKind::Variant { adt_def: _ , variant_index,  .. } => {
+                // We have a pattern testing for variant `variant_index`
+                // set the corresponding index to true
+                variants.insert(variant_index);
+                true
+            }
+            _ => {
+                // don't know how to add these patterns to a switch
+                false
+            }
+        }
+    }
+
     /// Generates the code to perform a test.
     pub fn perform_test(&mut self,
                         block: BasicBlock,
                         lvalue: &Lvalue<'tcx>,
                         test: &Test<'tcx>)
                         -> Vec<BasicBlock> {
-        let scope_id = self.innermost_scope_id();
+        let source_info = self.source_info(test.span);
         match test.kind {
-            TestKind::Switch { adt_def } => {
+            TestKind::Switch { adt_def, ref variants } => {
                 let num_enum_variants = self.hir.num_variants(adt_def);
-                let target_blocks: Vec<_> =
-                    (0..num_enum_variants).map(|_| self.cfg.start_new_block())
-                                          .collect();
-                self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Switch {
+                let mut otherwise_block = None;
+                let target_blocks: Vec<_> = (0..num_enum_variants).map(|i| {
+                    if variants.contains(i) {
+                        self.cfg.start_new_block()
+                    } else {
+                        if otherwise_block.is_none() {
+                            otherwise_block = Some(self.cfg.start_new_block());
+                        }
+                        otherwise_block.unwrap()
+                    }
+                }).collect();
+                debug!("num_enum_variants: {}, num tested variants: {}, variants: {:?}",
+                       num_enum_variants, variants.iter().count(), variants);
+                self.cfg.terminate(block, source_info, TerminatorKind::Switch {
                     discr: lvalue.clone(),
                     adt_def: adt_def,
                     targets: target_blocks.clone()
@@ -162,21 +203,50 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
 
             TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
-                let otherwise = self.cfg.start_new_block();
-                let targets: Vec<_> =
-                    options.iter()
-                           .map(|_| self.cfg.start_new_block())
-                           .chain(Some(otherwise))
-                           .collect();
-                self.cfg.terminate(block,
-                                   scope_id,
-                                   test.span,
-                                   TerminatorKind::SwitchInt {
-                                       discr: lvalue.clone(),
-                                       switch_ty: switch_ty,
-                                       values: options.clone(),
-                                       targets: targets.clone(),
-                                   });
+                let (targets, term) = match switch_ty.sty {
+                    // If we're matching on boolean we can
+                    // use the If TerminatorKind instead
+                    ty::TyBool => {
+                        assert!(options.len() > 0 && options.len() <= 2);
+
+                        let (true_bb, else_bb) =
+                            (self.cfg.start_new_block(),
+                             self.cfg.start_new_block());
+
+                        let targets = match &options[0] {
+                            &ConstVal::Bool(true) => vec![true_bb, else_bb],
+                            &ConstVal::Bool(false) => vec![else_bb, true_bb],
+                            v => span_bug!(test.span, "expected boolean value but got {:?}", v)
+                        };
+
+                        (targets,
+                         TerminatorKind::If {
+                             cond: Operand::Consume(lvalue.clone()),
+                             targets: (true_bb, else_bb)
+                         })
+
+                    }
+                    _ => {
+                        // The switch may be inexhaustive so we
+                        // add a catch all block
+                        let otherwise = self.cfg.start_new_block();
+                        let targets: Vec<_> =
+                            options.iter()
+                                   .map(|_| self.cfg.start_new_block())
+                                   .chain(Some(otherwise))
+                                   .collect();
+
+                        (targets.clone(),
+                         TerminatorKind::SwitchInt {
+                             discr: lvalue.clone(),
+                             switch_ty: switch_ty,
+                             values: options.clone(),
+                             targets: targets
+                         })
+                    }
+                };
+
+                self.cfg.terminate(block, source_info, term);
                 targets
             }
 
@@ -193,7 +263,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         if let ty::TyArray(_, _) = mt.ty.sty {
                             ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8));
                             let val_slice = self.temp(ty);
-                            self.cfg.push_assign(block, scope_id, test.span, &val_slice,
+                            self.cfg.push_assign(block, source_info, &val_slice,
                                                  Rvalue::Cast(CastKind::Unsize, val, ty));
                             val = Operand::Consume(val_slice);
                         }
@@ -208,7 +278,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     });
 
                     let slice = self.temp(ty);
-                    self.cfg.push_assign(block, scope_id, test.span, &slice,
+                    self.cfg.push_assign(block, source_info, &slice,
                                          Rvalue::Cast(CastKind::Unsize, array, ty));
                     Operand::Consume(slice)
                 } else {
@@ -229,7 +299,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     let eq_result = self.temp(bool_ty);
                     let eq_block = self.cfg.start_new_block();
                     let cleanup = self.diverge_cleanup();
-                    self.cfg.terminate(block, scope_id, test.span, TerminatorKind::Call {
+                    self.cfg.terminate(block, source_info, TerminatorKind::Call {
                         func: Operand::Constant(Constant {
                             span: test.span,
                             ty: mty,
@@ -242,7 +312,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                     // check the result
                     let block = self.cfg.start_new_block();
-                    self.cfg.terminate(eq_block, scope_id, test.span, TerminatorKind::If {
+                    self.cfg.terminate(eq_block, source_info, TerminatorKind::If {
                         cond: Operand::Consume(eq_result),
                         targets: (block, fail),
                     });
@@ -272,17 +342,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty));
 
                 // actual = len(lvalue)
-                self.cfg.push_assign(block, scope_id, test.span,
+                self.cfg.push_assign(block, source_info,
                                      &actual, Rvalue::Len(lvalue.clone()));
 
                 // expected = <N>
-                let expected = self.push_usize(block, scope_id, test.span, len);
+                let expected = self.push_usize(block, source_info, len);
 
                 // result = actual == expected OR result = actual < expected
-                self.cfg.push_assign(block,
-                                     scope_id,
-                                     test.span,
-                                     &result,
+                self.cfg.push_assign(block, source_info, &result,
                                      Rvalue::BinaryOp(op,
                                                       Operand::Consume(actual),
                                                       Operand::Consume(expected)));
@@ -290,7 +357,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // branch based on result
                 let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
                                                  self.cfg.start_new_block()];
-                self.cfg.terminate(block, scope_id, test.span, TerminatorKind::If {
+                self.cfg.terminate(block, source_info, TerminatorKind::If {
                     cond: Operand::Consume(result),
                     targets: (target_blocks[0], target_blocks[1])
                 });
@@ -311,13 +378,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let result = self.temp(bool_ty);
 
         // result = op(left, right)
-        let scope_id = self.innermost_scope_id();
-        self.cfg.push_assign(block, scope_id, span, &result,
+        let source_info = self.source_info(span);
+        self.cfg.push_assign(block, source_info, &result,
                              Rvalue::BinaryOp(op, left, right));
 
         // branch based on result
         let target_block = self.cfg.start_new_block();
-        self.cfg.terminate(block, scope_id, span, TerminatorKind::If {
+        self.cfg.terminate(block, source_info, TerminatorKind::If {
             cond: Operand::Consume(result),
             targets: (target_block, fail_block)
         });
@@ -380,69 +447,118 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
         };
 
-        match test.kind {
+        match (&test.kind, &*match_pair.pattern.kind) {
             // If we are performing a variant switch, then this
             // informs variant patterns, but nothing else.
-            TestKind::Switch { adt_def: tested_adt_def } => {
-                match *match_pair.pattern.kind {
-                    PatternKind::Variant { adt_def, variant_index, ref subpatterns } => {
-                        assert_eq!(adt_def, tested_adt_def);
-                        let new_candidate =
-                            self.candidate_after_variant_switch(match_pair_index,
-                                                                adt_def,
-                                                                variant_index,
-                                                                subpatterns,
-                                                                candidate);
-                        resulting_candidates[variant_index].push(new_candidate);
-                        true
-                    }
-                    _ => {
-                        false
-                    }
-                }
+            (&TestKind::Switch { adt_def: tested_adt_def, .. },
+             &PatternKind::Variant { adt_def, variant_index, ref subpatterns }) => {
+                assert_eq!(adt_def, tested_adt_def);
+                let new_candidate =
+                    self.candidate_after_variant_switch(match_pair_index,
+                                                        adt_def,
+                                                        variant_index,
+                                                        subpatterns,
+                                                        candidate);
+                resulting_candidates[variant_index].push(new_candidate);
+                true
             }
+            (&TestKind::Switch { .. }, _) => false,
 
             // If we are performing a switch over integers, then this informs integer
             // equality, but nothing else.
             //
-            // FIXME(#29623) we could use TestKind::Range to rule
+            // FIXME(#29623) we could use PatternKind::Range to rule
             // things out here, in some cases.
-            TestKind::SwitchInt { switch_ty: _, options: _, ref indices } => {
-                match *match_pair.pattern.kind {
-                    PatternKind::Constant { ref value }
-                    if is_switch_ty(match_pair.pattern.ty) => {
-                        let index = indices[value];
-                        let new_candidate = self.candidate_without_match_pair(match_pair_index,
-                                                                              candidate);
-                        resulting_candidates[index].push(new_candidate);
+            (&TestKind::SwitchInt { switch_ty: _, options: _, ref indices },
+             &PatternKind::Constant { ref value })
+            if is_switch_ty(match_pair.pattern.ty) => {
+                let index = indices[value];
+                let new_candidate = self.candidate_without_match_pair(match_pair_index,
+                                                                      candidate);
+                resulting_candidates[index].push(new_candidate);
+                true
+            }
+            (&TestKind::SwitchInt { .. }, _) => false,
+
+
+            (&TestKind::Len { len: test_len, op: BinOp::Eq },
+             &PatternKind::Slice { ref prefix, ref slice, ref suffix }) => {
+                let pat_len = (prefix.len() + suffix.len()) as u64;
+                match (test_len.cmp(&pat_len), slice) {
+                    (Ordering::Equal, &None) => {
+                        // on true, min_len = len = $actual_length,
+                        // on false, len != $actual_length
+                        resulting_candidates[0].push(
+                            self.candidate_after_slice_test(match_pair_index,
+                                                            candidate,
+                                                            prefix,
+                                                            slice.as_ref(),
+                                                            suffix)
+                        );
                         true
                     }
-                    _ => {
+                    (Ordering::Less, _) => {
+                        // test_len < pat_len. If $actual_len = test_len,
+                        // then $actual_len < pat_len and we don't have
+                        // enough elements.
+                        resulting_candidates[1].push(candidate.clone());
+                        true
+                    }
+                    (Ordering::Equal, &Some(_)) | (Ordering::Greater, &Some(_)) => {
+                        // This can match both if $actual_len = test_len >= pat_len,
+                        // and if $actual_len > test_len. We can't advance.
                         false
                     }
+                    (Ordering::Greater, &None) => {
+                        // test_len != pat_len, so if $actual_len = test_len, then
+                        // $actual_len != pat_len.
+                        resulting_candidates[1].push(candidate.clone());
+                        true
+                    }
                 }
             }
 
-            // If we are performing a length check, then this
-            // informs slice patterns, but nothing else.
-            TestKind::Len { .. } => {
-                let pattern_test = self.test(&match_pair);
-                match *match_pair.pattern.kind {
-                    PatternKind::Slice { .. } if pattern_test.kind == test.kind => {
-                        let mut new_candidate = candidate.clone();
-
-                        // Set up the MatchKind to simplify this like an array.
-                        new_candidate.match_pairs[match_pair_index]
-                                     .slice_len_checked = true;
-                        resulting_candidates[0].push(new_candidate);
+            (&TestKind::Len { len: test_len, op: BinOp::Ge },
+             &PatternKind::Slice { ref prefix, ref slice, ref suffix }) => {
+                // the test is `$actual_len >= test_len`
+                let pat_len = (prefix.len() + suffix.len()) as u64;
+                match (test_len.cmp(&pat_len), slice) {
+                    (Ordering::Equal, &Some(_))  => {
+                        // $actual_len >= test_len = pat_len,
+                        // so we can match.
+                        resulting_candidates[0].push(
+                            self.candidate_after_slice_test(match_pair_index,
+                                                            candidate,
+                                                            prefix,
+                                                            slice.as_ref(),
+                                                            suffix)
+                        );
+                        true
+                    }
+                    (Ordering::Less, _) | (Ordering::Equal, &None) => {
+                        // test_len <= pat_len. If $actual_len < test_len,
+                        // then it is also < pat_len, so the test passing is
+                        // necessary (but insufficient).
+                        resulting_candidates[0].push(candidate.clone());
                         true
                     }
-                    _ => false
+                    (Ordering::Greater, &None) => {
+                        // test_len > pat_len. If $actual_len >= test_len > pat_len,
+                        // then we know we won't have a match.
+                        resulting_candidates[1].push(candidate.clone());
+                        true
+                    }
+                    (Ordering::Greater, &Some(_)) => {
+                        // test_len < pat_len, and is therefore less
+                        // strict. This can still go both ways.
+                        false
+                    }
                 }
             }
 
-            TestKind::Eq { .. } |
-            TestKind::Range { .. } => {
+            (&TestKind::Eq { .. }, _) |
+            (&TestKind::Range { .. }, _) |
+            (&TestKind::Len { .. }, _) => {
                 // These are all binary tests.
                 //
                 // FIXME(#29623) we can be more clever here
@@ -478,6 +594,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         }
     }
 
+    fn candidate_after_slice_test<'pat>(&mut self,
+                                        match_pair_index: usize,
+                                        candidate: &Candidate<'pat, 'tcx>,
+                                        prefix: &'pat [Pattern<'tcx>],
+                                        opt_slice: Option<&'pat Pattern<'tcx>>,
+                                        suffix: &'pat [Pattern<'tcx>])
+                                        -> Candidate<'pat, 'tcx> {
+        let mut new_candidate =
+            self.candidate_without_match_pair(match_pair_index, candidate);
+        self.prefix_slice_suffix(
+            &mut new_candidate.match_pairs,
+            &candidate.match_pairs[match_pair_index].lvalue,
+            prefix,
+            opt_slice,
+            suffix);
+
+        new_candidate
+    }
+
     fn candidate_after_variant_switch<'pat>(&mut self,
                                             match_pair_index: usize,
                                             adt_def: ty::AdtDef<'tcx>,
index 5eb58f7612d744c5b9e38943bd5d1020190751a2..53ebf6fceb5c807e2f8ea006dad651db8f42dd46 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use build::{BlockAnd, BlockAndExtension, Builder};
+use build::Builder;
 use build::matches::MatchPair;
 use hair::*;
 use rustc::mir::repr::*;
@@ -28,64 +28,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                    .collect()
     }
 
-    /// When processing an array/slice pattern like `lv @ [x, y, ..s, z]`,
-    /// this function converts the prefix (`x`, `y`) and suffix (`z`) into
-    /// distinct match pairs:
-    ///
-    /// ```rust,ignore
-    ///     lv[0 of 3] @ x  // see ProjectionElem::ConstantIndex (and its Debug impl)
-    ///     lv[1 of 3] @ y  // to explain the `[x of y]` notation
-    ///     lv[-1 of 3] @ z
-    /// ```
-    ///
-    /// If a slice like `s` is present, then the function also creates
-    /// a temporary like:
-    ///
-    /// ```rust,ignore
-    ///     tmp0 = lv[2..-1] // using the special Rvalue::Slice
-    /// ```
-    ///
-    /// and creates a match pair `tmp0 @ s`
-    pub fn prefix_suffix_slice<'pat>(&mut self,
+    pub fn prefix_slice_suffix<'pat>(&mut self,
                                      match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
-                                     block: BasicBlock,
-                                     lvalue: Lvalue<'tcx>,
+                                     lvalue: &Lvalue<'tcx>,
                                      prefix: &'pat [Pattern<'tcx>],
                                      opt_slice: Option<&'pat Pattern<'tcx>>,
-                                     suffix: &'pat [Pattern<'tcx>])
-                                     -> BlockAnd<()> {
-        // If there is a `..P` pattern, create a temporary `t0` for
-        // the slice and then a match pair `t0 @ P`:
-        if let Some(slice) = opt_slice {
-            let prefix_len = prefix.len();
-            let suffix_len = suffix.len();
-            let rvalue = Rvalue::Slice {
-                input: lvalue.clone(),
-                from_start: prefix_len,
-                from_end: suffix_len,
-            };
-            let temp = self.temp(slice.ty.clone()); // no need to schedule drop, temp is always copy
-            let scope_id = self.innermost_scope_id();
-            self.cfg.push_assign(block, scope_id, slice.span, &temp, rvalue);
-            match_pairs.push(MatchPair::new(temp, slice));
-        }
-
-        self.prefix_suffix(match_pairs, lvalue, prefix, suffix);
-
-        block.unit()
-    }
-
-    /// Helper for `prefix_suffix_slice` which just processes the prefix and suffix.
-    fn prefix_suffix<'pat>(&mut self,
-                           match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
-                           lvalue: Lvalue<'tcx>,
-                           prefix: &'pat [Pattern<'tcx>],
-                           suffix: &'pat [Pattern<'tcx>]) {
+                                     suffix: &'pat [Pattern<'tcx>]) {
         let min_length = prefix.len() + suffix.len();
         assert!(min_length < u32::MAX as usize);
         let min_length = min_length as u32;
 
-        let prefix_pairs: Vec<_> =
+        match_pairs.extend(
             prefix.iter()
                   .enumerate()
                   .map(|(idx, subpattern)| {
@@ -97,9 +50,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                       let lvalue = lvalue.clone().elem(elem);
                       MatchPair::new(lvalue, subpattern)
                   })
-                  .collect();
+        );
+
+        if let Some(subslice_pat) = opt_slice {
+            let subslice = lvalue.clone().elem(ProjectionElem::Subslice {
+                from: prefix.len() as u32,
+                to: suffix.len() as u32
+            });
+            match_pairs.push(MatchPair::new(subslice, subslice_pat));
+        }
 
-        let suffix_pairs: Vec<_> =
+        match_pairs.extend(
             suffix.iter()
                   .rev()
                   .enumerate()
@@ -112,9 +73,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                       let lvalue = lvalue.clone().elem(elem);
                       MatchPair::new(lvalue, subpattern)
                   })
-                  .collect();
-
-        match_pairs.extend(prefix_pairs.into_iter().chain(suffix_pairs));
+        );
     }
 }
 
index 7317c6f9b31336149a0a54acb83206a4a59653d1..79a4cf73041d7c6e6d120018d37e2297d8ac1a6b 100644 (file)
 //! kind of thing.
 
 use build::Builder;
-use rustc::ty::Ty;
+
+use rustc_const_math::{ConstInt, ConstUsize, ConstIsize};
+use rustc::middle::const_val::ConstVal;
+use rustc::ty::{self, Ty};
+
 use rustc::mir::repr::*;
-use std::u32;
-use syntax::codemap::Span;
+use syntax::ast;
+use syntax_pos::Span;
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// Add a new temporary value of type `ty` storing the result of
@@ -24,12 +28,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// NB: **No cleanup is scheduled for this temporary.** You should
     /// call `schedule_drop` once the temporary is initialized.
     pub fn temp(&mut self, ty: Ty<'tcx>) -> Lvalue<'tcx> {
-        let index = self.temp_decls.len();
-        self.temp_decls.push(TempDecl { ty: ty });
-        assert!(index < (u32::MAX) as usize);
-        let lvalue = Lvalue::Temp(index as u32);
+        let temp = self.temp_decls.push(TempDecl { ty: ty });
+        let lvalue = Lvalue::Temp(temp);
         debug!("temp: created temp {:?} with type {:?}",
-               lvalue, self.temp_decls.last().unwrap().ty);
+               lvalue, self.temp_decls[temp].ty);
         lvalue
     }
 
@@ -50,18 +52,63 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         Rvalue::Aggregate(AggregateKind::Tuple, vec![])
     }
 
+    // Returns a zero literal operand for the appropriate type, works for
+    // bool, char and integers.
+    pub fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
+        let literal = match ty.sty {
+            ty::TyBool => {
+                self.hir.false_literal()
+            }
+            ty::TyChar => Literal::Value { value: ConstVal::Char('\0') },
+            ty::TyUint(ity) => {
+                let val = match ity {
+                    ast::UintTy::U8  => ConstInt::U8(0),
+                    ast::UintTy::U16 => ConstInt::U16(0),
+                    ast::UintTy::U32 => ConstInt::U32(0),
+                    ast::UintTy::U64 => ConstInt::U64(0),
+                    ast::UintTy::Us => {
+                        let uint_ty = self.hir.tcx().sess.target.uint_type;
+                        let val = ConstUsize::new(0, uint_ty).unwrap();
+                        ConstInt::Usize(val)
+                    }
+                };
+
+                Literal::Value { value: ConstVal::Integral(val) }
+            }
+            ty::TyInt(ity) => {
+                let val = match ity {
+                    ast::IntTy::I8  => ConstInt::I8(0),
+                    ast::IntTy::I16 => ConstInt::I16(0),
+                    ast::IntTy::I32 => ConstInt::I32(0),
+                    ast::IntTy::I64 => ConstInt::I64(0),
+                    ast::IntTy::Is => {
+                        let int_ty = self.hir.tcx().sess.target.int_type;
+                        let val = ConstIsize::new(0, int_ty).unwrap();
+                        ConstInt::Isize(val)
+                    }
+                };
+
+                Literal::Value { value: ConstVal::Integral(val) }
+            }
+            _ => {
+                span_bug!(span, "Invalid type for zero_literal: `{:?}`", ty)
+            }
+        };
+
+        self.literal_operand(span, ty, literal)
+    }
+
     pub fn push_usize(&mut self,
                       block: BasicBlock,
-                      scope_id: ScopeId,
-                      span: Span,
+                      source_info: SourceInfo,
                       value: u64)
                       -> Lvalue<'tcx> {
         let usize_ty = self.hir.usize_ty();
         let temp = self.temp(usize_ty);
         self.cfg.push_assign_constant(
-            block, scope_id, span, &temp,
+            block, source_info, &temp,
             Constant {
-                span: span,
+                span: source_info.span,
                 ty: self.hir.usize_ty(),
                 literal: self.hir.usize_literal(value),
             });
index d75cf3b7587ea57e3f7e960711a7bba4e79f8316..362e1e26fdf1e4cf79c76208f7c9efbb0abdb20f 100644 (file)
@@ -12,14 +12,16 @@ use hair::cx::Cx;
 use rustc::middle::region::{CodeExtent, CodeExtentData, ROOT_CODE_EXTENT};
 use rustc::ty::{self, Ty};
 use rustc::mir::repr::*;
-use rustc_data_structures::fnv::FnvHashMap;
+use rustc::util::nodemap::NodeMap;
 use rustc::hir;
-use rustc::hir::pat_util::pat_is_binding;
-use std::ops::{Index, IndexMut};
 use syntax::abi::Abi;
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::parse::token::keywords;
+use syntax_pos::Span;
+
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+
+use std::u32;
 
 pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     hir: Cx<'a, 'gcx, 'tcx>,
@@ -36,7 +38,7 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     ///  but these are liable to get out of date once optimization
     ///  begins. They are also hopefully temporary, and will be
     ///  no longer needed when we adopt graph-based regions.
-    scope_auxiliary: ScopeAuxiliaryVec,
+    scope_auxiliary: IndexVec<ScopeId, ScopeAuxiliary>,
 
     /// the current set of loops; see the `scope` module for more
     /// details
@@ -44,11 +46,12 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
     /// the vector of all scopes that we have created thus far;
     /// we track this for debuginfo later
-    scope_datas: Vec<ScopeData>,
+    visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
+    visibility_scope: VisibilityScope,
 
-    var_decls: Vec<VarDecl<'tcx>>,
-    var_indices: FnvHashMap<ast::NodeId, u32>,
-    temp_decls: Vec<TempDecl<'tcx>>,
+    var_decls: IndexVec<Var, VarDecl<'tcx>>,
+    var_indices: NodeMap<Var>,
+    temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
     unit_temp: Option<Lvalue<'tcx>>,
 
     /// cached block with the RESUME terminator; this is created
@@ -59,7 +62,21 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 }
 
 struct CFG<'tcx> {
-    basic_blocks: Vec<BasicBlockData<'tcx>>,
+    basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct ScopeId(u32);
+
+impl Idx for ScopeId {
+    fn new(index: usize) -> ScopeId {
+        assert!(index < (u32::MAX as usize));
+        ScopeId(index as u32)
+    }
+
+    fn index(self) -> usize {
+        self.0 as usize
+    }
 }
 
 /// For each scope, we track the extent (from the HIR) and a
@@ -94,25 +111,7 @@ pub struct Location {
     pub statement_index: usize,
 }
 
-pub struct ScopeAuxiliaryVec {
-    pub vec: Vec<ScopeAuxiliary>
-}
-
-impl Index<ScopeId> for ScopeAuxiliaryVec {
-    type Output = ScopeAuxiliary;
-
-    #[inline]
-    fn index(&self, index: ScopeId) -> &ScopeAuxiliary {
-        &self.vec[index.index()]
-    }
-}
-
-impl IndexMut<ScopeId> for ScopeAuxiliaryVec {
-    #[inline]
-    fn index_mut(&mut self, index: ScopeId) -> &mut ScopeAuxiliary {
-        &mut self.vec[index.index()]
-    }
-}
+pub type ScopeAuxiliaryVec = IndexVec<ScopeId, ScopeAuxiliary>;
 
 ///////////////////////////////////////////////////////////////////////////
 /// The `BlockAnd` "monad" packages up the new basic block along with a
@@ -180,17 +179,16 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
         tcx.region_maps.lookup_code_extent(
             CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body_id });
     let mut block = START_BLOCK;
-    let mut arg_decls = unpack!(block = builder.in_scope(call_site_extent, block,
-                                                         |builder, call_site_scope_id| {
-        let arg_decls = unpack!(block = builder.in_scope(arg_extent, block,
-                                                         |builder, arg_scope_id| {
-            builder.args_and_body(block, return_ty, arguments, arg_scope_id, ast_block)
+    let mut arg_decls = unpack!(block = builder.in_scope(call_site_extent, block, |builder| {
+        let arg_decls = unpack!(block = builder.in_scope(arg_extent, block, |builder| {
+            builder.args_and_body(block, return_ty, arguments, arg_extent, ast_block)
         }));
 
+        let source_info = builder.source_info(span);
         let return_block = builder.return_block();
-        builder.cfg.terminate(block, call_site_scope_id, span,
+        builder.cfg.terminate(block, source_info,
                               TerminatorKind::Goto { target: return_block });
-        builder.cfg.terminate(return_block, call_site_scope_id, span,
+        builder.cfg.terminate(return_block, source_info,
                               TerminatorKind::Return);
         return_block.and(arg_decls)
     }));
@@ -199,8 +197,8 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
     match tcx.node_id_to_type(fn_id).sty {
         ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
             // RustCall pseudo-ABI untuples the last argument.
-            if let Some(arg_decl) = arg_decls.last_mut() {
-                arg_decl.spread = true;
+            if let Some(last_arg) = arg_decls.last() {
+                arg_decls[last_arg].spread = true;
             }
         }
         _ => {}
@@ -221,7 +219,7 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
                 by_ref: by_ref
             };
             if let Some(hir::map::NodeLocal(pat)) = tcx.map.find(fv.def.var_id()) {
-                if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
+                if let hir::PatKind::Binding(_, ref ident, _) = pat.node {
                     decl.debug_name = ident.node;
                 }
             }
@@ -242,49 +240,53 @@ pub fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
 
     let extent = ROOT_CODE_EXTENT;
     let mut block = START_BLOCK;
-    let _ = builder.in_scope(extent, block, |builder, call_site_scope_id| {
+    let _ = builder.in_scope(extent, block, |builder| {
         let expr = builder.hir.mirror(ast_expr);
         unpack!(block = builder.into(&Lvalue::ReturnPointer, block, expr));
 
+        let source_info = builder.source_info(span);
         let return_block = builder.return_block();
-        builder.cfg.terminate(block, call_site_scope_id, span,
+        builder.cfg.terminate(block, source_info,
                               TerminatorKind::Goto { target: return_block });
-        builder.cfg.terminate(return_block, call_site_scope_id, span,
+        builder.cfg.terminate(return_block, source_info,
                               TerminatorKind::Return);
 
         return_block.unit()
     });
 
     let ty = tcx.expr_ty_adjusted(ast_expr);
-    builder.finish(vec![], vec![], ty::FnConverging(ty))
+    builder.finish(vec![], IndexVec::new(), ty::FnConverging(ty))
 }
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     fn new(hir: Cx<'a, 'gcx, 'tcx>, span: Span) -> Builder<'a, 'gcx, 'tcx> {
         let mut builder = Builder {
             hir: hir,
-            cfg: CFG { basic_blocks: vec![] },
+            cfg: CFG { basic_blocks: IndexVec::new() },
             fn_span: span,
             scopes: vec![],
-            scope_datas: vec![],
-            scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
+            visibility_scopes: IndexVec::new(),
+            visibility_scope: ARGUMENT_VISIBILITY_SCOPE,
+            scope_auxiliary: IndexVec::new(),
             loop_scopes: vec![],
-            temp_decls: vec![],
-            var_decls: vec![],
-            var_indices: FnvHashMap(),
+            temp_decls: IndexVec::new(),
+            var_decls: IndexVec::new(),
+            var_indices: NodeMap(),
             unit_temp: None,
             cached_resume_block: None,
             cached_return_block: None
         };
 
         assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
+        assert_eq!(builder.new_visibility_scope(span), ARGUMENT_VISIBILITY_SCOPE);
+        builder.visibility_scopes[ARGUMENT_VISIBILITY_SCOPE].parent_scope = None;
 
         builder
     }
 
     fn finish(self,
               upvar_decls: Vec<UpvarDecl>,
-              arg_decls: Vec<ArgDecl<'tcx>>,
+              arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
               return_ty: ty::FnOutput<'tcx>)
               -> (Mir<'tcx>, ScopeAuxiliaryVec) {
         for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
@@ -293,50 +295,45 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
         }
 
-        (Mir {
-            basic_blocks: self.cfg.basic_blocks,
-            scopes: self.scope_datas,
-            promoted: vec![],
-            var_decls: self.var_decls,
-            arg_decls: arg_decls,
-            temp_decls: self.temp_decls,
-            upvar_decls: upvar_decls,
-            return_ty: return_ty,
-            span: self.fn_span
-        }, self.scope_auxiliary)
+        (Mir::new(self.cfg.basic_blocks,
+                  self.visibility_scopes,
+                  IndexVec::new(),
+                  return_ty,
+                  self.var_decls,
+                  arg_decls,
+                  self.temp_decls,
+                  upvar_decls,
+                  self.fn_span
+        ), self.scope_auxiliary)
     }
 
     fn args_and_body<A>(&mut self,
                         mut block: BasicBlock,
                         return_ty: ty::FnOutput<'tcx>,
                         arguments: A,
-                        argument_scope_id: ScopeId,
+                        argument_extent: CodeExtent,
                         ast_block: &'gcx hir::Block)
-                        -> BlockAnd<Vec<ArgDecl<'tcx>>>
+                        -> BlockAnd<IndexVec<Arg, ArgDecl<'tcx>>>
         where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
     {
         // to start, translate the argument patterns and collect the argument types.
+        let mut scope = None;
         let arg_decls = arguments.enumerate().map(|(index, (ty, pattern))| {
-            let lvalue = Lvalue::Arg(index as u32);
+            let lvalue = Lvalue::Arg(Arg::new(index));
             if let Some(pattern) = pattern {
                 let pattern = self.hir.irrefutable_pat(pattern);
-                unpack!(block = self.lvalue_into_pattern(block,
-                                                         argument_scope_id,
-                                                         pattern,
-                                                         &lvalue));
+                scope = self.declare_bindings(scope, ast_block.span, &pattern);
+                unpack!(block = self.lvalue_into_pattern(block, pattern, &lvalue));
             }
 
             // Make sure we drop (parts of) the argument even when not matched on.
-            let argument_extent = self.scope_auxiliary[argument_scope_id].extent;
             self.schedule_drop(pattern.as_ref().map_or(ast_block.span, |pat| pat.span),
                                argument_extent, &lvalue, ty);
 
             let mut name = keywords::Invalid.name();
             if let Some(pat) = pattern {
-                if let hir::PatKind::Ident(_, ref ident, _) = pat.node {
-                    if pat_is_binding(&self.hir.tcx().def_map.borrow(), pat) {
-                        name = ident.node;
-                    }
+                if let hir::PatKind::Binding(_, ref ident, _) = pat.node {
+                    name = ident.node;
                 }
             }
 
@@ -347,6 +344,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
         }).collect();
 
+        // Enter the argument pattern bindings visibility scope, if it exists.
+        if let Some(visibility_scope) = scope {
+            self.visibility_scope = visibility_scope;
+        }
+
         // FIXME(#32959): temporary hack for the issue at hand
         let return_is_unit = if let ty::FnConverging(t) = return_ty {
             t.is_nil()
index 071c8d618c845faaccd32bf643ab5da874ac3285..1703fee9360cc4923929001ae990ef4202401333 100644 (file)
@@ -86,21 +86,23 @@ should go to.
 
 */
 
-use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary};
+use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary, ScopeId};
 use rustc::middle::region::{CodeExtent, CodeExtentData};
 use rustc::middle::lang_items;
 use rustc::ty::subst::{Substs, Subst, VecPerParamSpace};
-use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::{Ty, TyCtxt};
 use rustc::mir::repr::*;
-use syntax::codemap::{Span, DUMMY_SP};
-use syntax::parse::token::intern_and_get_ident;
-use rustc::middle::const_val::ConstVal;
-use rustc_const_math::ConstInt;
+use syntax_pos::Span;
+use rustc_data_structures::indexed_vec::Idx;
+use rustc_data_structures::fnv::FnvHashMap;
 
 pub struct Scope<'tcx> {
-    /// the scope-id within the scope_datas
+    /// the scope-id within the scope_auxiliary
     id: ScopeId,
 
+    /// The visibility scope this scope was created in.
+    visibility_scope: VisibilityScope,
+
     /// the extent of this scope within source code; also stored in
     /// `ScopeAuxiliary`, but kept here for convenience
     extent: CodeExtent,
@@ -126,12 +128,8 @@ pub struct Scope<'tcx> {
     /// stage.
     free: Option<FreeData<'tcx>>,
 
-    /// The cached block for the cleanups-on-diverge path. This block
-    /// contains a block that will just do a RESUME to an appropriate
-    /// place. This block does not execute any of the drops or free:
-    /// each of those has their own cached-blocks, which will branch
-    /// to this point.
-    cached_block: Option<BasicBlock>
+    /// The cache for drop chain on “normal” exit into a particular BasicBlock.
+    cached_exits: FnvHashMap<(BasicBlock, CodeExtent), BasicBlock>,
 }
 
 struct DropData<'tcx> {
@@ -139,7 +137,7 @@ struct DropData<'tcx> {
     span: Span,
 
     /// lvalue to drop
-    value: Lvalue<'tcx>,
+    location: Lvalue<'tcx>,
 
     /// The cached block for the cleanups-on-diverge path. This block
     /// contains code to run the current drop and all the preceding
@@ -171,7 +169,7 @@ pub struct LoopScope {
     pub continue_block: BasicBlock,
     /// Block to branch into when the loop terminates (either by being `break`-en out from, or by
     /// having its condition to become false)
-    pub break_block: BasicBlock, // where to go on a `break
+    pub break_block: BasicBlock,
     /// Indicates the reachability of the break_block for this loop
     pub might_break: bool
 }
@@ -182,7 +180,7 @@ impl<'tcx> Scope<'tcx> {
     /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a
     /// larger extent of code.
     fn invalidate_cache(&mut self) {
-        self.cached_block = None;
+        self.cached_exits = FnvHashMap();
         for dropdata in &mut self.drops {
             dropdata.cached_block = None;
         }
@@ -191,7 +189,7 @@ impl<'tcx> Scope<'tcx> {
         }
     }
 
-    /// Returns the cached block for this scope.
+    /// Returns the cached entrypoint for diverging exit from this scope.
     ///
     /// Precondition: the caches must be fully filled (i.e. diverge_cleanup is called) in order for
     /// this method to work correctly.
@@ -204,6 +202,14 @@ impl<'tcx> Scope<'tcx> {
             None
         }
     }
+
+    /// Given a span and this scope's visibility scope, make a SourceInfo.
+    fn source_info(&self, span: Span) -> SourceInfo {
+        SourceInfo {
+            span: span,
+            scope: self.visibility_scope
+        }
+    }
 }
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
@@ -237,11 +243,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// Convenience wrapper that pushes a scope and then executes `f`
     /// to build its contents, popping the scope afterwards.
     pub fn in_scope<F, R>(&mut self, extent: CodeExtent, mut block: BasicBlock, f: F) -> BlockAnd<R>
-        where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>, ScopeId) -> BlockAnd<R>
+        where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
     {
         debug!("in_scope(extent={:?}, block={:?})", extent, block);
-        let id = self.push_scope(extent, block);
-        let rv = unpack!(block = f(self, id));
+        self.push_scope(extent, block);
+        let rv = unpack!(block = f(self));
         unpack!(block = self.pop_scope(extent, block));
         debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
         block.and(rv)
@@ -251,28 +257,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// scope and call `pop_scope` afterwards. Note that these two
     /// calls must be paired; using `in_scope` as a convenience
     /// wrapper maybe preferable.
-    pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) -> ScopeId {
+    pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) {
         debug!("push_scope({:?})", extent);
-        let parent_id = self.scopes.last().map(|s| s.id);
-        let id = ScopeId::new(self.scope_datas.len());
-        let tcx = self.hir.tcx();
-        self.scope_datas.push(ScopeData {
-            span: extent.span(&tcx.region_maps, &tcx.map).unwrap_or(DUMMY_SP),
-            parent_scope: parent_id,
-        });
+        let id = ScopeId::new(self.scope_auxiliary.len());
+        let vis_scope = self.visibility_scope;
         self.scopes.push(Scope {
             id: id,
+            visibility_scope: vis_scope,
             extent: extent,
             drops: vec![],
             free: None,
-            cached_block: None,
+            cached_exits: FnvHashMap()
         });
-        self.scope_auxiliary.vec.push(ScopeAuxiliary {
+        self.scope_auxiliary.push(ScopeAuxiliary {
             extent: extent,
             dom: self.cfg.current_location(entry),
             postdoms: vec![]
         });
-        id
     }
 
     /// Pops a scope, which should have extent `extent`, adding any
@@ -310,35 +311,52 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                                       .unwrap_or_else(||{
             span_bug!(span, "extent {:?} does not enclose", extent)
         });
-
+        let len = self.scopes.len();
+        assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes");
         let tmp = self.get_unit_temp();
-        for (idx, ref scope) in self.scopes.iter().enumerate().rev().take(scope_count) {
-            unpack!(block = build_scope_drops(&mut self.cfg,
-                                              scope,
-                                              &self.scopes[..idx],
-                                              block));
+        {
+        let mut rest = &mut self.scopes[(len - scope_count)..];
+        while let Some((scope, rest_)) = {rest}.split_last_mut() {
+            rest = rest_;
+            block = if let Some(&e) = scope.cached_exits.get(&(target, extent)) {
+                self.cfg.terminate(block, scope.source_info(span),
+                                   TerminatorKind::Goto { target: e });
+                return;
+            } else {
+                let b = self.cfg.start_new_block();
+                self.cfg.terminate(block, scope.source_info(span),
+                                   TerminatorKind::Goto { target: b });
+                scope.cached_exits.insert((target, extent), b);
+                b
+            };
+            unpack!(block = build_scope_drops(&mut self.cfg, scope, rest, block));
             if let Some(ref free_data) = scope.free {
                 let next = self.cfg.start_new_block();
                 let free = build_free(self.hir.tcx(), &tmp, free_data, next);
-                self.cfg.terminate(block, scope.id, span, free);
+                self.cfg.terminate(block, scope.source_info(span), free);
                 block = next;
             }
             self.scope_auxiliary[scope.id]
                 .postdoms
                 .push(self.cfg.current_location(block));
         }
-
-        assert!(scope_count < self.scopes.len(),
-                "should never use `exit_scope` to pop *ALL* scopes");
-        let scope = self.scopes.iter().rev().skip(scope_count)
-                                            .next()
-                                            .unwrap();
-        self.cfg.terminate(block,
-                           scope.id,
-                           span,
+        }
+        let scope = &self.scopes[len - scope_count];
+        self.cfg.terminate(block, scope.source_info(span),
                            TerminatorKind::Goto { target: target });
     }
 
+    /// Creates a new visibility scope, nested in the current one.
+    pub fn new_visibility_scope(&mut self, span: Span) -> VisibilityScope {
+        let parent = self.visibility_scope;
+        let scope = VisibilityScope::new(self.visibility_scopes.len());
+        self.visibility_scopes.push(VisibilityScopeData {
+            span: span,
+            parent_scope: Some(parent),
+        });
+        scope
+    }
+
     // Finding scopes
     // ==============
     /// Finds the loop scope for a given label. This is used for
@@ -363,8 +381,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         }.unwrap_or_else(|| span_bug!(span, "no enclosing loop scope found?"))
     }
 
-    pub fn innermost_scope_id(&self) -> ScopeId {
-        self.scopes.last().map(|scope| scope.id).unwrap()
+    /// Given a span and the current visibility scope, make a SourceInfo.
+    pub fn source_info(&self, span: Span) -> SourceInfo {
+        SourceInfo {
+            span: span,
+            scope: self.visibility_scope
+        }
     }
 
     pub fn extent_of_innermost_scope(&self) -> CodeExtent {
@@ -402,7 +424,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // the drop that comes before it in the vector.
                 scope.drops.push(DropData {
                     span: span,
-                    value: lvalue.clone(),
+                    location: lvalue.clone(),
                     cached_block: None
                 });
                 return;
@@ -481,15 +503,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             target
         } else {
             let resumeblk = cfg.start_new_cleanup_block();
-            cfg.terminate(resumeblk, scopes[0].id, self.fn_span, TerminatorKind::Resume);
+            cfg.terminate(resumeblk,
+                          scopes[0].source_info(self.fn_span),
+                          TerminatorKind::Resume);
             *cached_resume_block = Some(resumeblk);
             resumeblk
         };
 
-        for scope in scopes {
+        for scope in scopes.iter_mut().filter(|s| !s.drops.is_empty() || s.free.is_some()) {
             target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target);
         }
-
         Some(target)
     }
 
@@ -497,142 +520,67 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     pub fn build_drop(&mut self,
                       block: BasicBlock,
                       span: Span,
-                      value: Lvalue<'tcx>,
+                      location: Lvalue<'tcx>,
                       ty: Ty<'tcx>) -> BlockAnd<()> {
         if !self.hir.needs_drop(ty) {
             return block.unit();
         }
-        let scope_id = self.innermost_scope_id();
+        let source_info = self.source_info(span);
         let next_target = self.cfg.start_new_block();
         let diverge_target = self.diverge_cleanup();
-        self.cfg.terminate(block,
-                           scope_id,
-                           span,
+        self.cfg.terminate(block, source_info,
                            TerminatorKind::Drop {
-                               value: value,
+                               location: location,
                                target: next_target,
                                unwind: diverge_target,
                            });
         next_target.unit()
     }
 
-
-    // Panicking
-    // =========
-    // FIXME: should be moved into their own module
-    pub fn panic_bounds_check(&mut self,
-                              block: BasicBlock,
-                              index: Operand<'tcx>,
-                              len: Operand<'tcx>,
-                              span: Span) {
-        // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> !
-        let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
-        let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
-        let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
-
-        let ref_ty = args[0];
-        let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
-            tyandmut.ty
-        } else {
-            span_bug!(span, "unexpected panic_bound_check type: {:?}", func.ty);
-        };
-
-        let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
-        let (file, line) = self.span_to_fileline_args(span);
-        let elems = vec![Operand::Constant(file), Operand::Constant(line)];
-        let scope_id = self.innermost_scope_id();
-        // FIXME: We should have this as a constant, rather than a stack variable (to not pollute
-        // icache with cold branch code), however to achieve that we either have to rely on rvalue
-        // promotion or have some way, in MIR, to create constants.
-        self.cfg.push_assign(block, scope_id, span, &tuple, // tuple = (file_arg, line_arg);
-                             Rvalue::Aggregate(AggregateKind::Tuple, elems));
-        // FIXME: is this region really correct here?
-        self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
-                             Rvalue::Ref(region, BorrowKind::Shared, tuple));
-        let cleanup = self.diverge_cleanup();
-        self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
-            func: Operand::Constant(func),
-            args: vec![Operand::Consume(tuple_ref), index, len],
-            destination: None,
-            cleanup: cleanup,
-        });
+    /// Utility function for *non*-scope code to build their own drops
+    pub fn build_drop_and_replace(&mut self,
+                                  block: BasicBlock,
+                                  span: Span,
+                                  location: Lvalue<'tcx>,
+                                  value: Operand<'tcx>) -> BlockAnd<()> {
+        let source_info = self.source_info(span);
+        let next_target = self.cfg.start_new_block();
+        let diverge_target = self.diverge_cleanup();
+        self.cfg.terminate(block, source_info,
+                           TerminatorKind::DropAndReplace {
+                               location: location,
+                               value: value,
+                               target: next_target,
+                               unwind: diverge_target,
+                           });
+        next_target.unit()
     }
 
-    /// Create diverge cleanup and branch to it from `block`.
-    pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
-        // fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
-        let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
-        let func = self.lang_function(lang_items::PanicFnLangItem);
-        let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
-
-        let ref_ty = args[0];
-        let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
-            tyandmut.ty
-        } else {
-            span_bug!(span, "unexpected panic type: {:?}", func.ty);
-        };
-
-        let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
-        let (file, line) = self.span_to_fileline_args(span);
-        let message = Constant {
-            span: span,
-            ty: self.hir.tcx().mk_static_str(),
-            literal: self.hir.str_literal(intern_and_get_ident(msg))
-        };
-        let elems = vec![Operand::Constant(message),
-                         Operand::Constant(file),
-                         Operand::Constant(line)];
-        let scope_id = self.innermost_scope_id();
-        // FIXME: We should have this as a constant, rather than a stack variable (to not pollute
-        // icache with cold branch code), however to achieve that we either have to rely on rvalue
-        // promotion or have some way, in MIR, to create constants.
-        self.cfg.push_assign(block, scope_id, span, &tuple, // [1]
-                             Rvalue::Aggregate(AggregateKind::Tuple, elems));
-        // [1] tuple = (message_arg, file_arg, line_arg);
-        // FIXME: is this region really correct here?
-        self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
-                             Rvalue::Ref(region, BorrowKind::Shared, tuple));
+    /// Create an Assert terminator and return the success block.
+    /// If the boolean condition operand is not the expected value,
+    /// a runtime panic will be caused with the given message.
+    pub fn assert(&mut self, block: BasicBlock,
+                  cond: Operand<'tcx>,
+                  expected: bool,
+                  msg: AssertMessage<'tcx>,
+                  span: Span)
+                  -> BasicBlock {
+        let source_info = self.source_info(span);
+
+        let success_block = self.cfg.start_new_block();
         let cleanup = self.diverge_cleanup();
-        self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
-            func: Operand::Constant(func),
-            args: vec![Operand::Consume(tuple_ref)],
-            cleanup: cleanup,
-            destination: None,
-        });
-    }
 
-    fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Constant<'tcx> {
-        let funcdid = match self.hir.tcx().lang_items.require(lang_item) {
-            Ok(d) => d,
-            Err(m) => {
-                self.hir.tcx().sess.fatal(&m)
-            }
-        };
-        Constant {
-            span: DUMMY_SP,
-            ty: self.hir.tcx().lookup_item_type(funcdid).ty,
-            literal: Literal::Item {
-                def_id: funcdid,
-                substs: self.hir.tcx().mk_substs(Substs::empty())
-            }
-        }
-    }
+        self.cfg.terminate(block, source_info,
+                           TerminatorKind::Assert {
+                               cond: cond,
+                               expected: expected,
+                               msg: msg,
+                               target: success_block,
+                               cleanup: cleanup
+                           });
 
-    fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) {
-        let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo);
-        (Constant {
-            span: span,
-            ty: self.hir.tcx().mk_static_str(),
-            literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name))
-        }, Constant {
-            span: span,
-            ty: self.hir.tcx().types.u32,
-            literal: Literal::Value {
-                value: ConstVal::Integral(ConstInt::U32(span_lines.line as u32)),
-            },
-        })
+        success_block
     }
-
 }
 
 /// Builds drops for pop_scope and exit_scope.
@@ -652,8 +600,8 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
             earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
         });
         let next = cfg.start_new_block();
-        cfg.terminate(block, scope.id, drop_data.span, TerminatorKind::Drop {
-            value: drop_data.value.clone(),
+        cfg.terminate(block, scope.source_info(drop_data.span), TerminatorKind::Drop {
+            location: drop_data.location.clone(),
             target: next,
             unwind: on_diverge
         });
@@ -682,15 +630,19 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
     // remainder. If everything is cached, we'll just walk right to
     // left reading the cached results but never created anything.
 
+    let visibility_scope = scope.visibility_scope;
+    let source_info = |span| SourceInfo {
+        span: span,
+        scope: visibility_scope
+    };
+
     // Next, build up any free.
     if let Some(ref mut free_data) = scope.free {
         target = if let Some(cached_block) = free_data.cached_block {
             cached_block
         } else {
             let into = cfg.start_new_cleanup_block();
-            cfg.terminate(into,
-                          scope.id,
-                          free_data.span,
+            cfg.terminate(into, source_info(free_data.span),
                           build_free(tcx, unit_temp, free_data, target));
             free_data.cached_block = Some(into);
             into
@@ -705,11 +657,9 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
             cached_block
         } else {
             let block = cfg.start_new_cleanup_block();
-            cfg.terminate(block,
-                          scope.id,
-                          drop_data.span,
+            cfg.terminate(block, source_info(drop_data.span),
                           TerminatorKind::Drop {
-                              value: drop_data.value.clone(),
+                              location: drop_data.location.clone(),
                               target: target,
                               unwind: None
                           });
index 6a34d9ff0b4c4ed1cdfda7474bb6569efafb802d..fdfa872b0b698805f140863294837893e86a70e7 100644 (file)
@@ -15,6 +15,8 @@ use std::fmt::Debug;
 use std::io::{self, Write};
 use syntax::ast::NodeId;
 
+use rustc_data_structures::indexed_vec::Idx;
+
 /// Write a graphviz DOT graph of a list of MIRs.
 pub fn write_mir_graphviz<'a, 'b, 'tcx, W, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
                                               iter: I, w: &mut W)
@@ -32,12 +34,12 @@ where W: Write, I: Iterator<Item=(&'a NodeId, &'a Mir<'a>)> {
         write_graph_label(tcx, nodeid, mir, w)?;
 
         // Nodes
-        for block in mir.all_basic_blocks() {
+        for (block, _) in mir.basic_blocks().iter_enumerated() {
             write_node(block, mir, w)?;
         }
 
         // Edges
-        for source in mir.all_basic_blocks() {
+        for (source, _) in mir.basic_blocks().iter_enumerated() {
             write_edges(source, mir, w)?;
         }
         writeln!(w, "}}")?
@@ -61,7 +63,7 @@ pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
     where INIT: Fn(&mut W) -> io::Result<()>,
           FINI: Fn(&mut W) -> io::Result<()>
 {
-    let data = mir.basic_block_data(block);
+    let data = &mir[block];
 
     write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
 
@@ -105,7 +107,7 @@ fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<(
 
 /// Write graphviz DOT edges with labels between the given basic block and all of its successors.
 fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
-    let terminator = &mir.basic_block_data(source).terminator();
+    let terminator = mir[source].terminator();
     let labels = terminator.kind.fmt_successor_labels();
 
     for (&target, label) in terminator.successors().iter().zip(labels) {
@@ -130,7 +132,7 @@ fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         if i > 0 {
             write!(w, ", ")?;
         }
-        write!(w, "{:?}: {}", Lvalue::Arg(i as u32), escape(&arg.ty))?;
+        write!(w, "{:?}: {}", Lvalue::Arg(Arg::new(i)), escape(&arg.ty))?;
     }
 
     write!(w, ") -&gt; ")?;
@@ -150,13 +152,13 @@ fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             write!(w, "mut ")?;
         }
         write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
-               Lvalue::Var(i as u32), escape(&var.ty), var.name)?;
+               Lvalue::Var(Var::new(i)), escape(&var.ty), var.name)?;
     }
 
     // Compiler-introduced temporary types.
     for (i, temp) in mir.temp_decls.iter().enumerate() {
         write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
-               Lvalue::Temp(i as u32), escape(&temp.ty))?;
+               Lvalue::Temp(Temp::new(i)), escape(&temp.ty))?;
     }
 
     writeln!(w, ">;")
index 1e7164a62c070d8ea2020ea2b9a4fbd8b17947e5..b5e2ce9de483692054406c1b0371dea674538ae8 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use hair::*;
-use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::indexed_vec::Idx;
 use rustc_const_math::ConstInt;
 use hair::cx::Cx;
 use hair::cx::block;
@@ -19,7 +19,6 @@ use rustc::hir::def::Def;
 use rustc::middle::const_val::ConstVal;
 use rustc_const_eval as const_eval;
 use rustc::middle::region::CodeExtent;
-use rustc::hir::pat_util;
 use rustc::ty::{self, VariantDef, Ty};
 use rustc::ty::cast::CastKind as TyCastKind;
 use rustc::mir::repr::*;
@@ -263,7 +262,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                 let adt_data = if let hir::ExprPath(..) = fun.node {
                     // Tuple-like ADTs are represented as ExprCall. We convert them here.
                     expr_ty.ty_adt_def().and_then(|adt_def|{
-                        match cx.tcx.def_map.borrow()[&fun.id].full_def() {
+                        match cx.tcx.expect_def(fun.id) {
                             Def::Variant(_, variant_id) => {
                                 Some((adt_def, adt_def.variant_index_with_id(variant_id)))
                             },
@@ -471,7 +470,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                     }
                 }
                 ty::TyEnum(adt, substs) => {
-                    match cx.tcx.def_map.borrow()[&expr.id].full_def() {
+                    match cx.tcx.expect_def(expr.id) {
                         Def::Variant(enum_id, variant_id) => {
                             debug_assert!(adt.did == enum_id);
                             assert!(base.is_none());
@@ -651,19 +650,8 @@ fn to_borrow_kind(m: hir::Mutability) -> BorrowKind {
 
 fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                arm: &'tcx hir::Arm) -> Arm<'tcx> {
-    let mut map;
-    let opt_map = if arm.pats.len() == 1 {
-        None
-    } else {
-        map = FnvHashMap();
-        pat_util::pat_bindings(&cx.tcx.def_map, &arm.pats[0], |_, p_id, _, path| {
-            map.insert(path.node, p_id);
-        });
-        Some(&map)
-    };
-
     Arm {
-        patterns: arm.pats.iter().map(|p| cx.refutable_pat(opt_map, p)).collect(),
+        patterns: arm.pats.iter().map(|p| cx.refutable_pat(p)).collect(),
         guard: arm.guard.to_ref(),
         body: arm.body.to_ref(),
     }
@@ -674,7 +662,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                      -> ExprKind<'tcx> {
     let substs = cx.tcx.node_id_item_substs(expr.id).substs;
     // Otherwise there may be def_map borrow conflicts
-    let def = cx.tcx.def_map.borrow()[&expr.id].full_def();
+    let def = cx.tcx.expect_def(expr.id);
     let def_id = match def {
         // A regular function.
         Def::Fn(def_id) | Def::Method(def_id) => def_id,
@@ -730,14 +718,9 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
             id: node_id,
         },
 
-        def @ Def::Local(..) |
-        def @ Def::Upvar(..) => return convert_var(cx, expr, def),
+        Def::Local(..) | Def::Upvar(..) => return convert_var(cx, expr, def),
 
-        def =>
-            span_bug!(
-                expr.span,
-                "def `{:?}` not yet implemented",
-                def),
+        _ => span_bug!(expr.span, "def `{:?}` not yet implemented", def),
     };
     ExprKind::Literal {
         literal: Literal::Item { def_id: def_id, substs: substs }
@@ -1038,11 +1021,9 @@ fn capture_freevar<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 
 fn loop_label<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                               expr: &'tcx hir::Expr) -> CodeExtent {
-    match cx.tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def()) {
-        Some(Def::Label(loop_id)) => cx.tcx.region_maps.node_extent(loop_id),
-        d => {
-            span_bug!(expr.span, "loop scope resolved to {:?}", d);
-        }
+    match cx.tcx.expect_def(expr.id) {
+        Def::Label(loop_id) => cx.tcx.region_maps.node_extent(loop_id),
+        d => span_bug!(expr.span, "loop scope resolved to {:?}", d),
     }
 }
 
index fad6cfb7ae1aab784d84ebf4f603deddf5d674b2..81b098281d6a19c578b84fac946526e7685c55b6 100644 (file)
 
 use hair::*;
 use rustc::mir::repr::*;
+use rustc::mir::transform::MirSource;
 
 use rustc::middle::const_val::ConstVal;
 use rustc_const_eval as const_eval;
+use rustc_data_structures::indexed_vec::Idx;
 use rustc::hir::def_id::DefId;
+use rustc::hir::intravisit::FnKind;
+use rustc::hir::map::blocks::FnLikeNode;
 use rustc::infer::InferCtxt;
 use rustc::ty::subst::{Subst, Substs};
 use rustc::ty::{self, Ty, TyCtxt};
 use syntax::parse::token;
 use rustc::hir;
 use rustc_const_math::{ConstInt, ConstUsize};
+use syntax::attr::AttrMetaMethods;
 
 #[derive(Copy, Clone)]
 pub struct Cx<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
-    constness: hir::Constness
+    constness: hir::Constness,
+
+    /// True if this constant/function needs overflow checks.
+    check_overflow: bool
 }
 
 impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
     pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
-               constness: hir::Constness)
+               src: MirSource)
                -> Cx<'a, 'gcx, 'tcx> {
+        let constness = match src {
+            MirSource::Const(_) |
+            MirSource::Static(..) => hir::Constness::Const,
+            MirSource::Fn(id) => {
+                let fn_like = FnLikeNode::from_node(infcx.tcx.map.get(id));
+                match fn_like.map(|f| f.kind()) {
+                    Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => c,
+                    Some(FnKind::Method(_, m, _, _)) => m.constness,
+                    _ => hir::Constness::NotConst
+                }
+            }
+            MirSource::Promoted(..) => bug!()
+        };
+
+        let attrs = infcx.tcx.map.attrs(src.item_id());
+
+        // Some functions always have overflow checks enabled,
+        // however, they may not get codegen'd, depending on
+        // the settings for the crate they are translated in.
+        let mut check_overflow = attrs.iter().any(|item| {
+            item.check_name("rustc_inherit_overflow_checks")
+        });
+
+        // Respect -Z force-overflow-checks=on and -C debug-assertions.
+        check_overflow |= infcx.tcx.sess.opts.debugging_opts.force_overflow_checks
+               .unwrap_or(infcx.tcx.sess.opts.debug_assertions);
+
+        // Constants and const fn's always need overflow checks.
+        check_overflow |= constness == hir::Constness::Const;
+
         Cx {
             tcx: infcx.tcx,
             infcx: infcx,
             constness: constness,
+            check_overflow: check_overflow
         }
     }
 }
@@ -154,6 +193,10 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
     pub fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
         self.tcx
     }
+
+    pub fn check_overflow(&self) -> bool {
+        self.check_overflow
+    }
 }
 
 mod block;
index 0118b97dd7f3e5454b47cf29957a150645d12cac..b5da50792762f17f33e4202447b0a05f44e143d6 100644 (file)
 
 use hair::*;
 use hair::cx::Cx;
-use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::indexed_vec::Idx;
 use rustc_const_eval as const_eval;
 use rustc::hir::def::Def;
-use rustc::hir::pat_util::{pat_is_resolved_const, pat_is_binding};
+use rustc::hir::pat_util::{EnumerateAndAdjustIterator, pat_is_resolved_const};
 use rustc::ty::{self, Ty};
 use rustc::mir::repr::*;
 use rustc::hir::{self, PatKind};
-use syntax::ast;
-use syntax::codemap::Span;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 /// When there are multiple patterns in a single arm, each one has its
 /// own node-ids for the bindings.  References to the variables always
@@ -36,29 +35,25 @@ use syntax::ptr::P;
 /// ```
 struct PatCx<'patcx, 'cx: 'patcx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
     cx: &'patcx mut Cx<'cx, 'gcx, 'tcx>,
-    binding_map: Option<&'patcx FnvHashMap<ast::Name, ast::NodeId>>,
 }
 
 impl<'cx, 'gcx, 'tcx> Cx<'cx, 'gcx, 'tcx> {
     pub fn irrefutable_pat(&mut self, pat: &hir::Pat) -> Pattern<'tcx> {
-        PatCx::new(self, None).to_pattern(pat)
+        PatCx::new(self).to_pattern(pat)
     }
 
     pub fn refutable_pat(&mut self,
-                         binding_map: Option<&FnvHashMap<ast::Name, ast::NodeId>>,
                          pat: &hir::Pat)
                          -> Pattern<'tcx> {
-        PatCx::new(self, binding_map).to_pattern(pat)
+        PatCx::new(self).to_pattern(pat)
     }
 }
 
 impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
-    fn new(cx: &'patcx mut Cx<'cx, 'gcx, 'tcx>,
-               binding_map: Option<&'patcx FnvHashMap<ast::Name, ast::NodeId>>)
+    fn new(cx: &'patcx mut Cx<'cx, 'gcx, 'tcx>)
                -> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
         PatCx {
             cx: cx,
-            binding_map: binding_map,
         }
     }
 
@@ -81,11 +76,10 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
                 PatternKind::Range { lo: lo, hi: hi }
             },
 
-            PatKind::Path(..) | PatKind::Ident(..) | PatKind::QPath(..)
+            PatKind::Path(..) | PatKind::QPath(..)
                 if pat_is_resolved_const(&self.cx.tcx.def_map.borrow(), pat) =>
             {
-                let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
-                match def {
+                match self.cx.tcx.expect_def(pat.id) {
                     Def::Const(def_id) | Def::AssociatedConst(def_id) => {
                         let tcx = self.cx.tcx.global_tcx();
                         let substs = Some(self.cx.tcx.node_id_item_substs(pat.id).substs);
@@ -110,7 +104,7 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
                             }
                         }
                     }
-                    _ =>
+                    def =>
                         span_bug!(
                             pat.span,
                             "def not a constant: {:?}",
@@ -148,26 +142,27 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
                 }
             }
 
-            PatKind::Tup(ref subpatterns) => {
-                let subpatterns =
-                    subpatterns.iter()
-                               .enumerate()
-                               .map(|(i, subpattern)| FieldPattern {
-                                   field: Field::new(i),
-                                   pattern: self.to_pattern(subpattern),
-                               })
-                               .collect();
+            PatKind::Tuple(ref subpatterns, ddpos) => {
+                match self.cx.tcx.node_id_to_type(pat.id).sty {
+                    ty::TyTuple(ref tys) => {
+                        let subpatterns =
+                            subpatterns.iter()
+                                       .enumerate_and_adjust(tys.len(), ddpos)
+                                       .map(|(i, subpattern)| FieldPattern {
+                                            field: Field::new(i),
+                                            pattern: self.to_pattern(subpattern),
+                                       })
+                                       .collect();
+
+                        PatternKind::Leaf { subpatterns: subpatterns }
+                    }
 
-                PatternKind::Leaf { subpatterns: subpatterns }
+                    ref sty => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", sty),
+                }
             }
 
-            PatKind::Ident(bm, ref ident, ref sub)
-                if pat_is_binding(&self.cx.tcx.def_map.borrow(), pat) =>
-            {
-                let id = match self.binding_map {
-                    None => pat.id,
-                    Some(ref map) => map[&ident.node],
-                };
+            PatKind::Binding(bm, ref ident, ref sub) => {
+                let id = self.cx.tcx.expect_def(pat.id).var_id();
                 let var_ty = self.cx.tcx.node_id_to_type(pat.id);
                 let region = match var_ty.sty {
                     ty::TyRef(&r, _) => Some(r),
@@ -204,15 +199,21 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
                 }
             }
 
-            PatKind::Ident(..) | PatKind::Path(..) => {
+            PatKind::Path(..) => {
                 self.variant_or_leaf(pat, vec![])
             }
 
-            PatKind::TupleStruct(_, ref opt_subpatterns) => {
+            PatKind::TupleStruct(_, ref subpatterns, ddpos) => {
+                let pat_ty = self.cx.tcx.node_id_to_type(pat.id);
+                let adt_def = match pat_ty.sty {
+                    ty::TyStruct(adt_def, _) | ty::TyEnum(adt_def, _) => adt_def,
+                    _ => span_bug!(pat.span, "tuple struct pattern not applied to struct or enum"),
+                };
+                let variant_def = adt_def.variant_of_def(self.cx.tcx.expect_def(pat.id));
+
                 let subpatterns =
-                    opt_subpatterns.iter()
-                                   .flat_map(|v| v.iter())
-                                   .enumerate()
+                        subpatterns.iter()
+                                   .enumerate_and_adjust(variant_def.fields.len(), ddpos)
                                    .map(|(i, field)| FieldPattern {
                                        field: Field::new(i),
                                        pattern: self.to_pattern(field),
@@ -231,9 +232,7 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
                             "struct pattern not applied to struct or enum");
                     }
                 };
-
-                let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
-                let variant_def = adt_def.variant_of_def(def);
+                let variant_def = adt_def.variant_of_def(self.cx.tcx.expect_def(pat.id));
 
                 let subpatterns =
                     fields.iter()
@@ -312,8 +311,7 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
                        pat: &hir::Pat,
                        subpatterns: Vec<FieldPattern<'tcx>>)
                        -> PatternKind<'tcx> {
-        let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
-        match def {
+        match self.cx.tcx.expect_def(pat.id) {
             Def::Variant(enum_id, variant_id) => {
                 let adt_def = self.cx.tcx.lookup_adt_def(enum_id);
                 if adt_def.variants.len() > 1 {
@@ -331,7 +329,7 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> {
                 PatternKind::Leaf { subpatterns: subpatterns }
             }
 
-            _ => {
+            def => {
                 span_bug!(pat.span, "inappropriate def for pattern: {:?}", def);
             }
         }
index 020fbb6fcd19c66bb0aae0105a90fbfddb7c12fe..a2746bf30c91f7020702f6bd15f94f243c59c27a 100644 (file)
@@ -23,7 +23,7 @@ use rustc::ty::subst::Substs;
 use rustc::ty::{self, AdtDef, ClosureSubsts, Region, Ty};
 use rustc::hir;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use self::cx::Cx;
 
 pub mod cx;
index 79d11e78bde5af65d08b7e4a1382dd665970e2c4..3d01d49c53472ecb57c1429a97ca57b1cea19f16 100644 (file)
@@ -38,6 +38,7 @@ extern crate rustc_back;
 extern crate rustc_bitflags;
 #[macro_use]
 extern crate syntax;
+extern crate syntax_pos;
 extern crate rustc_const_math;
 extern crate rustc_const_eval;
 
@@ -49,4 +50,3 @@ mod hair;
 pub mod mir_map;
 pub mod pretty;
 pub mod transform;
-pub mod traversal;
index 73cfdeda74a885ab14e4f9a89af6789acc50d350..b7c5f35892b0b870c20df88a70913b5a9ca30e56 100644 (file)
@@ -32,9 +32,8 @@ use rustc::ty::subst::Substs;
 use rustc::util::nodemap::NodeMap;
 use rustc::hir;
 use rustc::hir::intravisit::{self, FnKind, Visitor};
-use rustc::hir::map::blocks::FnLikeNode;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use std::mem;
 
@@ -116,20 +115,7 @@ impl<'a, 'gcx, 'tcx> CxBuilder<'a, 'gcx, 'tcx> {
     {
         let src = self.src;
         let mir = self.infcx.enter(|infcx| {
-            let constness = match src {
-                MirSource::Const(_) |
-                MirSource::Static(..) => hir::Constness::Const,
-                MirSource::Fn(id) => {
-                    let fn_like = FnLikeNode::from_node(infcx.tcx.map.get(id));
-                    match fn_like.map(|f| f.kind()) {
-                        Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => c,
-                        Some(FnKind::Method(_, m, _, _)) => m.constness,
-                        _ => hir::Constness::NotConst
-                    }
-                }
-                MirSource::Promoted(..) => bug!()
-            };
-            let (mut mir, scope_auxiliary) = f(Cx::new(&infcx, constness));
+            let (mut mir, scope_auxiliary) = f(Cx::new(&infcx, src));
 
             // Convert the Mir to global types.
             let mut globalizer = GlobalizeMir {
index fb29cbd5fa8a24651eccd3786848b621b41fdfb5..515620d425389676617e3efa5f9cbba8cf5c96b3 100644 (file)
@@ -8,17 +8,17 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use build::{Location, ScopeAuxiliaryVec};
+use build::{Location, ScopeAuxiliaryVec, ScopeId};
 use rustc::hir;
 use rustc::mir::repr::*;
 use rustc::mir::transform::MirSource;
 use rustc::ty::{self, TyCtxt};
 use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::indexed_vec::{Idx};
 use std::fmt::Display;
 use std::fs;
 use std::io::{self, Write};
 use syntax::ast::NodeId;
-use syntax::codemap::Span;
 
 const INDENT: &'static str = "    ";
 /// Alignment for lining up comments following MIR statements
@@ -61,8 +61,13 @@ pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         return;
     }
 
-    let file_name = format!("rustc.node{}.{}.{}.mir",
-                            node_id, pass_name, disambiguator);
+    let promotion_id = match src {
+        MirSource::Promoted(_, id) => format!("-{:?}", id),
+        _ => String::new()
+    };
+
+    let file_name = format!("rustc.node{}{}.{}.{}.mir",
+                            node_id, promotion_id, pass_name, disambiguator);
     let _ = fs::File::create(&file_name).and_then(|mut file| {
         try!(writeln!(file, "// MIR for `{}`", node_path));
         try!(writeln!(file, "// node_id = {}", node_id));
@@ -93,7 +98,7 @@ pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
         let src = MirSource::from_node(tcx, id);
         write_mir_fn(tcx, src, mir, w, None)?;
 
-        for (i, mir) in mir.promoted.iter().enumerate() {
+        for (i, mir) in mir.promoted.iter_enumerated() {
             writeln!(w, "")?;
             write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w, None)?;
         }
@@ -106,18 +111,13 @@ enum Annotation {
     ExitScope(ScopeId),
 }
 
-pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                              src: MirSource,
-                              mir: &Mir<'tcx>,
-                              w: &mut Write,
-                              auxiliary: Option<&ScopeAuxiliaryVec>)
-                              -> io::Result<()> {
+fn scope_entry_exit_annotations(auxiliary: Option<&ScopeAuxiliaryVec>)
+                                -> FnvHashMap<Location, Vec<Annotation>>
+{
     // compute scope/entry exit annotations
     let mut annotations = FnvHashMap();
     if let Some(auxiliary) = auxiliary {
-        for (index, auxiliary) in auxiliary.vec.iter().enumerate() {
-            let scope_id = ScopeId::new(index);
-
+        for (scope_id, auxiliary) in auxiliary.iter_enumerated() {
             annotations.entry(auxiliary.dom)
                        .or_insert(vec![])
                        .push(Annotation::EnterScope(scope_id));
@@ -129,24 +129,24 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             }
         }
     }
+    return annotations;
+}
 
+pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                              src: MirSource,
+                              mir: &Mir<'tcx>,
+                              w: &mut Write,
+                              auxiliary: Option<&ScopeAuxiliaryVec>)
+                              -> io::Result<()> {
+    let annotations = scope_entry_exit_annotations(auxiliary);
     write_mir_intro(tcx, src, mir, w)?;
-    for block in mir.all_basic_blocks() {
+    for block in mir.basic_blocks().indices() {
         write_basic_block(tcx, block, mir, w, &annotations)?;
+        if block.index() + 1 != mir.basic_blocks().len() {
+            writeln!(w, "")?;
+        }
     }
 
-    // construct a scope tree and write it out
-    let mut scope_tree: FnvHashMap<Option<ScopeId>, Vec<ScopeId>> = FnvHashMap();
-    for (index, scope_data) in mir.scopes.iter().enumerate() {
-        scope_tree.entry(scope_data.parent_scope)
-                  .or_insert(vec![])
-                  .push(ScopeId::new(index));
-    }
-
-    writeln!(w, "{}scope tree:", INDENT)?;
-    write_scope_tree(tcx, mir, auxiliary, &scope_tree, w, None, 1, false)?;
-    writeln!(w, "")?;
-
     writeln!(w, "}}")?;
     Ok(())
 }
@@ -158,7 +158,7 @@ fn write_basic_block(tcx: TyCtxt,
                      w: &mut Write,
                      annotations: &FnvHashMap<Location, Vec<Annotation>>)
                      -> io::Result<()> {
-    let data = mir.basic_block_data(block);
+    let data = &mir[block];
 
     // Basic block label at the top.
     writeln!(w, "{}{:?}: {{", INDENT, block)?;
@@ -183,7 +183,7 @@ fn write_basic_block(tcx: TyCtxt,
         writeln!(w, "{0:1$} // {2}",
                  indented_mir,
                  ALIGN,
-                 comment(tcx, statement.scope, statement.span))?;
+                 comment(tcx, statement.source_info))?;
 
         current_location.statement_index += 1;
     }
@@ -193,71 +193,64 @@ fn write_basic_block(tcx: TyCtxt,
     writeln!(w, "{0:1$} // {2}",
              indented_terminator,
              ALIGN,
-             comment(tcx, data.terminator().scope, data.terminator().span))?;
+             comment(tcx, data.terminator().source_info))?;
 
     writeln!(w, "{}}}\n", INDENT)
 }
 
-fn comment(tcx: TyCtxt, scope: ScopeId, span: Span) -> String {
+fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
     format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
 }
 
 fn write_scope_tree(tcx: TyCtxt,
                     mir: &Mir,
-                    auxiliary: Option<&ScopeAuxiliaryVec>,
-                    scope_tree: &FnvHashMap<Option<ScopeId>, Vec<ScopeId>>,
+                    scope_tree: &FnvHashMap<VisibilityScope, Vec<VisibilityScope>>,
                     w: &mut Write,
-                    parent: Option<ScopeId>,
-                    depth: usize,
-                    same_line: bool)
+                    parent: VisibilityScope,
+                    depth: usize)
                     -> io::Result<()> {
-    let indent = if same_line {
-        0
-    } else {
-        depth * INDENT.len()
-    };
+    let indent = depth * INDENT.len();
 
     let children = match scope_tree.get(&parent) {
         Some(childs) => childs,
         None => return Ok(()),
     };
 
-    for (index, &child) in children.iter().enumerate() {
-        if index == 0 && same_line {
-            // We know we're going to output a scope, so prefix it with a space to separate it from
-            // the previous scopes on this line
-            write!(w, " ")?;
-        }
+    for &child in children {
+        let data = &mir.visibility_scopes[child];
+        assert_eq!(data.parent_scope, Some(parent));
+        writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
 
-        let data = &mir.scopes[child];
-        assert_eq!(data.parent_scope, parent);
-        write!(w, "{0:1$}{2}", "", indent, child.index())?;
-
-        let indent = indent + INDENT.len();
+        // User variable types (including the user's name in a comment).
+        for (id, var) in mir.var_decls.iter_enumerated() {
+            // Skip if not declared in this scope.
+            if var.source_info.scope != child {
+                continue;
+            }
 
-        if let Some(auxiliary) = auxiliary {
-            let extent = auxiliary[child].extent;
-            let data = tcx.region_maps.code_extent_data(extent);
-            writeln!(w, "{0:1$}Extent: {2:?}", "", indent, data)?;
+            let mut_str = if var.mutability == Mutability::Mut {
+                "mut "
+            } else {
+                ""
+            };
+
+            let indent = indent + INDENT.len();
+            let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
+                                       INDENT,
+                                       indent,
+                                       mut_str,
+                                       id,
+                                       var.ty);
+            writeln!(w, "{0:1$} // \"{2}\" in {3}",
+                     indented_var,
+                     ALIGN,
+                     var.name,
+                     comment(tcx, var.source_info))?;
         }
 
-        let child_count = scope_tree.get(&Some(child)).map(Vec::len).unwrap_or(0);
-        if child_count < 2 {
-            // Skip the braces when there's no or only a single subscope
-            write_scope_tree(tcx, mir, auxiliary, scope_tree, w,
-                             Some(child), depth, true)?;
-        } else {
-            // 2 or more child scopes? Put them in braces and on new lines.
-            writeln!(w, " {{")?;
-            write_scope_tree(tcx, mir, auxiliary, scope_tree, w,
-                             Some(child), depth + 1, false)?;
+        write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
 
-            write!(w, "\n{0:1$}}}", "", depth * INDENT.len())?;
-        }
-
-        if !same_line && index + 1 < children.len() {
-            writeln!(w, "")?;
-        }
+        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
     }
 
     Ok(())
@@ -270,12 +263,36 @@ fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              mir: &Mir,
                              w: &mut Write)
                              -> io::Result<()> {
+    write_mir_sig(tcx, src, mir, w)?;
+    writeln!(w, " {{")?;
+
+    // construct a scope tree and write it out
+    let mut scope_tree: FnvHashMap<VisibilityScope, Vec<VisibilityScope>> = FnvHashMap();
+    for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
+        if let Some(parent) = scope_data.parent_scope {
+            scope_tree.entry(parent)
+                      .or_insert(vec![])
+                      .push(VisibilityScope::new(index));
+        } else {
+            // Only the argument scope has no parent, because it's the root.
+            assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
+        }
+    }
+
+    write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
+
+    write_mir_decls(mir, w)
+}
+
+fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
+                 -> io::Result<()>
+{
     match src {
         MirSource::Fn(_) => write!(w, "fn")?,
         MirSource::Const(_) => write!(w, "const")?,
         MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
         MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
-        MirSource::Promoted(_, i) => write!(w, "promoted{} in", i)?
+        MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
     }
 
     write!(w, " {}", tcx.node_path_str(src.item_id()))?;
@@ -284,50 +301,30 @@ fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         write!(w, "(")?;
 
         // fn argument types.
-        for (i, arg) in mir.arg_decls.iter().enumerate() {
-            if i > 0 {
+        for (i, arg) in mir.arg_decls.iter_enumerated() {
+            if i.index() != 0 {
                 write!(w, ", ")?;
             }
-            write!(w, "{:?}: {}", Lvalue::Arg(i as u32), arg.ty)?;
+            write!(w, "{:?}: {}", Lvalue::Arg(i), arg.ty)?;
         }
 
         write!(w, ") -> ")?;
 
         // fn return type.
         match mir.return_ty {
-            ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty)?,
-            ty::FnOutput::FnDiverging => write!(w, "!")?,
+            ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty),
+            ty::FnOutput::FnDiverging => write!(w, "!"),
         }
     } else {
         assert!(mir.arg_decls.is_empty());
-        write!(w, ": {} =", mir.return_ty.unwrap())?;
-    }
-
-    writeln!(w, " {{")?;
-
-    // User variable types (including the user's name in a comment).
-    for (i, var) in mir.var_decls.iter().enumerate() {
-        let mut_str = if var.mutability == Mutability::Mut {
-            "mut "
-        } else {
-            ""
-        };
-
-        let indented_var = format!("{}let {}{:?}: {};",
-                                   INDENT,
-                                   mut_str,
-                                   Lvalue::Var(i as u32),
-                                   var.ty);
-        writeln!(w, "{0:1$} // \"{2}\" in {3}",
-                 indented_var,
-                 ALIGN,
-                 var.name,
-                 comment(tcx, var.scope, var.span))?;
+        write!(w, ": {} =", mir.return_ty.unwrap())
     }
+}
 
+fn write_mir_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
     // Compiler-introduced temporary types.
-    for (i, temp) in mir.temp_decls.iter().enumerate() {
-        writeln!(w, "{}let mut {:?}: {};", INDENT, Lvalue::Temp(i as u32), temp.ty)?;
+    for (id, temp) in mir.temp_decls.iter_enumerated() {
+        writeln!(w, "{}let mut {:?}: {};", INDENT, id, temp.ty)?;
     }
 
     // Wrote any declaration? Add an empty line before the first block is printed.
diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs
new file mode 100644 (file)
index 0000000..c028504
--- /dev/null
@@ -0,0 +1,82 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::ty::TyCtxt;
+use rustc::mir::repr::*;
+use rustc::mir::transform::{MirPass, MirSource, Pass};
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+
+pub struct AddCallGuards;
+
+/**
+ * Breaks outgoing critical edges for call terminators in the MIR.
+ *
+ * Critical edges are edges that are neither the only edge leaving a
+ * block, nor the only edge entering one.
+ *
+ * When you want something to happen "along" an edge, you can either
+ * do at the end of the predecessor block, or at the start of the
+ * successor block. Critical edges have to be broken in order to prevent
+ * "edge actions" from affecting other edges. We need this for calls that are
+ * translated to LLVM invoke instructions, because invoke is a block terminator
+ * in LLVM so we can't insert any code to handle the call's result into the
+ * block that performs the call.
+ *
+ * This function will break those edges by inserting new blocks along them.
+ *
+ * NOTE: Simplify CFG will happily undo most of the work this pass does.
+ *
+ */
+
+impl<'tcx> MirPass<'tcx> for AddCallGuards {
+    fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) {
+        let pred_count: IndexVec<_, _> =
+            mir.predecessors().iter().map(|ps| ps.len()).collect();
+
+        // We need a place to store the new blocks generated
+        let mut new_blocks = Vec::new();
+
+        let cur_len = mir.basic_blocks().len();
+
+        for block in mir.basic_blocks_mut() {
+            match block.terminator {
+                Some(Terminator {
+                    kind: TerminatorKind::Call {
+                        destination: Some((_, ref mut destination)),
+                        cleanup: Some(_),
+                        ..
+                    }, source_info
+                }) if pred_count[*destination] > 1 => {
+                    // It's a critical edge, break it
+                    let call_guard = BasicBlockData {
+                        statements: vec![],
+                        is_cleanup: block.is_cleanup,
+                        terminator: Some(Terminator {
+                            source_info: source_info,
+                            kind: TerminatorKind::Goto { target: *destination }
+                        })
+                    };
+
+                    // Get the index it will be when inserted into the MIR
+                    let idx = cur_len + new_blocks.len();
+                    new_blocks.push(call_guard);
+                    *destination = BasicBlock::new(idx);
+                }
+                _ => {}
+            }
+        }
+
+        debug!("Broke {} N edges", new_blocks.len());
+
+        mir.basic_blocks_mut().extend(new_blocks);
+    }
+}
+
+impl Pass for AddCallGuards {}
diff --git a/src/librustc_mir/transform/break_cleanup_edges.rs b/src/librustc_mir/transform/break_cleanup_edges.rs
deleted file mode 100644 (file)
index 0eb6223..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use rustc::ty::TyCtxt;
-use rustc::mir::repr::*;
-use rustc::mir::transform::{MirPass, MirSource, Pass};
-
-use rustc_data_structures::bitvec::BitVector;
-
-use pretty;
-
-use traversal;
-
-pub struct BreakCleanupEdges;
-
-/**
- * Breaks outgoing critical edges for call terminators in the MIR.
- *
- * Critical edges are edges that are neither the only edge leaving a
- * block, nor the only edge entering one.
- *
- * When you want something to happen "along" an edge, you can either
- * do at the end of the predecessor block, or at the start of the
- * successor block. Critical edges have to be broken in order to prevent
- * "edge actions" from affecting other edges. We need this for calls that are
- * translated to LLVM invoke instructions, because invoke is a block terminator
- * in LLVM so we can't insert any code to handle the call's result into the
- * block that performs the call.
- *
- * This function will break those edges by inserting new blocks along them.
- *
- * NOTE: Simplify CFG will happily undo most of the work this pass does.
- *
- */
-
-impl<'tcx> MirPass<'tcx> for BreakCleanupEdges {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
-        let mut pred_count = vec![0u32; mir.basic_blocks.len()];
-
-        // Build the precedecessor map for the MIR
-        for (_, data) in traversal::preorder(mir) {
-            if let Some(ref term) = data.terminator {
-                for &tgt in term.successors().iter() {
-                    pred_count[tgt.index()] += 1;
-                }
-            }
-        }
-
-        let cleanup_map : BitVector = mir.basic_blocks
-            .iter().map(|bb| bb.is_cleanup).collect();
-
-        // We need a place to store the new blocks generated
-        let mut new_blocks = Vec::new();
-
-        let bbs = mir.all_basic_blocks();
-        let cur_len = mir.basic_blocks.len();
-
-        for &bb in &bbs {
-            let data = mir.basic_block_data_mut(bb);
-
-            if let Some(ref mut term) = data.terminator {
-                if term_is_invoke(term) {
-                    let term_span = term.span;
-                    let term_scope = term.scope;
-                    let succs = term.successors_mut();
-                    for tgt in succs {
-                        let num_preds = pred_count[tgt.index()];
-                        if num_preds > 1 {
-                            // It's a critical edge, break it
-                            let goto = Terminator {
-                                span: term_span,
-                                scope: term_scope,
-                                kind: TerminatorKind::Goto { target: *tgt }
-                            };
-                            let mut data = BasicBlockData::new(Some(goto));
-                            data.is_cleanup = cleanup_map.contains(tgt.index());
-
-                            // Get the index it will be when inserted into the MIR
-                            let idx = cur_len + new_blocks.len();
-                            new_blocks.push(data);
-                            *tgt = BasicBlock::new(idx);
-                        }
-                    }
-                }
-            }
-        }
-
-        pretty::dump_mir(tcx, "break_cleanup_edges", &0, src, mir, None);
-        debug!("Broke {} N edges", new_blocks.len());
-
-        mir.basic_blocks.extend_from_slice(&new_blocks);
-    }
-}
-
-impl Pass for BreakCleanupEdges {}
-
-// Returns true if the terminator is a call that would use an invoke in LLVM.
-fn term_is_invoke(term: &Terminator) -> bool {
-    match term.kind {
-        TerminatorKind::Call { cleanup: Some(_), .. } |
-        TerminatorKind::Drop { unwind: Some(_), .. } => true,
-        _ => false
-    }
-}
diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs
new file mode 100644 (file)
index 0000000..642adee
--- /dev/null
@@ -0,0 +1,73 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This pass just dumps MIR at a specified point.
+
+use std::fmt;
+
+use rustc::ty::TyCtxt;
+use rustc::mir::repr::*;
+use rustc::mir::transform::{Pass, MirPass, MirPassHook, MirSource};
+use pretty;
+
+pub struct Marker<'a>(pub &'a str);
+
+impl<'b, 'tcx> MirPass<'tcx> for Marker<'b> {
+    fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                    _src: MirSource, _mir: &mut Mir<'tcx>)
+    {}
+}
+
+impl<'b> Pass for Marker<'b> {
+    fn name(&self) -> &str { self.0 }
+}
+
+pub struct Disambiguator<'a> {
+    pass: &'a Pass,
+    is_after: bool
+}
+
+impl<'a> fmt::Display for Disambiguator<'a> {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let title = if self.is_after { "after" } else { "before" };
+        if let Some(fmt) = self.pass.disambiguator() {
+            write!(formatter, "{}-{}", fmt, title)
+        } else {
+            write!(formatter, "{}", title)
+        }
+    }
+}
+
+pub struct DumpMir;
+
+impl<'tcx> MirPassHook<'tcx> for DumpMir {
+    fn on_mir_pass<'a>(
+        &mut self,
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        src: MirSource,
+        mir: &Mir<'tcx>,
+        pass: &Pass,
+        is_after: bool)
+    {
+        pretty::dump_mir(
+            tcx,
+            pass.name(),
+            &Disambiguator {
+                pass: pass,
+                is_after: is_after
+            },
+            src,
+            mir,
+            None
+        );
+    }
+}
+
+impl<'b> Pass for DumpMir {}
index 0dcb7ef84d01df4bca25c8bf469c2589a93dc4f0..7b707b4adb69ac2aa2a7499a505948c5bb41565e 100644 (file)
@@ -8,11 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub mod remove_dead_blocks;
+pub mod simplify_branches;
 pub mod simplify_cfg;
 pub mod erase_regions;
 pub mod no_landing_pads;
 pub mod type_check;
-pub mod break_cleanup_edges;
+pub mod add_call_guards;
 pub mod promote_consts;
 pub mod qualify_consts;
+pub mod dump_mir;
index de05032fa558624e90f40206504bd942a1ca6db3..818f060ed445ccff0404d04554de1508991a515b 100644 (file)
@@ -24,17 +24,18 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
             TerminatorKind::Goto { .. } |
             TerminatorKind::Resume |
             TerminatorKind::Return |
+            TerminatorKind::Unreachable |
             TerminatorKind::If { .. } |
             TerminatorKind::Switch { .. } |
             TerminatorKind::SwitchInt { .. } => {
                 /* nothing to do */
             },
+            TerminatorKind::Call { cleanup: ref mut unwind, .. } |
+            TerminatorKind::Assert { cleanup: ref mut unwind, .. } |
+            TerminatorKind::DropAndReplace { ref mut unwind, .. } |
             TerminatorKind::Drop { ref mut unwind, .. } => {
                 unwind.take();
             },
-            TerminatorKind::Call { ref mut cleanup, .. } => {
-                cleanup.take();
-            },
         }
         self.super_terminator(bb, terminator);
     }
index 431568b004d3ddbda6ec75e2680e9c5efa18c8d2..4b551d6bb083cfa85eeb1af32293f67e1b6f21c8 100644 (file)
 
 use rustc::mir::repr::*;
 use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
+use rustc::mir::traversal::ReversePostorder;
 use rustc::ty::{self, TyCtxt};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use build::Location;
-use traversal::ReversePostorder;
+
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 
 use std::mem;
 
@@ -74,7 +76,7 @@ pub enum Candidate {
 }
 
 struct TempCollector {
-    temps: Vec<TempState>,
+    temps: IndexVec<Temp, TempState>,
     location: Location,
     span: Span
 }
@@ -89,7 +91,7 @@ impl<'tcx> Visitor<'tcx> for TempCollector {
                 return;
             }
 
-            let temp = &mut self.temps[index as usize];
+            let temp = &mut self.temps[index];
             if *temp == TempState::Undefined {
                 match context {
                     LvalueContext::Store |
@@ -117,18 +119,16 @@ impl<'tcx> Visitor<'tcx> for TempCollector {
         }
     }
 
+    fn visit_source_info(&mut self, source_info: &SourceInfo) {
+        self.span = source_info.span;
+    }
+
     fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
         assert_eq!(self.location.block, bb);
-        self.span = statement.span;
         self.super_statement(bb, statement);
         self.location.statement_index += 1;
     }
 
-    fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
-        self.span = terminator.span;
-        self.super_terminator(bb, terminator);
-    }
-
     fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
         self.location.statement_index = 0;
         self.location.block = bb;
@@ -136,9 +136,9 @@ impl<'tcx> Visitor<'tcx> for TempCollector {
     }
 }
 
-pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> Vec<TempState> {
+pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Temp, TempState> {
     let mut collector = TempCollector {
-        temps: vec![TempState::Undefined; mir.temp_decls.len()],
+        temps: IndexVec::from_elem(TempState::Undefined, &mir.temp_decls),
         location: Location {
             block: START_BLOCK,
             statement_index: 0
@@ -154,7 +154,7 @@ pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> Vec<TempState> {
 struct Promoter<'a, 'tcx: 'a> {
     source: &'a mut Mir<'tcx>,
     promoted: Mir<'tcx>,
-    temps: &'a mut Vec<TempState>,
+    temps: &'a mut IndexVec<Temp, TempState>,
 
     /// If true, all nested temps are also kept in the
     /// source MIR, not moved to the promoted MIR.
@@ -163,34 +163,37 @@ struct Promoter<'a, 'tcx: 'a> {
 
 impl<'a, 'tcx> Promoter<'a, 'tcx> {
     fn new_block(&mut self) -> BasicBlock {
-        let index = self.promoted.basic_blocks.len();
-        self.promoted.basic_blocks.push(BasicBlockData {
+        let span = self.promoted.span;
+        self.promoted.basic_blocks_mut().push(BasicBlockData {
             statements: vec![],
             terminator: Some(Terminator {
-                span: self.promoted.span,
-                scope: ScopeId::new(0),
+                source_info: SourceInfo {
+                    span: span,
+                    scope: ARGUMENT_VISIBILITY_SCOPE
+                },
                 kind: TerminatorKind::Return
             }),
             is_cleanup: false
-        });
-        BasicBlock::new(index)
+        })
     }
 
     fn assign(&mut self, dest: Lvalue<'tcx>, rvalue: Rvalue<'tcx>, span: Span) {
-        let data = self.promoted.basic_blocks.last_mut().unwrap();
+        let last = self.promoted.basic_blocks().last().unwrap();
+        let data = &mut self.promoted[last];
         data.statements.push(Statement {
-            span: span,
-            scope: ScopeId::new(0),
+            source_info: SourceInfo {
+                span: span,
+                scope: ARGUMENT_VISIBILITY_SCOPE
+            },
             kind: StatementKind::Assign(dest, rvalue)
         });
     }
 
     /// Copy the initialization of this temp to the
     /// promoted MIR, recursing through temps.
-    fn promote_temp(&mut self, index: u32) -> u32 {
-        let index = index as usize;
+    fn promote_temp(&mut self, temp: Temp) -> Temp {
         let old_keep_original = self.keep_original;
-        let (bb, stmt_idx) = match self.temps[index] {
+        let (bb, stmt_idx) = match self.temps[temp] {
             TempState::Defined {
                 location: Location { block, statement_index },
                 uses
@@ -200,13 +203,13 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                 }
                 (block, statement_index)
             }
-            temp =>  {
-                span_bug!(self.promoted.span, "tmp{} not promotable: {:?}",
-                          index, temp);
+            state =>  {
+                span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
+                          temp, state);
             }
         };
         if !self.keep_original {
-            self.temps[index] = TempState::PromotedOut;
+            self.temps[temp] = TempState::PromotedOut;
         }
 
         let no_stmts = self.source[bb].statements.len();
@@ -214,7 +217,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
         // First, take the Rvalue or Call out of the source MIR,
         // or duplicate it, depending on keep_original.
         let (mut rvalue, mut call) = (None, None);
-        let span = if stmt_idx < no_stmts {
+        let source_info = if stmt_idx < no_stmts {
             let statement = &mut self.source[bb].statements[stmt_idx];
             let StatementKind::Assign(_, ref mut rhs) = statement.kind;
             if self.keep_original {
@@ -223,11 +226,11 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                 let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
                 rvalue = Some(mem::replace(rhs, unit));
             }
-            statement.span
+            statement.source_info
         } else if self.keep_original {
             let terminator = self.source[bb].terminator().clone();
             call = Some(terminator.kind);
-            terminator.span
+            terminator.source_info
         } else {
             let terminator = self.source[bb].terminator_mut();
             let target = match terminator.kind {
@@ -242,13 +245,13 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                     dest.take().unwrap().1
                 }
                 ref kind => {
-                    span_bug!(terminator.span, "{:?} not promotable", kind);
+                    span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                 }
             };
             call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
                 target: target
             }));
-            terminator.span
+            terminator.source_info
         };
 
         // Then, recurse for components in the Rvalue or Call.
@@ -258,34 +261,32 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             self.visit_terminator_kind(bb, call.as_mut().unwrap());
         }
 
-        let new_index = self.promoted.temp_decls.len() as u32;
-        let new_temp = Lvalue::Temp(new_index);
-        self.promoted.temp_decls.push(TempDecl {
-            ty: self.source.temp_decls[index].ty
+        let new_temp = self.promoted.temp_decls.push(TempDecl {
+            ty: self.source.temp_decls[temp].ty
         });
 
         // Inject the Rvalue or Call into the promoted MIR.
         if stmt_idx < no_stmts {
-            self.assign(new_temp, rvalue.unwrap(), span);
+            self.assign(Lvalue::Temp(new_temp), rvalue.unwrap(), source_info.span);
         } else {
-            let last = self.promoted.basic_blocks.len() - 1;
+            let last = self.promoted.basic_blocks().last().unwrap();
             let new_target = self.new_block();
             let mut call = call.unwrap();
             match call {
                 TerminatorKind::Call { ref mut destination, ..}  => {
-                    *destination = Some((new_temp, new_target));
+                    *destination = Some((Lvalue::Temp(new_temp), new_target));
                 }
                 _ => bug!()
             }
-            let terminator = &mut self.promoted.basic_blocks[last].terminator_mut();
-            terminator.span = span;
+            let terminator = self.promoted[last].terminator_mut();
+            terminator.source_info.span = source_info.span;
             terminator.kind = call;
         }
 
         // Restore the old duplication state.
         self.keep_original = old_keep_original;
 
-        new_index
+        new_temp
     }
 
     fn promote_candidate(mut self, candidate: Candidate) {
@@ -294,7 +295,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             span: span,
             ty: self.promoted.return_ty.unwrap(),
             literal: Literal::Promoted {
-                index: self.source.promoted.len()
+                index: Promoted::new(self.source.promoted.len())
             }
         });
         let mut rvalue = match candidate {
@@ -323,8 +324,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
 /// Replaces all temporaries with their promoted counterparts.
 impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
     fn visit_lvalue(&mut self, lvalue: &mut Lvalue<'tcx>, context: LvalueContext) {
-        if let Lvalue::Temp(ref mut index) = *lvalue {
-            *index = self.promote_temp(*index);
+        if let Lvalue::Temp(ref mut temp) = *lvalue {
+            *temp = self.promote_temp(*temp);
         }
         self.super_lvalue(lvalue, context);
     }
@@ -332,7 +333,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
 
 pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                                     tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                    mut temps: Vec<TempState>,
+                                    mut temps: IndexVec<Temp, TempState>,
                                     candidates: Vec<Candidate>) {
     // Visit candidates in reverse, in case they're nested.
     for candidate in candidates.into_iter().rev() {
@@ -341,12 +342,12 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                 let statement = &mir[bb].statements[stmt_idx];
                 let StatementKind::Assign(ref dest, _) = statement.kind;
                 if let Lvalue::Temp(index) = *dest {
-                    if temps[index as usize] == TempState::PromotedOut {
+                    if temps[index] == TempState::PromotedOut {
                         // Already promoted.
                         continue;
                     }
                 }
-                (statement.span, mir.lvalue_ty(tcx, dest).to_ty(tcx))
+                (statement.source_info.span, mir.lvalue_ty(tcx, dest).to_ty(tcx))
             }
             Candidate::ShuffleIndices(bb) => {
                 let terminator = mir[bb].terminator();
@@ -355,30 +356,30 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                         mir.operand_ty(tcx, &args[2])
                     }
                     _ => {
-                        span_bug!(terminator.span,
+                        span_bug!(terminator.source_info.span,
                                   "expected simd_shuffleN call to promote");
                     }
                 };
-                (terminator.span, ty)
+                (terminator.source_info.span, ty)
             }
         };
 
         let mut promoter = Promoter {
             source: mir,
-            promoted: Mir {
-                basic_blocks: vec![],
-                scopes: vec![ScopeData {
+            promoted: Mir::new(
+                IndexVec::new(),
+                Some(VisibilityScopeData {
                     span: span,
                     parent_scope: None
-                }],
-                promoted: vec![],
-                return_ty: ty::FnConverging(ty),
-                var_decls: vec![],
-                arg_decls: vec![],
-                temp_decls: vec![],
-                upvar_decls: vec![],
-                span: span
-            },
+                }).into_iter().collect(),
+                IndexVec::new(),
+                ty::FnConverging(ty),
+                IndexVec::new(),
+                IndexVec::new(),
+                IndexVec::new(),
+                vec![],
+                span
+            ),
             temps: &mut temps,
             keep_original: false
         };
@@ -387,8 +388,8 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
     }
 
     // Eliminate assignments to, and drops of promoted temps.
-    let promoted = |index: u32| temps[index as usize] == TempState::PromotedOut;
-    for block in &mut mir.basic_blocks {
+    let promoted = |index: Temp| temps[index] == TempState::PromotedOut;
+    for block in mir.basic_blocks_mut() {
         block.statements.retain(|statement| {
             match statement.kind {
                 StatementKind::Assign(Lvalue::Temp(index), _) => {
@@ -399,7 +400,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
         });
         let terminator = block.terminator_mut();
         match terminator.kind {
-            TerminatorKind::Drop { value: Lvalue::Temp(index), target, .. } => {
+            TerminatorKind::Drop { location: Lvalue::Temp(index), target, .. } => {
                 if promoted(index) {
                     terminator.kind = TerminatorKind::Goto {
                         target: target
index 2e4400c834f2325a1ce689eddc842a44af107efc..1d00938fb25eb3d2c636fb0097ddf906d020c77d 100644 (file)
@@ -15,6 +15,7 @@
 //! diagnostics as to why a constant rvalue wasn't promoted.
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 use rustc::hir;
 use rustc::hir::def_id::DefId;
 use rustc::hir::intravisit::FnKind;
@@ -24,18 +25,18 @@ use rustc::ty::{self, TyCtxt, Ty};
 use rustc::ty::cast::CastTy;
 use rustc::mir::repr::*;
 use rustc::mir::mir_map::MirMap;
-use rustc::mir::transform::{Pass, MirMapPass, MirSource};
+use rustc::mir::traversal::{self, ReversePostorder};
+use rustc::mir::transform::{Pass, MirMapPass, MirPassHook, MirSource};
 use rustc::mir::visit::{LvalueContext, Visitor};
 use rustc::util::nodemap::DefIdMap;
 use syntax::abi::Abi;
-use syntax::codemap::Span;
 use syntax::feature_gate::UnstableFeatures;
+use syntax_pos::Span;
 
 use std::collections::hash_map::Entry;
 use std::fmt;
 
 use build::Location;
-use traversal::{self, ReversePostorder};
 
 use super::promote_consts::{self, Candidate, TempState};
 
@@ -141,12 +142,12 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     param_env: ty::ParameterEnvironment<'tcx>,
     qualif_map: &'a mut DefIdMap<Qualif>,
     mir_map: Option<&'a MirMap<'tcx>>,
-    temp_qualif: Vec<Option<Qualif>>,
+    temp_qualif: IndexVec<Temp, Option<Qualif>>,
     return_qualif: Option<Qualif>,
     qualif: Qualif,
     const_fn_arg_vars: BitVector,
     location: Location,
-    temp_promotion_state: Vec<TempState>,
+    temp_promotion_state: IndexVec<Temp, TempState>,
     promotion_candidates: Vec<Candidate>
 }
 
@@ -172,7 +173,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
             param_env: param_env,
             qualif_map: qualif_map,
             mir_map: mir_map,
-            temp_qualif: vec![None; mir.temp_decls.len()],
+            temp_qualif: IndexVec::from_elem(None, &mir.temp_decls),
             return_qualif: None,
             qualif: Qualif::empty(),
             const_fn_arg_vars: BitVector::new(mir.var_decls.len()),
@@ -301,22 +302,22 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
         // Only handle promotable temps in non-const functions.
         if self.mode == Mode::Fn {
             if let Lvalue::Temp(index) = *dest {
-                if self.temp_promotion_state[index as usize].is_promotable() {
-                    store(&mut self.temp_qualif[index as usize]);
+                if self.temp_promotion_state[index].is_promotable() {
+                    store(&mut self.temp_qualif[index]);
                 }
             }
             return;
         }
 
         match *dest {
-            Lvalue::Temp(index) => store(&mut self.temp_qualif[index as usize]),
+            Lvalue::Temp(index) => store(&mut self.temp_qualif[index]),
             Lvalue::ReturnPointer => store(&mut self.return_qualif),
 
             Lvalue::Projection(box Projection {
                 base: Lvalue::Temp(index),
                 elem: ProjectionElem::Deref
-            }) if self.mir.temp_decls[index as usize].ty.is_unique()
-               && self.temp_qualif[index as usize].map_or(false, |qualif| {
+            }) if self.mir.temp_decls[index].ty.is_unique()
+               && self.temp_qualif[index].map_or(false, |qualif| {
                     qualif.intersects(Qualif::NOT_CONST)
                }) => {
                 // Part of `box expr`, we should've errored
@@ -332,66 +333,11 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
         }
     }
 
-    /// Returns true if the block ends in a bounds check branch, i.e.:
-    /// len = Len(array);
-    /// cond = Lt(idx, len);
-    /// if cond {
-    ///     ...
-    /// } else {
-    ///     loc = (...);
-    ///     loc_ref = &loc;
-    ///     panic_bounds_check(loc_ref, idx, len);
-    /// }
-    fn is_bounds_check(&self, bb: BasicBlock,
-                       cond_op: &Operand<'tcx>,
-                       if_else: BasicBlock) -> bool {
-        use rustc::mir::repr::Lvalue::*;
-        use rustc::mir::repr::Operand::Consume;
-        use rustc::mir::repr::Rvalue::*;
-        use rustc::mir::repr::StatementKind::*;
-        use rustc::mir::repr::TerminatorKind::*;
-
-        let stmts = &self.mir[bb].statements;
-        let stmts_panic = &self.mir[if_else].statements;
-        if stmts.len() < 2 || stmts_panic.len() != 2 {
-            return false;
-        }
-
-        let all = (&stmts[stmts.len() - 2].kind,
-                   &stmts[stmts.len() - 1].kind,
-                   cond_op,
-                   &stmts_panic[0].kind,
-                   &stmts_panic[1].kind,
-                   &self.mir[if_else].terminator().kind);
-        match all {
-            (&Assign(Temp(len), Len(_)),
-             &Assign(Temp(cond), BinaryOp(BinOp::Lt, ref idx, Consume(Temp(len2)))),
-             /* if */ &Consume(Temp(cond2)), /* {...} else */
-             &Assign(Temp(loc), Aggregate(..)),
-             &Assign(Temp(loc_ref), Ref(_, _, Temp(loc2))),
-             &Call {
-                func: Operand::Constant(Constant {
-                    literal: Literal::Item { def_id, .. }, ..
-                }),
-                ref args,
-                destination: None,
-                ..
-            }) => {
-                len == len2 && cond == cond2 && loc == loc2 &&
-                args[0] == Consume(Temp(loc_ref)) &&
-                args[1] == *idx &&
-                args[2] == Consume(Temp(len)) &&
-                Some(def_id) == self.tcx.lang_items.panic_bounds_check_fn()
-            }
-            _ => false
-        }
-    }
-
     /// Qualify a whole const, static initializer or const fn.
     fn qualify_const(&mut self) -> Qualif {
         let mir = self.mir;
 
-        let mut seen_blocks = BitVector::new(mir.basic_blocks.len());
+        let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
         let mut bb = START_BLOCK;
         loop {
             seen_blocks.insert(bb.index());
@@ -402,6 +348,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                 TerminatorKind::Goto { target } |
                 // Drops are considered noops.
                 TerminatorKind::Drop { target, .. } |
+                TerminatorKind::Assert { target, .. } |
                 TerminatorKind::Call { destination: Some((_, target)), .. } => {
                     Some(target)
                 }
@@ -411,38 +358,33 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                     return Qualif::empty();
                 }
 
-                // Need to allow bounds checking branches.
-                TerminatorKind::If { ref cond, targets: (if_true, if_else) } => {
-                    if self.is_bounds_check(bb, cond, if_else) {
-                        Some(if_true)
-                    } else {
-                        None
-                    }
-                }
-
+                TerminatorKind::If {..} |
                 TerminatorKind::Switch {..} |
                 TerminatorKind::SwitchInt {..} |
-                TerminatorKind::Resume => None,
+                TerminatorKind::DropAndReplace { .. } |
+                TerminatorKind::Resume |
+                TerminatorKind::Unreachable => None,
 
                 TerminatorKind::Return => {
                     // Check for unused values. This usually means
                     // there are extra statements in the AST.
-                    for i in 0..mir.temp_decls.len() {
-                        if self.temp_qualif[i].is_none() {
+                    for temp in mir.temp_decls.indices() {
+                        if self.temp_qualif[temp].is_none() {
                             continue;
                         }
 
-                        let state = self.temp_promotion_state[i];
+                        let state = self.temp_promotion_state[temp];
                         if let TempState::Defined { location, uses: 0 } = state {
                             let data = &mir[location.block];
                             let stmt_idx = location.statement_index;
 
                             // Get the span for the initialization.
-                            if stmt_idx < data.statements.len() {
-                                self.span = data.statements[stmt_idx].span;
+                            let source_info = if stmt_idx < data.statements.len() {
+                                data.statements[stmt_idx].source_info
                             } else {
-                                self.span = data.terminator().span;
-                            }
+                                data.terminator().source_info
+                            };
+                            self.span = source_info.span;
 
                             // Treat this as a statement in the AST.
                             self.statement_like();
@@ -453,7 +395,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                     self.qualif = Qualif::NOT_CONST;
                     for index in 0..mir.var_decls.len() {
                         if !self.const_fn_arg_vars.contains(index) {
-                            self.assign(&Lvalue::Var(index as u32));
+                            self.assign(&Lvalue::Var(Var::new(index)));
                         }
                     }
 
@@ -508,11 +450,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 self.add(Qualif::NOT_CONST);
             }
             Lvalue::Temp(index) => {
-                if !self.temp_promotion_state[index as usize].is_promotable() {
+                if !self.temp_promotion_state[index].is_promotable() {
                     self.add(Qualif::NOT_PROMOTABLE);
                 }
 
-                if let Some(qualif) = self.temp_qualif[index as usize] {
+                if let Some(qualif) = self.temp_qualif[index] {
                     self.add(qualif);
                 } else {
                     self.not_const();
@@ -569,6 +511,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                         }
 
                         ProjectionElem::ConstantIndex {..} |
+                        ProjectionElem::Subslice {..} |
                         ProjectionElem::Downcast(..) => {
                             this.not_const()
                         }
@@ -629,6 +572,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
             Rvalue::Use(_) |
             Rvalue::Repeat(..) |
             Rvalue::UnaryOp(..) |
+            Rvalue::CheckedBinaryOp(..) |
             Rvalue::Cast(CastKind::ReifyFnPointer, _, _) |
             Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
             Rvalue::Cast(CastKind::Unsize, _, _) => {}
@@ -767,7 +711,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 }
             }
 
-            Rvalue::Slice {..} |
             Rvalue::InlineAsm {..} => {
                 self.not_const();
             }
@@ -881,7 +824,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
         // Check the allowed const fn argument forms.
         if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) {
-            if self.const_fn_arg_vars.insert(index as usize) {
+            if self.const_fn_arg_vars.insert(index.index()) {
                 // Direct use of an argument is permitted.
                 if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue {
                     return;
@@ -889,8 +832,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
                 // Avoid a generic error for other uses of arguments.
                 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
-                    let decl = &self.mir.var_decls[index as usize];
-                    span_err!(self.tcx.sess, decl.span, E0022,
+                    let decl = &self.mir.var_decls[index];
+                    span_err!(self.tcx.sess, decl.source_info.span, E0022,
                               "arguments of constant functions can only \
                                be immutable by-value bindings");
                     return;
@@ -901,16 +844,18 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
         self.assign(dest);
     }
 
+    fn visit_source_info(&mut self, source_info: &SourceInfo) {
+        self.span = source_info.span;
+    }
+
     fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
         assert_eq!(self.location.block, bb);
-        self.span = statement.span;
         self.nest(|this| this.super_statement(bb, statement));
         self.location.statement_index += 1;
     }
 
     fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
         assert_eq!(self.location.block, bb);
-        self.span = terminator.span;
         self.nest(|this| this.super_terminator(bb, terminator));
     }
 
@@ -964,7 +909,10 @@ pub struct QualifyAndPromoteConstants;
 impl Pass for QualifyAndPromoteConstants {}
 
 impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &mut MirMap<'tcx>) {
+    fn run_pass<'a>(&mut self,
+                    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                    map: &mut MirMap<'tcx>,
+                    hooks: &mut [Box<for<'s> MirPassHook<'s>>]) {
         let mut qualif_map = DefIdMap();
 
         // First, visit `const` items, potentially recursing, to get
@@ -1000,6 +948,10 @@ impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
             };
             let param_env = ty::ParameterEnvironment::for_item(tcx, id);
 
+            for hook in &mut *hooks {
+                hook.on_mir_pass(tcx, src, mir, self, false);
+            }
+
             if mode == Mode::Fn || mode == Mode::ConstFn {
                 // This is ugly because Qualifier holds onto mir,
                 // which can't be mutated until its scope ends.
@@ -1027,6 +979,10 @@ impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
                 qualifier.qualify_const();
             }
 
+            for hook in &mut *hooks {
+                hook.on_mir_pass(tcx, src, mir, self, true);
+            }
+
             // Statics must be Sync.
             if mode == Mode::Static {
                 let ty = mir.return_ty.unwrap();
diff --git a/src/librustc_mir/transform/remove_dead_blocks.rs b/src/librustc_mir/transform/remove_dead_blocks.rs
deleted file mode 100644 (file)
index 44f3ce7..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! A pass that erases the contents of dead blocks. This pass must
-//! run before any analysis passes because some of the dead blocks
-//! can be ill-typed.
-//!
-//! The main problem is that typeck lets most blocks whose end is not
-//! reachable have an arbitrary return type, rather than having the
-//! usual () return type (as a note, typeck's notion of reachability
-//! is in fact slightly weaker than MIR CFG reachability - see #31617).
-//!
-//! A standard example of the situation is:
-//! ```rust
-//!   fn example() {
-//!       let _a: char = { return; };
-//!   }
-//! ```
-//!
-//! Here the block (`{ return; }`) has the return type `char`,
-//! rather than `()`, but the MIR we naively generate still contains
-//! the `_a = ()` write in the unreachable block "after" the return.
-//!
-//! As we have to run this pass even when we want to debug the MIR,
-//! this pass just replaces the blocks with empty "return" blocks
-//! and does not renumber anything.
-
-use rustc_data_structures::bitvec::BitVector;
-use rustc::ty::TyCtxt;
-use rustc::mir::repr::*;
-use rustc::mir::transform::{Pass, MirPass, MirSource};
-
-pub struct RemoveDeadBlocks;
-
-impl<'tcx> MirPass<'tcx> for RemoveDeadBlocks {
-    fn run_pass<'a>(&mut self, _: TyCtxt<'a, 'tcx, 'tcx>,
-                    _: MirSource, mir: &mut Mir<'tcx>) {
-        let mut seen = BitVector::new(mir.basic_blocks.len());
-        // This block is always required.
-        seen.insert(START_BLOCK.index());
-
-        let mut worklist = Vec::with_capacity(4);
-        worklist.push(START_BLOCK);
-        while let Some(bb) = worklist.pop() {
-            for succ in mir.basic_block_data(bb).terminator().successors().iter() {
-                if seen.insert(succ.index()) {
-                    worklist.push(*succ);
-                }
-            }
-        }
-        retain_basic_blocks(mir, &seen);
-    }
-}
-
-impl Pass for RemoveDeadBlocks {}
-
-/// Mass removal of basic blocks to keep the ID-remapping cheap.
-fn retain_basic_blocks(mir: &mut Mir, keep: &BitVector) {
-    let num_blocks = mir.basic_blocks.len();
-
-    let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
-    let mut used_blocks = 0;
-    for alive_index in keep.iter() {
-        replacements[alive_index] = BasicBlock::new(used_blocks);
-        if alive_index != used_blocks {
-            // Swap the next alive block data with the current available slot. Since alive_index is
-            // non-decreasing this is a valid operation.
-            mir.basic_blocks.swap(alive_index, used_blocks);
-        }
-        used_blocks += 1;
-    }
-    mir.basic_blocks.truncate(used_blocks);
-
-    for bb in mir.all_basic_blocks() {
-        for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() {
-            *target = replacements[target.index()];
-        }
-    }
-}
diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs
new file mode 100644 (file)
index 0000000..b4960c6
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A pass that simplifies branches when their condition is known.
+
+use rustc::ty::TyCtxt;
+use rustc::middle::const_val::ConstVal;
+use rustc::mir::transform::{MirPass, MirSource, Pass};
+use rustc::mir::repr::*;
+
+use std::fmt;
+
+pub struct SimplifyBranches<'a> { label: &'a str }
+
+impl<'a> SimplifyBranches<'a> {
+    pub fn new(label: &'a str) -> Self {
+        SimplifyBranches { label: label }
+    }
+}
+
+impl<'l, 'tcx> MirPass<'tcx> for SimplifyBranches<'l> {
+    fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) {
+        for block in mir.basic_blocks_mut() {
+            let terminator = block.terminator_mut();
+            terminator.kind = match terminator.kind {
+                TerminatorKind::If { ref targets, cond: Operand::Constant(Constant {
+                    literal: Literal::Value {
+                        value: ConstVal::Bool(cond)
+                    }, ..
+                }) } => {
+                    if cond {
+                        TerminatorKind::Goto { target: targets.0 }
+                    } else {
+                        TerminatorKind::Goto { target: targets.1 }
+                    }
+                }
+
+                TerminatorKind::Assert { target, cond: Operand::Constant(Constant {
+                    literal: Literal::Value {
+                        value: ConstVal::Bool(cond)
+                    }, ..
+                }), expected, .. } if cond == expected => {
+                    TerminatorKind::Goto { target: target }
+                }
+
+                _ => continue
+            };
+        }
+    }
+}
+
+impl<'l> Pass for SimplifyBranches<'l> {
+    fn disambiguator<'a>(&'a self) -> Option<Box<fmt::Display+'a>> {
+        Some(Box::new(self.label))
+    }
+
+    // avoid calling `type_name` - it contains `<'static>`
+    fn name(&self) -> &str { "SimplifyBranches" }
+}
index 526157a49c734252cb1417605bb48d40056233e8..c0e7e54050adf7674eda4a86f99c54eec0a1c6a4 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+//! A pass that removes various redundancies in the CFG. It should be
+//! called after every significant CFG modification to tidy things
+//! up.
+//!
+//! This pass must also be run before any analysis passes because it removes
+//! dead blocks, and some of these can be ill-typed.
+//!
+//! The cause of that is that typeck lets most blocks whose end is not
+//! reachable have an arbitrary return type, rather than having the
+//! usual () return type (as a note, typeck's notion of reachability
+//! is in fact slightly weaker than MIR CFG reachability - see #31617).
+//!
+//! A standard example of the situation is:
+//! ```rust
+//!   fn example() {
+//!       let _a: char = { return; };
+//!   }
+//! ```
+//!
+//! Here the block (`{ return; }`) has the return type `char`,
+//! rather than `()`, but the MIR we naively generate still contains
+//! the `_a = ()` write in the unreachable block "after" the return.
+
+
 use rustc_data_structures::bitvec::BitVector;
-use rustc::middle::const_val::ConstVal;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc::ty::TyCtxt;
 use rustc::mir::repr::*;
 use rustc::mir::transform::{MirPass, MirSource, Pass};
-use pretty;
-use std::mem;
-
-use super::remove_dead_blocks::RemoveDeadBlocks;
+use rustc::mir::traversal;
+use std::fmt;
 
-use traversal;
+pub struct SimplifyCfg<'a> { label: &'a str }
 
-pub struct SimplifyCfg;
-
-impl SimplifyCfg {
-    pub fn new() -> SimplifyCfg {
-        SimplifyCfg
+impl<'a> SimplifyCfg<'a> {
+    pub fn new(label: &'a str) -> Self {
+        SimplifyCfg { label: label }
     }
 }
 
-impl<'tcx> MirPass<'tcx> for SimplifyCfg {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
-        simplify_branches(mir);
-        RemoveDeadBlocks.run_pass(tcx, src, mir);
-        merge_consecutive_blocks(mir);
-        RemoveDeadBlocks.run_pass(tcx, src, mir);
-        pretty::dump_mir(tcx, "simplify_cfg", &0, src, mir, None);
+impl<'l, 'tcx> MirPass<'tcx> for SimplifyCfg<'l> {
+    fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) {
+        CfgSimplifier::new(mir).simplify();
+        remove_dead_blocks(mir);
 
         // FIXME: Should probably be moved into some kind of pass manager
-        mir.basic_blocks.shrink_to_fit();
+        mir.basic_blocks_mut().raw.shrink_to_fit();
+    }
+}
+
+impl<'l> Pass for SimplifyCfg<'l> {
+    fn disambiguator<'a>(&'a self) -> Option<Box<fmt::Display+'a>> {
+        Some(Box::new(self.label))
     }
+
+    // avoid calling `type_name` - it contains `<'static>`
+    fn name(&self) -> &str { "SimplifyCfg" }
+}
+
+pub struct CfgSimplifier<'a, 'tcx: 'a> {
+    basic_blocks: &'a mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+    pred_count: IndexVec<BasicBlock, u32>
 }
 
-impl Pass for SimplifyCfg {}
+impl<'a, 'tcx: 'a> CfgSimplifier<'a, 'tcx> {
+    fn new(mir: &'a mut Mir<'tcx>) -> Self {
+        let mut pred_count = IndexVec::from_elem(0u32, mir.basic_blocks());
 
-fn merge_consecutive_blocks(mir: &mut Mir) {
-    // Build the precedecessor map for the MIR
-    let mut pred_count = vec![0u32; mir.basic_blocks.len()];
-    for (_, data) in traversal::preorder(mir) {
-        if let Some(ref term) = data.terminator {
-            for &tgt in term.successors().iter() {
-                pred_count[tgt.index()] += 1;
+        // we can't use mir.predecessors() here because that counts
+        // dead blocks, which we don't want to.
+        for (_, data) in traversal::preorder(mir) {
+            if let Some(ref term) = data.terminator {
+                for &tgt in term.successors().iter() {
+                    pred_count[tgt] += 1;
+                }
             }
         }
+
+        let basic_blocks = mir.basic_blocks_mut();
+
+        CfgSimplifier {
+            basic_blocks: basic_blocks,
+            pred_count: pred_count
+        }
     }
 
-    loop {
-        let mut changed = false;
-        let mut seen = BitVector::new(mir.basic_blocks.len());
-        let mut worklist = vec![START_BLOCK];
-        while let Some(bb) = worklist.pop() {
-            // Temporarily take ownership of the terminator we're modifying to keep borrowck happy
-            let mut terminator = mir.basic_block_data_mut(bb).terminator.take()
-                .expect("invalid terminator state");
-
-            // See if we can merge the target block into this one
-            loop {
-                let mut inner_change = false;
-
-                if let TerminatorKind::Goto { target } = terminator.kind {
-                    // Don't bother trying to merge a block into itself
-                    if target == bb {
-                        break;
-                    }
-
-                    let num_insts = mir.basic_block_data(target).statements.len();
-                    match mir.basic_block_data(target).terminator().kind {
-                        TerminatorKind::Goto { target: new_target } if num_insts == 0 => {
-                            inner_change = true;
-                            terminator.kind = TerminatorKind::Goto { target: new_target };
-                            pred_count[target.index()] -= 1;
-                            pred_count[new_target.index()] += 1;
-                        }
-                        _ if pred_count[target.index()] == 1 => {
-                            inner_change = true;
-                            let mut stmts = Vec::new();
-                            {
-                                let target_data = mir.basic_block_data_mut(target);
-                                mem::swap(&mut stmts, &mut target_data.statements);
-                                mem::swap(&mut terminator, target_data.terminator_mut());
-                            }
-
-                            mir.basic_block_data_mut(bb).statements.append(&mut stmts);
-                        }
-                        _ => {}
-                    };
+    fn simplify(mut self) {
+        loop {
+            let mut changed = false;
+
+            for bb in (0..self.basic_blocks.len()).map(BasicBlock::new) {
+                if self.pred_count[bb] == 0 {
+                    continue
                 }
 
-                for target in terminator.successors_mut() {
-                    let new_target = match final_target(mir, *target) {
-                        Some(new_target) => new_target,
-                        None if mir.basic_block_data(bb).statements.is_empty() => bb,
-                        None => continue
-                    };
-                    if *target != new_target {
-                        inner_change = true;
-                        pred_count[target.index()] -= 1;
-                        pred_count[new_target.index()] += 1;
-                        *target = new_target;
-                    }
+                debug!("simplifying {:?}", bb);
+
+                let mut terminator = self.basic_blocks[bb].terminator.take()
+                    .expect("invalid terminator state");
+
+                for successor in terminator.successors_mut() {
+                    self.collapse_goto_chain(successor, &mut changed);
                 }
 
-                changed |= inner_change;
-                if !inner_change {
-                    break;
+                let mut new_stmts = vec![];
+                let mut inner_changed = true;
+                while inner_changed {
+                    inner_changed = false;
+                    inner_changed |= self.simplify_branch(&mut terminator);
+                    inner_changed |= self.merge_successor(&mut new_stmts, &mut terminator);
+                    changed |= inner_changed;
                 }
-            }
 
-            mir.basic_block_data_mut(bb).terminator = Some(terminator);
+                self.basic_blocks[bb].statements.extend(new_stmts);
+                self.basic_blocks[bb].terminator = Some(terminator);
 
-            for succ in mir.basic_block_data(bb).terminator().successors().iter() {
-                if seen.insert(succ.index()) {
-                    worklist.push(*succ);
-                }
+                changed |= inner_changed;
             }
-        }
 
-        if !changed {
-            break;
+            if !changed { break }
         }
     }
-}
 
-// Find the target at the end of the jump chain, return None if there is a loop
-fn final_target(mir: &Mir, mut target: BasicBlock) -> Option<BasicBlock> {
-    // Keep track of already seen blocks to detect loops
-    let mut seen: Vec<BasicBlock> = Vec::with_capacity(8);
-
-    while mir.basic_block_data(target).statements.is_empty() {
-        // NB -- terminator may have been swapped with `None` in
-        // merge_consecutive_blocks, in which case we have a cycle and just want
-        // to stop
-        match mir.basic_block_data(target).terminator {
-            Some(Terminator { kind: TerminatorKind::Goto { target: next }, .. }) =>  {
-                if seen.contains(&next) {
-                    return None;
-                }
-                seen.push(next);
-                target = next;
+    // Collapse a goto chain starting from `start`
+    fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) {
+        let mut terminator = match self.basic_blocks[*start] {
+            BasicBlockData {
+                ref statements,
+                terminator: ref mut terminator @ Some(Terminator {
+                    kind: TerminatorKind::Goto { .. }, ..
+                }), ..
+            } if statements.is_empty() => terminator.take(),
+            // if `terminator` is None, this means we are in a loop. In that
+            // case, let all the loop collapse to its entry.
+            _ => return
+        };
+
+        let target = match terminator {
+            Some(Terminator { kind: TerminatorKind::Goto { ref mut target }, .. }) => {
+                self.collapse_goto_chain(target, changed);
+                *target
             }
-            _ => break
-        }
-    }
+            _ => unreachable!()
+        };
+        self.basic_blocks[*start].terminator = terminator;
 
-    Some(target)
-}
+        debug!("collapsing goto chain from {:?} to {:?}", *start, target);
 
-fn simplify_branches(mir: &mut Mir) {
-    loop {
-        let mut changed = false;
-
-        for bb in mir.all_basic_blocks() {
-            let basic_block = mir.basic_block_data_mut(bb);
-            let mut terminator = basic_block.terminator_mut();
-            terminator.kind = match terminator.kind {
-                TerminatorKind::If { ref targets, .. } if targets.0 == targets.1 => {
-                    changed = true;
-                    TerminatorKind::Goto { target: targets.0 }
-                }
+        *changed |= *start != target;
+        self.pred_count[target] += 1;
+        self.pred_count[*start] -= 1;
+        *start = target;
+    }
 
-                TerminatorKind::If { ref targets, cond: Operand::Constant(Constant {
-                    literal: Literal::Value {
-                        value: ConstVal::Bool(cond)
-                    }, ..
-                }) } => {
-                    changed = true;
-                    if cond {
-                        TerminatorKind::Goto { target: targets.0 }
-                    } else {
-                        TerminatorKind::Goto { target: targets.1 }
-                    }
-                }
+    // merge a block with 1 `goto` predecessor to its parent
+    fn merge_successor(&mut self,
+                       new_stmts: &mut Vec<Statement<'tcx>>,
+                       terminator: &mut Terminator<'tcx>)
+                       -> bool
+    {
+        let target = match terminator.kind {
+            TerminatorKind::Goto { target }
+                if self.pred_count[target] == 1
+                => target,
+            _ => return false
+        };
+
+        debug!("merging block {:?} into {:?}", target, terminator);
+        *terminator = match self.basic_blocks[target].terminator.take() {
+            Some(terminator) => terminator,
+            None => {
+                // unreachable loop - this should not be possible, as we
+                // don't strand blocks, but handle it correctly.
+                return false
+            }
+        };
+        new_stmts.extend(self.basic_blocks[target].statements.drain(..));
+        self.pred_count[target] = 0;
 
-                TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => {
-                    TerminatorKind::Goto { target: targets[0] }
+        true
+    }
+
+    // turn a branch with all successors identical to a goto
+    fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool {
+        match terminator.kind {
+            TerminatorKind::If { .. } |
+            TerminatorKind::Switch { .. } |
+            TerminatorKind::SwitchInt { .. } => {},
+            _ => return false
+        };
+
+        let first_succ = {
+            let successors = terminator.successors();
+            if let Some(&first_succ) = terminator.successors().get(0) {
+                if successors.iter().all(|s| *s == first_succ) {
+                    self.pred_count[first_succ] -= (successors.len()-1) as u32;
+                    first_succ
+                } else {
+                    return false
                 }
-                _ => continue
+            } else {
+                return false
             }
+        };
+
+        debug!("simplifying branch {:?}", terminator);
+        terminator.kind = TerminatorKind::Goto { target: first_succ };
+        true
+    }
+}
+
+fn remove_dead_blocks(mir: &mut Mir) {
+    let mut seen = BitVector::new(mir.basic_blocks().len());
+    for (bb, _) in traversal::preorder(mir) {
+        seen.insert(bb.index());
+    }
+
+    let basic_blocks = mir.basic_blocks_mut();
+
+    let num_blocks = basic_blocks.len();
+    let mut replacements : Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
+    let mut used_blocks = 0;
+    for alive_index in seen.iter() {
+        replacements[alive_index] = BasicBlock::new(used_blocks);
+        if alive_index != used_blocks {
+            // Swap the next alive block data with the current available slot. Since alive_index is
+            // non-decreasing this is a valid operation.
+            basic_blocks.raw.swap(alive_index, used_blocks);
         }
+        used_blocks += 1;
+    }
+    basic_blocks.raw.truncate(used_blocks);
 
-        if !changed {
-            break;
+    for block in basic_blocks {
+        for target in block.terminator_mut().successors_mut() {
+            *target = replacements[target.index()];
         }
     }
 }
index 40157aa934c6571999263021aaa3fcfe4974f084..db49e1e040791803103f66e7e34742e5d4b60248 100644 (file)
@@ -22,7 +22,9 @@ use rustc::mir::tcx::LvalueTy;
 use rustc::mir::transform::{MirPass, MirSource, Pass};
 use rustc::mir::visit::{self, Visitor};
 use std::fmt;
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
+
+use rustc_data_structures::indexed_vec::Idx;
 
 macro_rules! span_mirbug {
     ($context:expr, $elem:expr, $($message:tt)*) => ({
@@ -118,10 +120,6 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
         self.cx.infcx.tcx
     }
 
-    fn infcx(&self) -> &'a InferCtxt<'a, 'gcx, 'tcx> {
-        self.cx.infcx
-    }
-
     fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
         if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() {
             span_mirbug_and_err!(self, parent, "bad type {:?}", ty)
@@ -133,11 +131,9 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
     fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>) -> LvalueTy<'tcx> {
         debug!("sanitize_lvalue: {:?}", lvalue);
         match *lvalue {
-            Lvalue::Var(index) => LvalueTy::Ty { ty: self.mir.var_decls[index as usize].ty },
-            Lvalue::Temp(index) =>
-                LvalueTy::Ty { ty: self.mir.temp_decls[index as usize].ty },
-            Lvalue::Arg(index) =>
-                LvalueTy::Ty { ty: self.mir.arg_decls[index as usize].ty },
+            Lvalue::Var(index) => LvalueTy::Ty { ty: self.mir.var_decls[index].ty },
+            Lvalue::Temp(index) => LvalueTy::Ty { ty: self.mir.temp_decls[index].ty },
+            Lvalue::Arg(index) => LvalueTy::Ty { ty: self.mir.arg_decls[index].ty },
             Lvalue::Static(def_id) =>
                 LvalueTy::Ty { ty: self.tcx().lookup_item_type(def_id).ty },
             Lvalue::ReturnPointer => {
@@ -207,6 +203,26 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
                     })
                 }
             }
+            ProjectionElem::Subslice { from, to } => {
+                LvalueTy::Ty {
+                    ty: match base_ty.sty {
+                        ty::TyArray(inner, size) => {
+                            let min_size = (from as usize) + (to as usize);
+                            if let Some(rest_size) = size.checked_sub(min_size) {
+                                tcx.mk_array(inner, rest_size)
+                            } else {
+                                span_mirbug_and_err!(
+                                    self, lvalue, "taking too-small slice of {:?}", base_ty)
+                            }
+                        }
+                        ty::TySlice(..) => base_ty,
+                        _ => {
+                            span_mirbug_and_err!(
+                                self, lvalue, "slice of non-array {:?}", base_ty)
+                        }
+                    }
+                }
+            }
             ProjectionElem::Downcast(adt_def1, index) =>
                 match base_ty.sty {
                     ty::TyEnum(adt_def, substs) if adt_def == adt_def1 => {
@@ -292,30 +308,11 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
         };
 
         if let Some(field) = variant.fields.get(field.index()) {
-            Ok(self.normalize(field.ty(tcx, substs)))
+            Ok(self.cx.normalize(&field.ty(tcx, substs)))
         } else {
             Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
         }
     }
-
-    fn normalize(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let infcx = self.infcx();
-        let mut selcx = traits::SelectionContext::new(infcx);
-        let cause = traits::ObligationCause::misc(self.last_span, 0);
-        let traits::Normalized { value: ty, obligations } =
-            traits::normalize(&mut selcx, cause, &ty);
-
-        debug!("normalize: ty={:?} obligations={:?}",
-               ty,
-               obligations);
-
-        let mut fulfill_cx = &mut self.cx.fulfillment_cx;
-        for obligation in obligations {
-            fulfill_cx.register_predicate_obligation(infcx, obligation);
-        }
-
-        ty
-    }
 }
 
 pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
@@ -373,7 +370,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn check_terminator(&self,
+    fn check_terminator(&mut self,
                         mir: &Mir<'tcx>,
                         term: &Terminator<'tcx>) {
         debug!("check_terminator: {:?}", term);
@@ -382,10 +379,25 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             TerminatorKind::Goto { .. } |
             TerminatorKind::Resume |
             TerminatorKind::Return |
+            TerminatorKind::Unreachable |
             TerminatorKind::Drop { .. } => {
                 // no checks needed for these
             }
 
+
+            TerminatorKind::DropAndReplace {
+                ref location,
+                ref value,
+                ..
+            } => {
+                let lv_ty = mir.lvalue_ty(tcx, location).to_ty(tcx);
+                let rv_ty = mir.operand_ty(tcx, value);
+                if let Err(terr) = self.sub_types(self.last_span, rv_ty, lv_ty) {
+                    span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}",
+                                 lv_ty, rv_ty, terr);
+                }
+            }
+
             TerminatorKind::If { ref cond, .. } => {
                 let cond_ty = mir.operand_ty(tcx, cond);
                 match cond_ty.sty {
@@ -431,6 +443,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     }
                 };
                 let sig = tcx.erase_late_bound_regions(&func_ty.sig);
+                let sig = self.normalize(&sig);
                 self.check_call_dest(mir, term, &sig, destination);
 
                 if self.is_box_free(func) {
@@ -439,6 +452,21 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     self.check_call_inputs(mir, term, &sig, args);
                 }
             }
+            TerminatorKind::Assert { ref cond, ref msg, .. } => {
+                let cond_ty = mir.operand_ty(tcx, cond);
+                if cond_ty != tcx.types.bool {
+                    span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
+                }
+
+                if let AssertMessage::BoundsCheck { ref len, ref index } = *msg {
+                    if mir.operand_ty(tcx, len) != tcx.types.usize {
+                        span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
+                    }
+                    if mir.operand_ty(tcx, index) != tcx.types.usize {
+                        span_mirbug!(self, index, "bounds-check index non-usize {:?}", index)
+                    }
+                }
+            }
         }
     }
 
@@ -541,21 +569,106 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         }
     }
 
+    fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>)
+    {
+        let is_cleanup = block.is_cleanup;
+        self.last_span = block.terminator().source_info.span;
+        match block.terminator().kind {
+            TerminatorKind::Goto { target } =>
+                self.assert_iscleanup(mir, block, target, is_cleanup),
+            TerminatorKind::If { targets: (on_true, on_false), .. } => {
+                self.assert_iscleanup(mir, block, on_true, is_cleanup);
+                self.assert_iscleanup(mir, block, on_false, is_cleanup);
+            }
+            TerminatorKind::Switch { ref targets, .. } |
+            TerminatorKind::SwitchInt { ref targets, .. } => {
+                for target in targets {
+                    self.assert_iscleanup(mir, block, *target, is_cleanup);
+                }
+            }
+            TerminatorKind::Resume => {
+                if !is_cleanup {
+                    span_mirbug!(self, block, "resume on non-cleanup block!")
+                }
+            }
+            TerminatorKind::Return => {
+                if is_cleanup {
+                    span_mirbug!(self, block, "return on cleanup block")
+                }
+            }
+            TerminatorKind::Unreachable => {}
+            TerminatorKind::Drop { target, unwind, .. } |
+            TerminatorKind::DropAndReplace { target, unwind, .. } |
+            TerminatorKind::Assert { target, cleanup: unwind, .. } => {
+                self.assert_iscleanup(mir, block, target, is_cleanup);
+                if let Some(unwind) = unwind {
+                    if is_cleanup {
+                        span_mirbug!(self, block, "unwind on cleanup block")
+                    }
+                    self.assert_iscleanup(mir, block, unwind, true);
+                }
+            }
+            TerminatorKind::Call { ref destination, cleanup, .. } => {
+                if let &Some((_, target)) = destination {
+                    self.assert_iscleanup(mir, block, target, is_cleanup);
+                }
+                if let Some(cleanup) = cleanup {
+                    if is_cleanup {
+                        span_mirbug!(self, block, "cleanup on cleanup block")
+                    }
+                    self.assert_iscleanup(mir, block, cleanup, true);
+                }
+            }
+        }
+    }
+
+    fn assert_iscleanup(&mut self,
+                        mir: &Mir<'tcx>,
+                        ctxt: &fmt::Debug,
+                        bb: BasicBlock,
+                        iscleanuppad: bool)
+    {
+        if mir[bb].is_cleanup != iscleanuppad {
+            span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}",
+                         bb, iscleanuppad);
+        }
+    }
+
     fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
         self.last_span = mir.span;
         debug!("run_on_mir: {:?}", mir.span);
-        for block in &mir.basic_blocks {
+        for block in mir.basic_blocks() {
             for stmt in &block.statements {
-                if stmt.span != DUMMY_SP {
-                    self.last_span = stmt.span;
+                if stmt.source_info.span != DUMMY_SP {
+                    self.last_span = stmt.source_info.span;
                 }
                 self.check_stmt(mir, stmt);
             }
 
-            if let Some(ref terminator) = block.terminator {
-                self.check_terminator(mir, terminator);
-            }
+            self.check_terminator(mir, block.terminator());
+            self.check_iscleanup(mir, block);
+        }
+    }
+
+
+    fn normalize<T>(&mut self, value: &T) -> T
+        where T: fmt::Debug + TypeFoldable<'tcx>
+    {
+        let mut selcx = traits::SelectionContext::new(self.infcx);
+        let cause = traits::ObligationCause::misc(self.last_span, 0);
+        let traits::Normalized { value, obligations } =
+            traits::normalize(&mut selcx, cause, value);
+
+        debug!("normalize: value={:?} obligations={:?}",
+               value,
+               obligations);
+
+        let mut fulfill_cx = &mut self.fulfillment_cx;
+        for obligation in obligations {
+            fulfill_cx.register_predicate_obligation(self.infcx, obligation);
         }
+
+        value
     }
 
     fn verify_obligations(&mut self, mir: &Mir<'tcx>) {
diff --git a/src/librustc_mir/traversal.rs b/src/librustc_mir/traversal.rs
deleted file mode 100644 (file)
index c58b5c8..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use std::vec;
-
-use rustc_data_structures::bitvec::BitVector;
-
-use rustc::mir::repr::*;
-
-/// Preorder traversal of a graph.
-///
-/// Preorder traversal is when each node is visited before an of it's
-/// successors
-///
-/// ```text
-///
-///         A
-///        / \
-///       /   \
-///      B     C
-///       \   /
-///        \ /
-///         D
-/// ```
-///
-/// A preorder traversal of this graph is either `A B D C` or `A C D B`
-#[derive(Clone)]
-pub struct Preorder<'a, 'tcx: 'a> {
-    mir: &'a Mir<'tcx>,
-    visited: BitVector,
-    worklist: Vec<BasicBlock>,
-}
-
-impl<'a, 'tcx> Preorder<'a, 'tcx> {
-    pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> Preorder<'a, 'tcx> {
-        let worklist = vec![root];
-
-        Preorder {
-            mir: mir,
-            visited: BitVector::new(mir.basic_blocks.len()),
-            worklist: worklist
-        }
-    }
-}
-
-pub fn preorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> Preorder<'a, 'tcx> {
-    Preorder::new(mir, START_BLOCK)
-}
-
-impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
-    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
-
-    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
-        while let Some(idx) = self.worklist.pop() {
-            if !self.visited.insert(idx.index()) {
-                continue;
-            }
-
-            let data = self.mir.basic_block_data(idx);
-
-            if let Some(ref term) = data.terminator {
-                for &succ in term.successors().iter() {
-                    self.worklist.push(succ);
-                }
-            }
-
-            return Some((idx, data));
-        }
-
-        None
-    }
-}
-
-/// Postorder traversal of a graph.
-///
-/// Postorder traversal is when each node is visited after all of it's
-/// successors, except when the successor is only reachable by a back-edge
-///
-///
-/// ```text
-///
-///         A
-///        / \
-///       /   \
-///      B     C
-///       \   /
-///        \ /
-///         D
-/// ```
-///
-/// A Postorder traversal of this graph is `D B C A` or `D C B A`
-pub struct Postorder<'a, 'tcx: 'a> {
-    mir: &'a Mir<'tcx>,
-    visited: BitVector,
-    visit_stack: Vec<(BasicBlock, vec::IntoIter<BasicBlock>)>
-}
-
-impl<'a, 'tcx> Postorder<'a, 'tcx> {
-    pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> {
-        let mut po = Postorder {
-            mir: mir,
-            visited: BitVector::new(mir.basic_blocks.len()),
-            visit_stack: Vec::new()
-        };
-
-
-        let data = po.mir.basic_block_data(root);
-
-        if let Some(ref term) = data.terminator {
-            po.visited.insert(root.index());
-
-            let succs = term.successors().into_owned().into_iter();
-
-            po.visit_stack.push((root, succs));
-            po.traverse_successor();
-        }
-
-        po
-    }
-
-    fn traverse_successor(&mut self) {
-        // This is quite a complex loop due to 1. the borrow checker not liking it much
-        // and 2. what exactly is going on is not clear
-        //
-        // It does the actual traversal of the graph, while the `next` method on the iterator
-        // just pops off of the stack. `visit_stack` is a stack containing pairs of nodes and
-        // iterators over the sucessors of those nodes. Each iteration attempts to get the next
-        // node from the top of the stack, then pushes that node and an iterator over the
-        // successors to the top of the stack. This loop only grows `visit_stack`, stopping when
-        // we reach a child that has no children that we haven't already visited.
-        //
-        // For a graph that looks like this:
-        //
-        //         A
-        //        / \
-        //       /   \
-        //      B     C
-        //      |     |
-        //      |     |
-        //      D     |
-        //       \   /
-        //        \ /
-        //         E
-        //
-        // The state of the stack starts out with just the root node (`A` in this case);
-        //     [(A, [B, C])]
-        //
-        // When the first call to `traverse_sucessor` happens, the following happens:
-        //
-        //     [(B, [D]),  // `B` taken from the successors of `A`, pushed to the
-        //                 // top of the stack along with the successors of `B`
-        //      (A, [C])]
-        //
-        //     [(D, [E]),  // `D` taken from successors of `B`, pushed to stack
-        //      (B, []),
-        //      (A, [C])]
-        //
-        //     [(E, []),   // `E` taken from successors of `D`, pushed to stack
-        //      (D, []),
-        //      (B, []),
-        //      (A, [C])]
-        //
-        // Now that the top of the stack has no successors we can traverse, each item will
-        // be popped off during iteration until we get back to `A`. This yeilds [E, D, B].
-        //
-        // When we yield `B` and call `traverse_successor`, we push `C` to the stack, but
-        // since we've already visited `E`, that child isn't added to the stack. The last
-        // two iterations yield `C` and finally `A` for a final traversal of [E, D, B, C, A]
-        loop {
-            let bb = if let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() {
-                if let Some(bb) = iter.next() {
-                    bb
-                } else {
-                    break;
-                }
-            } else {
-                break;
-            };
-
-            if self.visited.insert(bb.index()) {
-                let data = self.mir.basic_block_data(bb);
-
-                if let Some(ref term) = data.terminator {
-                    let succs = term.successors().into_owned().into_iter();
-                    self.visit_stack.push((bb, succs));
-                }
-            }
-        }
-    }
-}
-
-pub fn postorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> Postorder<'a, 'tcx> {
-    Postorder::new(mir, START_BLOCK)
-}
-
-impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
-    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
-
-    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
-        let next = self.visit_stack.pop();
-        if next.is_some() {
-            self.traverse_successor();
-        }
-
-        next.map(|(bb, _)| {
-            let data = self.mir.basic_block_data(bb);
-            (bb, data)
-        })
-    }
-}
-
-/// Reverse postorder traversal of a graph
-///
-/// Reverse postorder is the reverse order of a postorder traversal.
-/// This is different to a preorder traversal and represents a natural
-/// linearisation of control-flow.
-///
-/// ```text
-///
-///         A
-///        / \
-///       /   \
-///      B     C
-///       \   /
-///        \ /
-///         D
-/// ```
-///
-/// A reverse postorder traversal of this graph is either `A B C D` or `A C B D`
-/// Note that for a graph containing no loops (i.e. A DAG), this is equivalent to
-/// a topological sort.
-///
-/// Construction of a `ReversePostorder` traversal requires doing a full
-/// postorder traversal of the graph, therefore this traversal should be
-/// constructed as few times as possible. Use the `reset` method to be able
-/// to re-use the traversal
-#[derive(Clone)]
-pub struct ReversePostorder<'a, 'tcx: 'a> {
-    mir: &'a Mir<'tcx>,
-    blocks: Vec<BasicBlock>,
-    idx: usize
-}
-
-impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
-    pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
-        let blocks : Vec<_> = Postorder::new(mir, root).map(|(bb, _)| bb).collect();
-
-        let len = blocks.len();
-
-        ReversePostorder {
-            mir: mir,
-            blocks: blocks,
-            idx: len
-        }
-    }
-
-    pub fn reset(&mut self) {
-        self.idx = self.blocks.len();
-    }
-}
-
-
-pub fn reverse_postorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> ReversePostorder<'a, 'tcx> {
-    ReversePostorder::new(mir, START_BLOCK)
-}
-
-impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
-    type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
-
-    fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
-        if self.idx == 0 { return None; }
-        self.idx -= 1;
-
-        self.blocks.get(self.idx).map(|&bb| {
-            let data = self.mir.basic_block_data(bb);
-            (bb, data)
-        })
-    }
-}
index 0c85ffd2e9c3967d6657510798733f868930381b..cc710e0ac3563772aa88214d498e9d12488f992d 100644 (file)
@@ -14,3 +14,5 @@ rustc = { path = "../librustc" }
 rustc_const_eval = { path = "../librustc_const_eval" }
 rustc_const_math = { path = "../librustc_const_math" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
+rustc_errors = { path = "../librustc_errors" }
\ No newline at end of file
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
new file mode 100644 (file)
index 0000000..a90b563
--- /dev/null
@@ -0,0 +1,171 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Validate AST before lowering it to HIR
+//
+// This pass is supposed to catch things that fit into AST data structures,
+// but not permitted by the language. It runs after expansion when AST is frozen,
+// so it can check for erroneous constructions produced by syntax extensions.
+// This pass is supposed to perform only simple checks not requiring name resolution
+// or type checking or some other kind of complex analysis.
+
+use rustc::lint;
+use rustc::session::Session;
+use syntax::ast::*;
+use syntax::parse::token::{self, keywords};
+use syntax::visit::{self, Visitor};
+use syntax_pos::Span;
+use errors;
+
+struct AstValidator<'a> {
+    session: &'a Session,
+}
+
+impl<'a> AstValidator<'a> {
+    fn err_handler(&self) -> &errors::Handler {
+        &self.session.parse_sess.span_diagnostic
+    }
+
+    fn check_label(&self, label: Ident, span: Span, id: NodeId) {
+        if label.name == keywords::StaticLifetime.name() {
+            self.err_handler().span_err(span, &format!("invalid label name `{}`", label.name));
+        }
+        if label.name.as_str() == "'_" {
+            self.session.add_lint(
+                lint::builtin::LIFETIME_UNDERSCORE, id, span,
+                format!("invalid label name `{}`", label.name)
+            );
+        }
+    }
+
+    fn invalid_visibility(&self, vis: &Visibility, span: Span, note: Option<&str>) {
+        if vis != &Visibility::Inherited {
+            let mut err = struct_span_err!(self.session, span, E0449,
+                                           "unnecessary visibility qualifier");
+            if let Some(note) = note {
+                err.span_note(span, note);
+            }
+            err.emit();
+        }
+    }
+}
+
+impl<'a> Visitor for AstValidator<'a> {
+    fn visit_lifetime(&mut self, lt: &Lifetime) {
+        if lt.name.as_str() == "'_" {
+            self.session.add_lint(
+                lint::builtin::LIFETIME_UNDERSCORE, lt.id, lt.span,
+                format!("invalid lifetime name `{}`", lt.name)
+            );
+        }
+
+        visit::walk_lifetime(self, lt)
+    }
+
+    fn visit_expr(&mut self, expr: &Expr) {
+        match expr.node {
+            ExprKind::While(_, _, Some(ident)) | ExprKind::Loop(_, Some(ident)) |
+            ExprKind::WhileLet(_, _, _, Some(ident)) | ExprKind::ForLoop(_, _, _, Some(ident)) |
+            ExprKind::Break(Some(ident)) | ExprKind::Continue(Some(ident)) => {
+                self.check_label(ident.node, ident.span, expr.id);
+            }
+            _ => {}
+        }
+
+        visit::walk_expr(self, expr)
+    }
+
+    fn visit_path(&mut self, path: &Path, id: NodeId) {
+        if path.global && path.segments.len() > 0 {
+            let ident = path.segments[0].identifier;
+            if token::Ident(ident).is_path_segment_keyword() {
+                self.session.add_lint(
+                    lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH, id, path.span,
+                    format!("global paths cannot start with `{}`", ident)
+                );
+            }
+        }
+
+        visit::walk_path(self, path)
+    }
+
+    fn visit_item(&mut self, item: &Item) {
+        match item.node {
+            ItemKind::Use(ref view_path) => {
+                let path = view_path.node.path();
+                if !path.segments.iter().all(|segment| segment.parameters.is_empty()) {
+                    self.err_handler().span_err(path.span, "type or lifetime parameters \
+                                                            in import path");
+                }
+            }
+            ItemKind::Impl(_, _, _, Some(..), _, ref impl_items) => {
+                self.invalid_visibility(&item.vis, item.span, None);
+                for impl_item in impl_items {
+                    self.invalid_visibility(&impl_item.vis, impl_item.span, None);
+                }
+            }
+            ItemKind::Impl(_, _, _, None, _, _) => {
+                self.invalid_visibility(&item.vis, item.span, Some("place qualifiers on individual \
+                                                                    impl items instead"));
+            }
+            ItemKind::DefaultImpl(..) => {
+                self.invalid_visibility(&item.vis, item.span, None);
+            }
+            ItemKind::ForeignMod(..) => {
+                self.invalid_visibility(&item.vis, item.span, Some("place qualifiers on individual \
+                                                                    foreign items instead"));
+            }
+            ItemKind::Enum(ref def, _) => {
+                for variant in &def.variants {
+                    for field in variant.node.data.fields() {
+                        self.invalid_visibility(&field.vis, field.span, None);
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        visit::walk_item(self, item)
+    }
+
+    fn visit_variant_data(&mut self, vdata: &VariantData, _: Ident,
+                          _: &Generics, _: NodeId, span: Span) {
+        if vdata.fields().is_empty() {
+            if vdata.is_tuple() {
+                self.err_handler().struct_span_err(span, "empty tuple structs and enum variants \
+                                                          are not allowed, use unit structs and \
+                                                          enum variants instead")
+                                         .span_help(span, "remove trailing `()` to make a unit \
+                                                           struct or unit enum variant")
+                                         .emit();
+            }
+        }
+
+        visit::walk_struct_def(self, vdata)
+    }
+
+    fn visit_vis(&mut self, vis: &Visibility) {
+        match *vis {
+            Visibility::Restricted{ref path, ..} => {
+                if !path.segments.iter().all(|segment| segment.parameters.is_empty()) {
+                    self.err_handler().span_err(path.span, "type or lifetime parameters \
+                                                            in visibility path");
+                }
+            }
+            _ => {}
+        }
+
+        visit::walk_vis(self, vis)
+    }
+}
+
+pub fn check_crate(session: &Session, krate: &Crate) {
+    visit::walk_crate(&mut AstValidator { session: session }, krate)
+}
index b1bb48aacee9ff2892071e9a0c9333d8edef3ed0..27ce03b2d9390c8c250f921282b072a33e3961c0 100644 (file)
@@ -46,7 +46,7 @@ use rustc::lint::builtin::CONST_ERR;
 
 use rustc::hir::{self, PatKind};
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::hir::intravisit::{self, FnKind, Visitor};
 
 use std::collections::hash_map::Entry;
@@ -499,38 +499,36 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
             }
         }
         hir::ExprPath(..) => {
-            let def = v.tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
-            match def {
-                Some(Def::Variant(..)) => {
+            match v.tcx.expect_def(e.id) {
+                Def::Variant(..) => {
                     // Count the discriminator or function pointer.
                     v.add_qualif(ConstQualif::NON_ZERO_SIZED);
                 }
-                Some(Def::Struct(..)) => {
+                Def::Struct(..) => {
                     if let ty::TyFnDef(..) = node_ty.sty {
                         // Count the function pointer.
                         v.add_qualif(ConstQualif::NON_ZERO_SIZED);
                     }
                 }
-                Some(Def::Fn(..)) | Some(Def::Method(..)) => {
+                Def::Fn(..) | Def::Method(..) => {
                     // Count the function pointer.
                     v.add_qualif(ConstQualif::NON_ZERO_SIZED);
                 }
-                Some(Def::Static(..)) => {
+                Def::Static(..) => {
                     match v.mode {
                         Mode::Static | Mode::StaticMut => {}
                         Mode::Const | Mode::ConstFn => {}
                         Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
                     }
                 }
-                Some(Def::Const(did)) |
-                Some(Def::AssociatedConst(did)) => {
+                Def::Const(did) | Def::AssociatedConst(did) => {
                     let substs = Some(v.tcx.node_id_item_substs(e.id).substs);
                     if let Some((expr, _)) = lookup_const_by_id(v.tcx, did, substs) {
                         let inner = v.global_expr(Mode::Const, expr);
                         v.add_qualif(inner);
                     }
                 }
-                Some(Def::Local(..)) if v.mode == Mode::ConstFn => {
+                Def::Local(..) if v.mode == Mode::ConstFn => {
                     // Sadly, we can't determine whether the types are zero-sized.
                     v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
                 }
@@ -550,8 +548,8 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                     _ => break
                 };
             }
-            let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
-            let is_const = match def {
+            // The callee is an arbitrary expression, it doesn't necessarily have a definition.
+            let is_const = match v.tcx.expect_def_or_none(callee.id) {
                 Some(Def::Struct(..)) => true,
                 Some(Def::Variant(..)) => {
                     // Count the discriminator.
@@ -586,8 +584,8 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
             }
         }
         hir::ExprStruct(..) => {
-            let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id());
-            if did == v.tcx.lang_items.unsafe_cell_type() {
+            // unsafe_cell_type doesn't necessarily exist with no_core
+            if Some(v.tcx.expect_def(e.id).def_id()) == v.tcx.lang_items.unsafe_cell_type() {
                 v.add_qualif(ConstQualif::MUTABLE_MEM);
             }
         }
index 77f896e011b938b076ad84c1deb9230ceaa8bc33..918e17d21ea998e35ca91df67c1115bd564d70a9 100644 (file)
@@ -50,11 +50,36 @@ match 5u32 {
 "##,
 
 E0161: r##"
+A value was moved. However, its size was not known at compile time, and only
+values of a known size can be moved.
+
+Erroneous code example:
+
+```compile_fail
+#![feature(box_syntax)]
+
+fn main() {
+    let array: &[isize] = &[1, 2, 3];
+    let _x: Box<[isize]> = box *array;
+    // error: cannot move a value of type [isize]: the size of [isize] cannot
+    //        be statically determined
+}
+```
+
 In Rust, you can only move a value when its size is known at compile time.
 
 To work around this restriction, consider "hiding" the value behind a reference:
 either `&x` or `&mut x`. Since a reference has a fixed size, this lets you move
-it around as usual.
+it around as usual. Example:
+
+```
+#![feature(box_syntax)]
+
+fn main() {
+    let array: &[isize] = &[1, 2, 3];
+    let _x: Box<&[isize]> = box array; // ok!
+}
+```
 "##,
 
 E0265: r##"
@@ -118,6 +143,46 @@ fn some_func() {
 ```
 "##,
 
+E0449: r##"
+A visibility qualifier was used when it was unnecessary. Erroneous code
+examples:
+
+```compile_fail
+struct Bar;
+
+trait Foo {
+    fn foo();
+}
+
+pub impl Bar {} // error: unnecessary visibility qualifier
+
+pub impl Foo for Bar { // error: unnecessary visibility qualifier
+    pub fn foo() {} // error: unnecessary visibility qualifier
+}
+```
+
+To fix this error, please remove the visibility qualifier when it is not
+required. Example:
+
+```ignore
+struct Bar;
+
+trait Foo {
+    fn foo();
+}
+
+// Directly implemented methods share the visibility of the type itself,
+// so `pub` is unnecessary here
+impl Bar {}
+
+// Trait methods share the visibility of the trait, so `pub` is
+// unnecessary in either case
+pub impl Foo for Bar {
+    pub fn foo() {}
+}
+```
+"##,
+
 }
 
 register_diagnostics! {
index 67a9c2fd17e9fcf6ffedecd9fe341698142df280..650613f4844f526facb139225cc39d3a8a46e1e4 100644 (file)
@@ -34,9 +34,12 @@ extern crate rustc_const_math;
 
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
+extern crate syntax_pos;
+extern crate rustc_errors as errors;
 
 pub mod diagnostics;
 
+pub mod ast_validation;
 pub mod consts;
 pub mod loops;
 pub mod no_asm;
index 2174d1cf9b82a0cd7efc478635b203a3247a0d33..dd0f16baaa395d524e6e91bf2abdcb3dbe5e0d65 100644 (file)
@@ -15,7 +15,7 @@ use rustc::dep_graph::DepNode;
 use rustc::hir::map::Map;
 use rustc::hir::intravisit::{self, Visitor};
 use rustc::hir;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 #[derive(Clone, Copy, PartialEq)]
 enum Context {
index 90f92c25b05ea54d80d768782537336e236fc08a..314513a974ecdb826b728523403a5056c635a062 100644 (file)
@@ -29,7 +29,7 @@ struct CheckNoAsm<'a> {
     sess: &'a Session,
 }
 
-impl<'a, 'v> Visitor<'v> for CheckNoAsm<'a> {
+impl<'a> Visitor for CheckNoAsm<'a> {
     fn visit_expr(&mut self, e: &ast::Expr) {
         match e.node {
             ast::ExprKind::InlineAsm(_) => span_err!(self.sess, e.span, E0472,
index 137a50642fcf4be2f8ff781355e5cad43add1738..4684683f02501afe32eeb60b76cd2afbe0891827 100644 (file)
@@ -20,7 +20,7 @@ use rustc::traits::ProjectionMode;
 use rustc::hir;
 use rustc::hir::intravisit;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
     let mut rvcx = RvalueContext { tcx: tcx };
index 245960a04f030972917105cdded1ea60fe619df3..d0938ad09a0da1bced4e17ef57d5c1c1096c97fe 100644 (file)
@@ -18,8 +18,8 @@ use rustc::hir::def::{Def, DefMap};
 use rustc::util::nodemap::NodeMap;
 
 use syntax::{ast};
-use syntax::codemap::Span;
 use syntax::feature_gate::{GateIssue, emit_feature_err};
+use syntax_pos::Span;
 use rustc::hir::intravisit::{self, Visitor};
 use rustc::hir;
 
index e9a32e53a9fe6372ad31fad1fbcabe8b0616cf4a..514d81ecc94f2a9f6b27ca8f5c39520b69f9c1b7 100644 (file)
@@ -14,5 +14,6 @@ rustc = { path = "../librustc" }
 rustc_back = { path = "../librustc_back" }
 rustc_bitflags = { path = "../librustc_bitflags" }
 rustc_metadata = { path = "../librustc_metadata" }
-rustc_mir = { path = "../librustc_mir" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
+rustc_errors = { path = "../librustc_errors" }
index 4c7755b1b0937bf8e62795104a6e9ba0a7021282..ff3038c3d1175326659d82763a0361e867105436 100644 (file)
@@ -12,8 +12,8 @@
 
 use syntax::ast;
 use syntax::attr;
-use syntax::codemap::Span;
-use syntax::errors;
+use errors;
+use syntax_pos::Span;
 use rustc::dep_graph::DepNode;
 use rustc::hir::map::Map;
 use rustc::hir::intravisit::Visitor;
index 5fa29771c57fbd1b423a197f81216833e6582c3d..e60a657ba193d9829e9844b586c825c3c138629f 100644 (file)
@@ -70,7 +70,8 @@
 extern crate rustc;
 extern crate rustc_back;
 extern crate rustc_metadata;
-extern crate rustc_mir;
+extern crate syntax_pos;
+extern crate rustc_errors as errors;
 
 pub use self::registry::Registry;
 
index 036e46c380398650385e9565e8ab9a719c1336a4..a3cd9b5da02bc3b2df506cc166614784826529d5 100644 (file)
@@ -20,9 +20,9 @@ use std::env;
 use std::mem;
 use std::path::PathBuf;
 use syntax::ast;
-use syntax::codemap::{Span, COMMAND_LINE_SP};
 use syntax::ptr::P;
 use syntax::attr::AttrMetaMethods;
+use syntax_pos::{Span, COMMAND_LINE_SP};
 
 /// Pointer to a registrar function.
 pub type PluginRegistrarFun =
@@ -101,7 +101,8 @@ impl<'a> PluginLoader<'a> {
     fn load_plugin(&mut self, span: Span, name: &str, args: Vec<P<ast::MetaItem>>) {
         let registrar = self.reader.find_plugin_registrar(span, name);
 
-        if let Some((lib, symbol)) = registrar {
+        if let Some((lib, svh, index)) = registrar {
+            let symbol = self.sess.generate_plugin_registrar_symbol(&svh, index);
             let fun = self.dylink_registrar(span, lib, symbol);
             self.plugins.push(PluginRegistrar {
                 fun: fun,
index dc5a38bb7647ee6d2974c4e9f7a611a2b093be0b..54fa0197de4fe0fcf2ee7013308ce69a3cb45701 100644 (file)
@@ -18,11 +18,11 @@ use rustc::mir::transform::MirMapPass;
 use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT};
 use syntax::ext::base::{IdentTT, MultiModifier, MultiDecorator};
 use syntax::ext::base::{MacroExpanderFn, MacroRulesTT};
-use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::ptr::P;
 use syntax::ast;
 use syntax::feature_gate::AttributeType;
+use syntax_pos::Span;
 
 use std::collections::HashMap;
 use std::borrow::ToOwned;
index 0553e54e3aa9b145626ec09d6bcf6df224691257..439fa661e0ab52dfc117c8dc48c749d43b4295b3 100644 (file)
@@ -9,6 +9,6 @@ path = "lib.rs"
 crate-type = ["dylib"]
 
 [dependencies]
-log = { path = "../liblog" }
 rustc = { path = "../librustc" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
index 1b49409970ded6549c6abcf45bfa64b26deea627..66afe5835bf6fc6f3549dfc788e7feaab720f948 100644 (file)
@@ -16,7 +16,9 @@ E0445: r##"
 A private trait was used on a public type parameter bound. Erroneous code
 examples:
 
-```compile_fail
+```compile_fail,E0445
+#![deny(private_in_public)]
+
 trait Foo {
     fn dummy(&self) { }
 }
@@ -44,7 +46,9 @@ pub fn foo<T: Foo> (t: T) {} // ok!
 E0446: r##"
 A private type was used in a public type signature. Erroneous code example:
 
-```compile_fail
+```compile_fail,E0446
+#![deny(private_in_public)]
+
 mod Foo {
     struct Bar(u32);
 
@@ -73,7 +77,7 @@ mod Foo {
 E0447: r##"
 The `pub` keyword was used inside a function. Erroneous code example:
 
-```compile_fail
+```ignore
 fn foo() {
     pub struct Bar; // error: visibility has no effect inside functions
 }
@@ -96,7 +100,7 @@ pub enum Foo {
 Since the enum is already public, adding `pub` on one its elements is
 unnecessary. Example:
 
-```compile_fail
+```compile_fail,
 enum Foo {
     pub Bar, // not ok!
 }
@@ -111,51 +115,11 @@ pub enum Foo {
 ```
 "##,
 
-E0449: r##"
-A visibility qualifier was used when it was unnecessary. Erroneous code
-examples:
-
-```compile_fail
-struct Bar;
-
-trait Foo {
-    fn foo();
-}
-
-pub impl Bar {} // error: unnecessary visibility qualifier
-
-pub impl Foo for Bar { // error: unnecessary visibility qualifier
-    pub fn foo() {} // error: unnecessary visibility qualifier
-}
-```
-
-To fix this error, please remove the visibility qualifier when it is not
-required. Example:
-
-```ignore
-struct Bar;
-
-trait Foo {
-    fn foo();
-}
-
-// Directly implemented methods share the visibility of the type itself,
-// so `pub` is unnecessary here
-impl Bar {}
-
-// Trait methods share the visibility of the trait, so `pub` is
-// unnecessary in either case
-pub impl Foo for Bar {
-    pub fn foo() {}
-}
-```
-"##,
-
 E0450: r##"
 A tuple constructor was invoked while some of its fields are private. Erroneous
 code example:
 
-```compile_fail
+```compile_fail,E0450
 mod Bar {
     pub struct Foo(isize);
 }
@@ -193,7 +157,7 @@ let f = bar::Foo::new(1);
 E0451: r##"
 A struct constructor with private fields was invoked. Erroneous code example:
 
-```compile_fail
+```compile_fail,E0451
 mod Bar {
     pub struct Foo {
         pub a: isize,
index f1e744098b960bd0d1068a040620ce321cb8ad1c..85a6f732dd52e71716efd93395dbcd210f028a99 100644 (file)
 #![feature(rustc_private)]
 #![feature(staged_api)]
 
-#[macro_use] extern crate log;
+extern crate rustc;
 #[macro_use] extern crate syntax;
-
-#[macro_use] extern crate rustc;
-
-use std::cmp;
-use std::mem::replace;
-
-use rustc::hir::{self, PatKind};
-use rustc::hir::intravisit::{self, Visitor};
+extern crate syntax_pos;
 
 use rustc::dep_graph::DepNode;
-use rustc::lint;
+use rustc::hir::{self, PatKind};
 use rustc::hir::def::{self, Def};
 use rustc::hir::def_id::DefId;
+use rustc::hir::intravisit::{self, Visitor};
+use rustc::hir::pat_util::EnumerateAndAdjustIterator;
+use rustc::lint;
 use rustc::middle::privacy::{AccessLevel, AccessLevels};
 use rustc::ty::{self, TyCtxt};
 use rustc::util::nodemap::NodeSet;
-use rustc::hir::map as ast_map;
-
 use syntax::ast;
-use syntax::codemap::Span;
-
-pub mod diagnostics;
+use syntax_pos::Span;
 
-type Context<'a, 'tcx> = (&'a ty::MethodMap<'tcx>, &'a def::ExportMap);
+use std::cmp;
+use std::mem::replace;
 
-/// Result of a checking operation - None => no errors were found. Some => an
-/// error and contains the span and message for reporting that error and
-/// optionally the same for a note about the error.
-type CheckResult = Option<(Span, String, Option<(Span, String)>)>;
+pub mod diagnostics;
 
 ////////////////////////////////////////////////////////////////////////////////
 /// The embargo visitor, used to determine the exports of the ast
@@ -76,7 +66,7 @@ struct ReachEverythingInTheInterfaceVisitor<'b, 'a: 'b, 'tcx: 'a> {
 impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
     fn ty_level(&self, ty: &hir::Ty) -> Option<AccessLevel> {
         if let hir::TyPath(..) = ty.node {
-            match self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def() {
+            match self.tcx.expect_def(ty.id) {
                 Def::PrimTy(..) | Def::SelfTy(..) | Def::TyParam(..) => {
                     Some(AccessLevel::Public)
                 }
@@ -94,7 +84,7 @@ impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
     }
 
     fn trait_level(&self, trait_ref: &hir::TraitRef) -> Option<AccessLevel> {
-        let did = self.tcx.trait_ref_to_def_id(trait_ref);
+        let did = self.tcx.expect_def(trait_ref.ref_id).def_id();
         if let Some(node_id) = self.tcx.map.as_local_node_id(did) {
             self.get(node_id)
         } else {
@@ -328,7 +318,7 @@ impl<'b, 'a, 'tcx: 'a> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
 impl<'b, 'a, 'tcx: 'a, 'v> Visitor<'v> for ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
     fn visit_ty(&mut self, ty: &hir::Ty) {
         if let hir::TyPath(_, ref path) = ty.node {
-            let def = self.ev.tcx.def_map.borrow().get(&ty.id).unwrap().full_def();
+            let def = self.ev.tcx.expect_def(ty.id);
             match def {
                 Def::Struct(def_id) | Def::Enum(def_id) | Def::TyAlias(def_id) |
                 Def::Trait(def_id) | Def::AssociatedTy(def_id, _) => {
@@ -354,7 +344,7 @@ impl<'b, 'a, 'tcx: 'a, 'v> Visitor<'v> for ReachEverythingInTheInterfaceVisitor<
     }
 
     fn visit_trait_ref(&mut self, trait_ref: &hir::TraitRef) {
-        let def_id = self.ev.tcx.trait_ref_to_def_id(trait_ref);
+        let def_id = self.ev.tcx.expect_def(trait_ref.ref_id).def_id();
         if let Some(node_id) = self.ev.tcx.map.as_local_node_id(def_id) {
             let item = self.ev.tcx.map.expect_item(node_id);
             self.ev.update(item.id, Some(AccessLevel::Reachable));
@@ -433,12 +423,11 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
             hir::ExprMethodCall(..) => {
                 let method_call = ty::MethodCall::expr(expr.id);
                 let method = self.tcx.tables.borrow().method_map[&method_call];
-                debug!("(privacy checking) checking impl method");
                 self.check_method(expr.span, method.def_id);
             }
             hir::ExprStruct(..) => {
                 let adt = self.tcx.expr_ty(expr).ty_adt_def().unwrap();
-                let variant = adt.variant_of_def(self.tcx.resolve_expr(expr));
+                let variant = adt.variant_of_def(self.tcx.expect_def(expr.id));
                 // RFC 736: ensure all unmentioned fields are visible.
                 // Rather than computing the set of unmentioned fields
                 // (i.e. `all_fields - fields`), just check them all.
@@ -448,7 +437,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
             }
             hir::ExprPath(..) => {
 
-                if let Def::Struct(..) = self.tcx.resolve_expr(expr) {
+                if let Def::Struct(..) = self.tcx.expect_def(expr.id) {
                     let expr_ty = self.tcx.expr_ty(expr);
                     let def = match expr_ty.sty {
                         ty::TyFnDef(_, _, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
@@ -482,19 +471,16 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
         match pattern.node {
             PatKind::Struct(_, ref fields, _) => {
                 let adt = self.tcx.pat_ty(pattern).ty_adt_def().unwrap();
-                let def = self.tcx.def_map.borrow().get(&pattern.id).unwrap().full_def();
-                let variant = adt.variant_of_def(def);
+                let variant = adt.variant_of_def(self.tcx.expect_def(pattern.id));
                 for field in fields {
                     self.check_field(pattern.span, adt, variant.field_named(field.node.name));
                 }
             }
-
-            // Patterns which bind no fields are allowable (the path is check
-            // elsewhere).
-            PatKind::TupleStruct(_, Some(ref fields)) => {
+            PatKind::TupleStruct(_, ref fields, ddpos) => {
                 match self.tcx.pat_ty(pattern).sty {
                     ty::TyStruct(def, _) => {
-                        for (i, field) in fields.iter().enumerate() {
+                        let expected_len = def.struct_variant().fields.len();
+                        for (i, field) in fields.iter().enumerate_and_adjust(expected_len, ddpos) {
                             if let PatKind::Wild = field.node {
                                 continue
                             }
@@ -506,7 +492,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
                     }
                     _ => {}
                 }
-
             }
             _ => {}
         }
@@ -521,74 +506,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
     }
 }
 
-////////////////////////////////////////////////////////////////////////////////
-/// The privacy sanity check visitor, ensures unnecessary visibility isn't here
-////////////////////////////////////////////////////////////////////////////////
-
-struct SanePrivacyVisitor<'a, 'tcx: 'a> {
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-}
-
-impl<'a, 'tcx, 'v> Visitor<'v> for SanePrivacyVisitor<'a, 'tcx> {
-    fn visit_item(&mut self, item: &hir::Item) {
-        self.check_sane_privacy(item);
-        intravisit::walk_item(self, item);
-    }
-}
-
-impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> {
-    /// Validate that items that shouldn't have visibility qualifiers don't have them.
-    /// Such qualifiers can be set by syntax extensions even if the parser doesn't allow them,
-    /// so we check things like variant fields too.
-    fn check_sane_privacy(&self, item: &hir::Item) {
-        let check_inherited = |sp, vis: &hir::Visibility, note: &str| {
-            if *vis != hir::Inherited {
-                let mut err = struct_span_err!(self.tcx.sess, sp, E0449,
-                                               "unnecessary visibility qualifier");
-                if !note.is_empty() {
-                    err.span_note(sp, note);
-                }
-                err.emit();
-            }
-        };
-
-        match item.node {
-            hir::ItemImpl(_, _, _, Some(..), _, ref impl_items) => {
-                check_inherited(item.span, &item.vis,
-                                "visibility qualifiers have no effect on trait impls");
-                for impl_item in impl_items {
-                    check_inherited(impl_item.span, &impl_item.vis,
-                                    "visibility qualifiers have no effect on trait impl items");
-                }
-            }
-            hir::ItemImpl(_, _, _, None, _, _) => {
-                check_inherited(item.span, &item.vis,
-                                "place qualifiers on individual methods instead");
-            }
-            hir::ItemDefaultImpl(..) => {
-                check_inherited(item.span, &item.vis,
-                                "visibility qualifiers have no effect on trait impls");
-            }
-            hir::ItemForeignMod(..) => {
-                check_inherited(item.span, &item.vis,
-                                "place qualifiers on individual functions instead");
-            }
-            hir::ItemEnum(ref def, _) => {
-                for variant in &def.variants {
-                    for field in variant.node.data.fields() {
-                        check_inherited(field.span, &field.vis,
-                                        "visibility qualifiers have no effect on variant fields");
-                    }
-                }
-            }
-            hir::ItemStruct(..) | hir::ItemTrait(..) |
-            hir::ItemConst(..) | hir::ItemStatic(..) | hir::ItemFn(..) |
-            hir::ItemMod(..) | hir::ItemExternCrate(..) |
-            hir::ItemUse(..) | hir::ItemTy(..) => {}
-        }
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 /// Obsolete visitors for checking for private items in public interfaces.
 /// These visitors are supposed to be kept in frozen state and produce an
@@ -617,10 +534,9 @@ struct ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> {
 
 impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
     fn path_is_private_type(&self, path_id: ast::NodeId) -> bool {
-        let did = match self.tcx.def_map.borrow().get(&path_id).map(|d| d.full_def()) {
-            // `int` etc. (None doesn't seem to occur.)
-            None | Some(Def::PrimTy(..)) | Some(Def::SelfTy(..)) => return false,
-            Some(def) => def.def_id(),
+        let did = match self.tcx.expect_def(path_id) {
+            Def::PrimTy(..) | Def::SelfTy(..) => return false,
+            def => def.def_id(),
         };
 
         // A path can only be private if:
@@ -629,7 +545,7 @@ impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
             // .. and it corresponds to a private type in the AST (this returns
             // None for type parameters)
             match self.tcx.map.find(node_id) {
-                Some(ast_map::NodeItem(ref item)) => item.vis != hir::Public,
+                Some(hir::map::NodeItem(ref item)) => item.vis != hir::Public,
                 Some(_) | None => false,
             }
         } else {
@@ -736,7 +652,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx>
                 let not_private_trait =
                     trait_ref.as_ref().map_or(true, // no trait counts as public trait
                                               |tr| {
-                        let did = self.tcx.trait_ref_to_def_id(tr);
+                        let did = self.tcx.expect_def(tr.ref_id).def_id();
 
                         if let Some(node_id) = self.tcx.map.as_local_node_id(did) {
                             self.trait_is_public(node_id)
@@ -863,7 +779,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx>
         // any `visit_ty`'s will be called on things that are in
         // public signatures, i.e. things that we're interested in for
         // this visitor.
-        debug!("VisiblePrivateTypesVisitor entering item {:?}", item);
         intravisit::walk_item(self, item);
     }
 
@@ -895,7 +810,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx>
     }
 
     fn visit_ty(&mut self, t: &hir::Ty) {
-        debug!("VisiblePrivateTypesVisitor checking ty {:?}", t);
         if let hir::TyPath(..) = t.node {
             if self.path_is_private_type(t.id) {
                 self.old_error_set.insert(t.id);
@@ -996,8 +910,7 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
 impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
     fn visit_ty(&mut self, ty: &hir::Ty) {
         if let hir::TyPath(_, ref path) = ty.node {
-            let def = self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def();
-            match def {
+            match self.tcx.expect_def(ty.id) {
                 Def::PrimTy(..) | Def::SelfTy(..) | Def::TyParam(..) => {
                     // Public
                 }
@@ -1047,7 +960,7 @@ impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a,
 
     fn visit_trait_ref(&mut self, trait_ref: &hir::TraitRef) {
         // Non-local means public (private items can't leave their crate, modulo bugs)
-        let def_id = self.tcx.trait_ref_to_def_id(trait_ref);
+        let def_id = self.tcx.expect_def(trait_ref.ref_id).def_id();
         if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) {
             let item = self.tcx.map.expect_item(node_id);
             let vis = ty::Visibility::from_hir(&item.vis, node_id, self.tcx);
@@ -1180,10 +1093,6 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     let krate = tcx.map.krate();
 
-    // Sanity check to make sure that all privacy usage is reasonable.
-    let mut visitor = SanePrivacyVisitor { tcx: tcx };
-    krate.visit_all_items(&mut visitor);
-
     // Use the parent map to check the privacy of everything
     let mut visitor = PrivacyVisitor {
         curitem: ast::DUMMY_NODE_ID,
index a63460d912d7d3033821ff16363b6e69b1631a75..5ce4c74e735fd6fb11015095554c5777730d2878 100644 (file)
@@ -14,3 +14,5 @@ log = { path = "../liblog" }
 syntax = { path = "../libsyntax" }
 rustc = { path = "../librustc" }
 arena = { path = "../libarena" }
+rustc_errors = { path = "../librustc_errors" }
+syntax_pos = { path = "../libsyntax_pos" }
index f56b22f924891bf0dca827a52e473e273b91b82e..5867e48c7ca27b64d5fbea039fffde6a34a6ec8f 100644 (file)
@@ -22,23 +22,23 @@ use Resolver;
 use {resolve_error, resolve_struct_error, ResolutionError};
 
 use rustc::middle::cstore::{ChildItem, DlDef};
-use rustc::lint;
 use rustc::hir::def::*;
 use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
 use rustc::ty::{self, VariantKind};
 
-use syntax::ast::{Name, NodeId};
-use syntax::attr::AttrMetaMethods;
-use syntax::parse::token::{self, keywords};
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax::ast::Name;
+use syntax::attr;
+use syntax::parse::token;
 
-use syntax::ast::{Block, Crate, DeclKind};
+use syntax::ast::{Block, Crate};
 use syntax::ast::{ForeignItem, ForeignItemKind, Item, ItemKind};
 use syntax::ast::{Mutability, PathListItemKind};
-use syntax::ast::{SelfKind, Stmt, StmtKind, TraitItemKind};
-use syntax::ast::{Variant, ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
+use syntax::ast::{StmtKind, TraitItemKind};
+use syntax::ast::{Variant, ViewPathGlob, ViewPathList, ViewPathSimple};
 use syntax::visit::{self, Visitor};
 
+use syntax_pos::{Span, DUMMY_SP};
+
 trait ToNameBinding<'a> {
     fn to_name_binding(self) -> NameBinding<'a>;
 }
@@ -58,6 +58,9 @@ impl<'a> ToNameBinding<'a> for (Def, Span, ty::Visibility) {
 impl<'b> Resolver<'b> {
     /// Constructs the reduced graph for the entire crate.
     pub fn build_reduced_graph(&mut self, krate: &Crate) {
+        let no_implicit_prelude = attr::contains_name(&krate.attrs, "no_implicit_prelude");
+        self.graph_root.no_implicit_prelude.set(no_implicit_prelude);
+
         let mut visitor = BuildReducedGraphVisitor {
             parent: self.graph_root,
             resolver: self,
@@ -82,47 +85,11 @@ impl<'b> Resolver<'b> {
     }
 
     fn block_needs_anonymous_module(&mut self, block: &Block) -> bool {
-        fn is_item(statement: &Stmt) -> bool {
-            if let StmtKind::Decl(ref declaration, _) = statement.node {
-                if let DeclKind::Item(_) = declaration.node {
-                    return true;
-                }
-            }
-            false
-        }
-
         // If any statements are items, we need to create an anonymous module
-        block.stmts.iter().any(is_item)
-    }
-
-    fn sanity_check_import(&self, view_path: &ViewPath, id: NodeId) {
-        let path = match view_path.node {
-            ViewPathSimple(_, ref path) |
-            ViewPathGlob (ref path) |
-            ViewPathList(ref path, _) => path
-        };
-
-        // Check for type parameters
-        let found_param = path.segments.iter().any(|segment| {
-            !segment.parameters.types().is_empty() ||
-            !segment.parameters.lifetimes().is_empty() ||
-            !segment.parameters.bindings().is_empty()
-        });
-        if found_param {
-            self.session.span_err(path.span, "type or lifetime parameters in import path");
-        }
-
-        // Checking for special identifiers in path
-        // prevent `self` or `super` at beginning of global path
-        if path.global && path.segments.len() > 0 {
-            let first = path.segments[0].identifier.name;
-            if first == keywords::Super.name() || first == keywords::SelfValue.name() {
-                self.session.add_lint(
-                    lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH, id, path.span,
-                    format!("expected identifier, found keyword `{}`", first)
-                );
-            }
-        }
+        block.stmts.iter().any(|statement| match statement.node {
+            StmtKind::Item(_) => true,
+            _ => false,
+        })
     }
 
     /// Constructs the reduced graph for one item.
@@ -158,10 +125,8 @@ impl<'b> Resolver<'b> {
                     }
                 };
 
-                self.sanity_check_import(view_path, item.id);
-
                 // Build up the import directives.
-                let is_prelude = item.attrs.iter().any(|attr| attr.name() == "prelude_import");
+                let is_prelude = attr::contains_name(&item.attrs, "prelude_import");
 
                 match view_path.node {
                     ViewPathSimple(binding, ref full_path) => {
@@ -254,6 +219,10 @@ impl<'b> Resolver<'b> {
                 let parent_link = ModuleParentLink(parent, name);
                 let def = Def::Mod(self.definitions.local_def_id(item.id));
                 let module = self.new_module(parent_link, Some(def), false);
+                module.no_implicit_prelude.set({
+                    parent.no_implicit_prelude.get() ||
+                        attr::contains_name(&item.attrs, "no_implicit_prelude")
+                });
                 self.define(parent, name, TypeNS, (module, sp, vis));
                 self.module_map.insert(item.id, module);
                 *parent_ref = module;
@@ -335,10 +304,11 @@ impl<'b> Resolver<'b> {
                     let (def, ns) = match item.node {
                         TraitItemKind::Const(..) => (Def::AssociatedConst(item_def_id), ValueNS),
                         TraitItemKind::Method(ref sig, _) => {
-                            is_static_method = sig.explicit_self.node == SelfKind::Static;
+                            is_static_method = !sig.decl.has_self();
                             (Def::Method(item_def_id), ValueNS)
                         }
                         TraitItemKind::Type(..) => (Def::AssociatedTy(def_id, item_def_id), TypeNS),
+                        TraitItemKind::Macro(_) => panic!("unexpanded macro in resolve!"),
                     };
 
                     self.define(module_parent, item.ident.name, ns, (def, item.span, vis));
@@ -529,7 +499,7 @@ struct BuildReducedGraphVisitor<'a, 'b: 'a> {
     parent: Module<'b>,
 }
 
-impl<'a, 'b, 'v> Visitor<'v> for BuildReducedGraphVisitor<'a, 'b> {
+impl<'a, 'b> Visitor for BuildReducedGraphVisitor<'a, 'b> {
     fn visit_item(&mut self, item: &Item) {
         let old_parent = self.parent;
         self.resolver.build_reduced_graph_for_item(item, &mut self.parent);
index 64347d7b84d3c4fb5ead87a6a58c6d4fbf84fc92..3084d9abbe1e4f8cab3a93f271145c4ad03a8f9e 100644 (file)
@@ -27,7 +27,7 @@ use Namespace::{TypeNS, ValueNS};
 use rustc::lint;
 use syntax::ast::{self, ViewPathGlob, ViewPathList, ViewPathSimple};
 use syntax::visit::{self, Visitor};
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
 
 
 struct UnusedImportCheckVisitor<'a, 'b: 'a> {
@@ -71,7 +71,7 @@ impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> {
     }
 }
 
-impl<'a, 'b, 'v> Visitor<'v> for UnusedImportCheckVisitor<'a, 'b> {
+impl<'a, 'b> Visitor for UnusedImportCheckVisitor<'a, 'b> {
     fn visit_item(&mut self, item: &ast::Item) {
         visit::walk_item(self, item);
         // Ignore is_public import statements because there's no way to be sure
index 351486220528952eae1c86f36257182369671e96..3e860150a35fdfbe3f2379e81cc727f3783f4778 100644 (file)
 register_long_diagnostics! {
 
 E0154: r##"
+## Note: this error code is no longer emitted by the compiler.
+
 Imports (`use` statements) are not allowed after non-item statements, such as
 variable declarations and expression statements.
 
 Here is an example that demonstrates the error:
 
-```compile_fail
+```ignore
 fn f() {
     // Variable declaration before import
     let x = 0;
@@ -50,6 +52,8 @@ https://doc.rust-lang.org/reference.html#statements
 "##,
 
 E0251: r##"
+## Note: this error code is no longer emitted by the compiler.
+
 Two items of the same name cannot be imported without rebinding one of the
 items under a new local name.
 
@@ -75,9 +79,9 @@ E0252: r##"
 Two items of the same name cannot be imported without rebinding one of the
 items under a new local name.
 
-An example of this error:
+Erroneous code example:
 
-```compile_fail
+```compile_fail,E0252
 use foo::baz;
 use bar::baz; // error, do `use bar::baz as quux` instead
 
@@ -87,6 +91,41 @@ mod foo {
     pub struct baz;
 }
 
+mod bar {
+    pub mod baz {}
+}
+```
+
+You can use aliases in order to fix this error. Example:
+
+```
+use foo::baz as foo_baz;
+use bar::baz; // ok!
+
+fn main() {}
+
+mod foo {
+    pub struct baz;
+}
+
+mod bar {
+    pub mod baz {}
+}
+```
+
+Or you can reference the item with its parent:
+
+```
+use bar::baz;
+
+fn main() {
+    let x = foo::baz; // ok!
+}
+
+mod foo {
+    pub struct baz;
+}
+
 mod bar {
     pub mod baz {}
 }
@@ -95,9 +134,11 @@ mod bar {
 
 E0253: r##"
 Attempt was made to import an unimportable value. This can happen when trying
-to import a method from a trait. An example of this error:
+to import a method from a trait.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0253
 mod foo {
     pub trait MyTrait {
         fn do_something();
@@ -105,6 +146,8 @@ mod foo {
 }
 
 use foo::MyTrait::do_something;
+
+fn main() {}
 ```
 
 It's invalid to directly import methods belonging to a trait or concrete type.
@@ -114,10 +157,10 @@ E0255: r##"
 You can't import a value whose name is the same as another value defined in the
 module.
 
-An example of this error:
+Erroneous code example:
 
-```compile_fail
-use bar::foo; // error, do `use bar::foo as baz` instead
+```compile_fail,E0255
+use bar::foo; // error: an item named `foo` is already in scope
 
 fn foo() {}
 
@@ -127,9 +170,39 @@ mod bar {
 
 fn main() {}
 ```
+
+You can use aliases in order to fix this error. Example:
+
+```
+use bar::foo as bar_foo; // ok!
+
+fn foo() {}
+
+mod bar {
+     pub fn foo() {}
+}
+
+fn main() {}
+```
+
+Or you can reference the item with its parent:
+
+```
+fn foo() {}
+
+mod bar {
+     pub fn foo() {}
+}
+
+fn main() {
+    bar::foo(); // we get the item by referring to its parent
+}
+```
 "##,
 
 E0256: r##"
+## Note: this error code is no longer emitted by the compiler.
+
 You can't import a type or module when the name of the item being imported is
 the same as another type or submodule defined in the module.
 
@@ -154,9 +227,11 @@ that has been imported into the current module.
 
 Erroneous code example:
 
-```compile_fail
-extern crate a;
-extern crate crate_a as a;
+```compile_fail,E0259
+extern crate std;
+extern crate libc as std;
+
+fn main() {}
 ```
 
 The solution is to choose a different name that doesn't conflict with any
@@ -165,17 +240,17 @@ external crate imported into the current module.
 Correct example:
 
 ```ignore
-extern crate a;
-extern crate crate_a as other_name;
+extern crate std;
+extern crate libc as other_name;
 ```
 "##,
 
 E0260: r##"
 The name for an item declaration conflicts with an external crate's name.
 
-For instance:
+Erroneous code example:
 
-```ignore
+```ignore,E0260
 extern crate abc;
 
 struct abc;
@@ -206,10 +281,10 @@ https://doc.rust-lang.org/reference.html#statements
 "##,
 
 E0364: r##"
-Private items cannot be publicly re-exported.  This error indicates that you
+Private items cannot be publicly re-exported. This error indicates that you
 attempted to `pub use` a type or value that was not itself public.
 
-Here is an example that demonstrates the error:
+Erroneous code example:
 
 ```compile_fail
 mod foo {
@@ -217,17 +292,21 @@ mod foo {
 }
 
 pub use foo::X;
+
+fn main() {}
 ```
 
 The solution to this problem is to ensure that the items that you are
 re-exporting are themselves marked with `pub`:
 
-```ignore
+```
 mod foo {
     pub const X: u32 = 1;
 }
 
 pub use foo::X;
+
+fn main() {}
 ```
 
 See the 'Use Declarations' section of the reference for more information on
@@ -240,25 +319,29 @@ E0365: r##"
 Private modules cannot be publicly re-exported. This error indicates that you
 attempted to `pub use` a module that was not itself public.
 
-Here is an example that demonstrates the error:
+Erroneous code example:
 
-```compile_fail
+```compile_fail,E0365
 mod foo {
     pub const X: u32 = 1;
 }
 
 pub use foo as foo2;
+
+fn main() {}
 ```
 
 The solution to this problem is to ensure that the module that you are
 re-exporting is itself marked with `pub`:
 
-```ignore
+```
 pub mod foo {
     pub const X: u32 = 1;
 }
 
 pub use foo as foo2;
+
+fn main() {}
 ```
 
 See the 'Use Declarations' section of the reference for more information
@@ -269,9 +352,11 @@ https://doc.rust-lang.org/reference.html#use-declarations
 
 E0401: r##"
 Inner items do not inherit type parameters from the functions they are embedded
-in. For example, this will not compile:
+in.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0401
 fn foo<T>(x: T) {
     fn bar(y: T) { // T is defined in the "outer" function
         // ..
@@ -282,7 +367,7 @@ fn foo<T>(x: T) {
 
 Nor will this:
 
-```compile_fail
+```compile_fail,E0401
 fn foo<T>(x: T) {
     type MaybeT = Option<T>;
     // ...
@@ -291,7 +376,7 @@ fn foo<T>(x: T) {
 
 Or this:
 
-```compile_fail
+```compile_fail,E0401
 fn foo<T>(x: T) {
     struct Foo {
         x: T,
@@ -374,9 +459,11 @@ closures or copying the parameters should still work.
 "##,
 
 E0403: r##"
-Some type parameters have the same name. Example of erroneous code:
+Some type parameters have the same name.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0403
 fn foo<T, T>(s: T, u: T) {} // error: the name `T` is already used for a type
                             //        parameter in this type parameter list
 ```
@@ -390,10 +477,11 @@ fn foo<T, Y>(s: T, u: Y) {} // ok!
 "##,
 
 E0404: r##"
-You tried to implement something which was not a trait on an object. Example of
-erroneous code:
+You tried to implement something which was not a trait on an object.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0404
 struct Foo;
 struct Bar;
 
@@ -416,9 +504,11 @@ impl Foo for Bar { // ok!
 "##,
 
 E0405: r##"
-The code refers to a trait that is not in scope. Example of erroneous code:
+The code refers to a trait that is not in scope.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0405
 struct Foo;
 
 impl SomeTrait for Foo {} // error: trait `SomeTrait` is not in scope
@@ -446,9 +536,11 @@ impl SomeTrait for Foo { // ok!
 
 E0407: r##"
 A definition of a method not in the implemented trait was given in a trait
-implementation. Example of erroneous code:
+implementation.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0407
 trait Foo {
     fn a();
 }
@@ -501,9 +593,9 @@ E0408: r##"
 An "or" pattern was used where the variable bindings are not consistently bound
 across patterns.
 
-Example of erroneous code:
+Erroneous code example:
 
-```compile_fail
+```compile_fail,E0408
 match x {
     Some(y) | None => { /* use y */ } // error: variable `y` from pattern #1 is
                                       //        not bound in pattern #2
@@ -545,9 +637,9 @@ E0409: r##"
 An "or" pattern was used where the variable bindings are not consistently bound
 across patterns.
 
-Example of erroneous code:
+Erroneous code example:
 
-```compile_fail
+```compile_fail,E0409
 let x = (0, 2);
 match x {
     (0, ref y) | (y, 0) => { /* use y */} // error: variable `y` is bound with
@@ -583,9 +675,11 @@ match x {
 "##,
 
 E0411: r##"
-The `Self` keyword was used outside an impl or a trait. Erroneous code example:
+The `Self` keyword was used outside an impl or a trait.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0411
 <Self>::foo; // error: use of `Self` outside of an impl or trait
 ```
 
@@ -639,9 +733,11 @@ trait Baz : Foo + Foo2 {
 "##,
 
 E0412: r##"
-The type name used is not in scope. Example of erroneous codes:
+The type name used is not in scope.
 
-```compile_fail
+Erroneous code examples:
+
+```compile_fail,E0412
 impl Something {} // error: type name `Something` is not in scope
 
 // or:
@@ -677,104 +773,12 @@ fn foo<T>(x: T) {} // ok!
 ```
 "##,
 
-E0413: r##"
-A declaration shadows an enum variant or unit-like struct in scope. Example of
-erroneous code:
-
-```compile_fail
-struct Foo;
-
-let Foo = 12i32; // error: declaration of `Foo` shadows an enum variant or
-                 //        unit-like struct in scope
-```
-
-To fix this error, rename the variable such that it doesn't shadow any enum
-variable or structure in scope. Example:
-
-```
-struct Foo;
-
-let foo = 12i32; // ok!
-```
-
-Or:
-
-```
-struct FooStruct;
-
-let Foo = 12i32; // ok!
-```
-
-The goal here is to avoid a conflict of names.
-"##,
-
-E0414: r##"
-A variable binding in an irrefutable pattern is shadowing the name of a
-constant. Example of erroneous code:
-
-```compile_fail
-const FOO: u8 = 7;
-
-let FOO = 5; // error: variable bindings cannot shadow constants
-
-// or
-
-fn bar(FOO: u8) { // error: variable bindings cannot shadow constants
-
-}
-
-// or
-
-for FOO in bar {
-
-}
-```
-
-Introducing a new variable in Rust is done through a pattern. Thus you can have
-`let` bindings like `let (a, b) = ...`. However, patterns also allow constants
-in them, e.g. if you want to match over a constant:
-
-```ignore
-const FOO: u8 = 1;
-
-match (x,y) {
- (3, 4) => { .. }, // it is (3,4)
- (FOO, 1) => { .. }, // it is (1,1)
- (foo, 1) => { .. }, // it is (anything, 1)
-                     // call the value in the first slot "foo"
- _ => { .. } // it is anything
-}
-```
-
-Here, the second arm matches the value of `x` against the constant `FOO`,
-whereas the third arm will accept any value of `x` and call it `foo`.
-
-This works for `match`, however in cases where an irrefutable pattern is
-required, constants can't be used. An irrefutable pattern is one which always
-matches, whose purpose is only to bind variable names to values. These are
-required by let, for, and function argument patterns.
-
-Refutable patterns in such a situation do not make sense, for example:
-
-```ignore
-let Some(x) = foo; // what if foo is None, instead?
-
-let (1, x) = foo; // what if foo.0 is not 1?
-
-let (SOME_CONST, x) = foo; // what if foo.0 is not SOME_CONST?
-
-let SOME_CONST = foo; // what if foo is not SOME_CONST?
-```
-
-Thus, an irrefutable variable binding can't contain a constant.
-
-To fix this error, just give the marked variable a different name.
-"##,
-
 E0415: r##"
-More than one function parameter have the same name. Example of erroneous code:
+More than one function parameter have the same name.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0415
 fn foo(f: i32, f: i32) {} // error: identifier `f` is bound more than
                           //        once in this parameter list
 ```
@@ -787,9 +791,11 @@ fn foo(f: i32, g: i32) {} // ok!
 "##,
 
 E0416: r##"
-An identifier is bound more than once in a pattern. Example of erroneous code:
+An identifier is bound more than once in a pattern.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0416
 match (1, 2) {
     (x, x) => {} // error: identifier `x` is bound more than once in the
                  //        same pattern
@@ -814,65 +820,12 @@ match (A, B, C) {
 ```
 "##,
 
-E0417: r##"
-A static variable was referenced in a pattern. Example of erroneous code:
-
-```compile_fail
-static FOO : i32 = 0;
-
-match 0 {
-    FOO => {} // error: static variables cannot be referenced in a
-              //        pattern, use a `const` instead
-    _ => {}
-}
-```
-
-The compiler needs to know the value of the pattern at compile time;
-compile-time patterns can defined via const or enum items. Please verify
-that the identifier is spelled correctly, and if so, use a const instead
-of static to define it. Example:
-
-```
-const FOO : i32 = 0;
-
-match 0 {
-    FOO => {} // ok!
-    _ => {}
-}
-```
-"##,
-
-E0419: r##"
-An unknown enum variant, struct or const was used. Example of erroneous code:
-
-```compile_fail
-match 0 {
-    Something::Foo => {} // error: unresolved enum variant, struct
-                         //        or const `Foo`
-}
-```
-
-Please verify you didn't misspell it and the enum variant, struct or const has
-been declared and imported into scope. Example:
-
-```
-enum Something {
-    Foo,
-    NotFoo,
-}
-
-match Something::NotFoo {
-    Something::Foo => {} // ok!
-    _ => {}
-}
-```
-"##,
-
 E0422: r##"
 You are trying to use an identifier that is either undefined or not a struct.
-For instance:
 
-``` compile_fail
+Erroneous code example:
+
+``` compile_fail,E0422
 fn main () {
     let x = Foo { x: 1, y: 2 };
 }
@@ -881,7 +834,7 @@ fn main () {
 In this case, `Foo` is undefined, so it inherently isn't anything, and
 definitely not a struct.
 
-```compile_fail
+```compile_fail,E0422
 fn main () {
     let foo = 1;
     let x = foo { x: 1, y: 2 };
@@ -893,10 +846,11 @@ one.
 "##,
 
 E0423: r##"
-A `struct` variant name was used like a function name. Example of erroneous
-code:
+A `struct` variant name was used like a function name.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0423
 struct Foo { a: bool};
 
 let f = Foo();
@@ -915,9 +869,11 @@ let f = Foo(); // ok!
 "##,
 
 E0424: r##"
-The `self` keyword was used in a static method. Example of erroneous code:
+The `self` keyword was used in a static method.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0424
 struct Foo;
 
 impl Foo {
@@ -947,9 +903,11 @@ impl Foo {
 "##,
 
 E0425: r##"
-An unresolved name was used. Example of erroneous codes:
+An unresolved name was used.
 
-```compile_fail
+Erroneous code examples:
+
+```compile_fail,E0425
 something_that_doesnt_exist::foo;
 // error: unresolved name `something_that_doesnt_exist::foo`
 
@@ -991,12 +949,25 @@ Or:
 let unknown_variable = 12u32;
 let x = unknown_variable; // ok!
 ```
+
+If the item is not defined in the current module, it must be imported using a
+`use` statement, like so:
+
+```ignore
+use foo::bar;
+bar();
+```
+
+If the item you are importing is not defined in some super-module of the
+current module, then it must also be declared as public (e.g., `pub fn`).
 "##,
 
 E0426: r##"
-An undeclared label was used. Example of erroneous code:
+An undeclared label was used.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0426
 loop {
     break 'a; // error: use of undeclared label `'a`
 }
@@ -1012,10 +983,11 @@ Please verify you spelt or declare the label correctly. Example:
 "##,
 
 E0428: r##"
-A type or module has been defined more than once. Example of erroneous
-code:
+A type or module has been defined more than once.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0428
 struct Bar;
 struct Bar; // error: duplicate definition of value `Bar`
 ```
@@ -1029,10 +1001,36 @@ struct Bar2; // ok!
 ```
 "##,
 
+E0429: r##"
+The `self` keyword cannot appear alone as the last segment in a `use`
+declaration.
+
+Erroneous code example:
+
+```compile_fail,E0429
+use std::fmt::self; // error: `self` imports are only allowed within a { } list
+```
+
+To use a namespace itself in addition to some of its members, `self` may appear
+as part of a brace-enclosed list of imports:
+
+```
+use std::fmt::{self, Debug};
+```
+
+If you only want to import the namespace, do so directly:
+
+```
+use std::fmt;
+```
+"##,
+
 E0430: r##"
-The `self` import appears more than once in the list. Erroneous code example:
+The `self` import appears more than once in the list.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0430
 use something::{self, self}; // error: `self` import can only appear once in
                              //        the list
 ```
@@ -1046,9 +1044,11 @@ use something::self; // ok!
 "##,
 
 E0431: r##"
-An invalid `self` import was made. Erroneous code example:
+An invalid `self` import was made.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0431
 use {self}; // error: `self` import can only appear in an import list with a
             //        non-empty prefix
 ```
@@ -1058,9 +1058,11 @@ or verify you didn't misspell it.
 "##,
 
 E0432: r##"
-An import was unresolved. Erroneous code example:
+An import was unresolved.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0432
 use something::Foo; // error: unresolved import `something::Foo`.
 ```
 
@@ -1089,14 +1091,23 @@ use homura::Madoka;
 "##,
 
 E0433: r##"
-Invalid import. Example of erroneous code:
+An undeclared type or module was used.
 
-```compile_fail
-use something_which_doesnt_exist;
-// error: unresolved import `something_which_doesnt_exist`
+Erroneous code example:
+
+```compile_fail,E0433
+let map = HashMap::new();
+// error: failed to resolve. Use of undeclared type or module `HashMap`
 ```
 
-Please verify you didn't misspell the import's name.
+Please verify you didn't misspell the type/module's name or that you didn't
+forgot to import it:
+
+
+```
+use std::collections::HashMap; // HashMap has been imported.
+let map: HashMap<u32, u32> = HashMap::new(); // So it can be used!
+```
 "##,
 
 E0434: r##"
@@ -1104,9 +1115,9 @@ This error indicates that a variable usage inside an inner function is invalid
 because the variable comes from a dynamic environment. Inner functions do not
 have access to their containing environment.
 
-Example of erroneous code:
+Erroneous code example:
 
-```compile_fail
+```compile_fail,E0434
 fn foo() {
     let y = 5;
     fn bar() -> u32 {
@@ -1145,10 +1156,11 @@ fn foo() {
 "##,
 
 E0435: r##"
-A non-constant value was used to initialise a constant. Example of erroneous
-code:
+A non-constant value was used to initialise a constant.
 
-```compile_fail
+Erroneous code example:
+
+```compile_fail,E0435
 let foo = 42u32;
 const FOO : u32 = foo; // error: attempt to use a non-constant value in a
                        //        constant
@@ -1174,9 +1186,9 @@ the trait in question. This error indicates that you attempted to implement
 an associated type whose name does not match the name of any associated type
 in the trait.
 
-Here is an example that demonstrates the error:
+Erroneous code example:
 
-```compile_fail
+```compile_fail,E0437
 trait Foo {}
 
 impl Foo for i32 {
@@ -1199,9 +1211,9 @@ members of the trait in question. This error indicates that you
 attempted to implement an associated constant whose name does not
 match the name of any associated constant in the trait.
 
-Here is an example that demonstrates the error:
+Erroneous code example:
 
-```compile_fail
+```compile_fail,E0438
 #![feature(associated_consts)]
 
 trait Foo {}
@@ -1229,11 +1241,17 @@ register_diagnostics! {
 //  E0257,
 //  E0258,
     E0402, // cannot use an outer type parameter in this context
-    E0406, // undeclared associated type
+//  E0406, merged into 420
 //  E0410, merged into 408
-    E0418, // is not an enum variant, struct or const
-    E0420, // is not an associated const
-    E0421, // unresolved associated const
-    E0427, // cannot use `ref` binding mode with ...
-    E0429, // `self` imports are only allowed within a { } list
+//  E0413, merged into 530
+//  E0414, merged into 530
+//  E0417, merged into 532
+//  E0418, merged into 532
+//  E0419, merged into 531
+//  E0420, merged into 532
+//  E0421, merged into 531
+    E0530, // X bindings cannot shadow Ys
+    E0531, // unresolved pattern path kind `name`
+    E0532, // expected pattern path kind, found another pattern path kind
+//  E0427, merged into 530
 }
index 61ed88ec173d4e2c8445afd482b72ccfe870f643..66b0d663424aa829a95c673de86b76e27b63ae8e 100644 (file)
 extern crate log;
 #[macro_use]
 extern crate syntax;
+extern crate syntax_pos;
+extern crate rustc_errors as errors;
 extern crate arena;
 #[macro_use]
 extern crate rustc;
 
-use self::PatternBindingMode::*;
 use self::Namespace::*;
 use self::ResolveResult::*;
 use self::FallbackSuggestion::*;
@@ -39,8 +40,6 @@ use self::TypeParameters::*;
 use self::RibKind::*;
 use self::UseLexicalScopeFlag::*;
 use self::ModulePrefixResult::*;
-use self::AssocItemResolveResult::*;
-use self::BareIdentifierPatternResolution::*;
 use self::ParentLink::*;
 
 use rustc::hir::map::Definitions;
@@ -48,7 +47,7 @@ use rustc::hir::{self, PrimTy, TyBool, TyChar, TyFloat, TyInt, TyUint, TyStr};
 use rustc::session::Session;
 use rustc::lint;
 use rustc::hir::def::*;
-use rustc::hir::def_id::DefId;
+use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
 use rustc::ty;
 use rustc::ty::subst::{ParamSpace, FnSpace, TypeSpace};
 use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
@@ -57,8 +56,6 @@ use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet};
 use syntax::ext::mtwt;
 use syntax::ast::{self, FloatTy};
 use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy};
-use syntax::codemap::{self, Span};
-use syntax::errors::DiagnosticBuilder;
 use syntax::parse::token::{self, keywords};
 use syntax::util::lev_distance::find_best_match_for_name;
 
@@ -66,8 +63,11 @@ use syntax::visit::{self, FnKind, Visitor};
 use syntax::ast::{Arm, BindingMode, Block, Crate, Expr, ExprKind};
 use syntax::ast::{FnDecl, ForeignItem, ForeignItemKind, Generics};
 use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind};
-use syntax::ast::{Local, Pat, PatKind, Path};
-use syntax::ast::{PathSegment, PathParameters, TraitItemKind, TraitRef, Ty, TyKind};
+use syntax::ast::{Local, Mutability, Pat, PatKind, Path};
+use syntax::ast::{PathSegment, PathParameters, QSelf, TraitItemKind, TraitRef, Ty, TyKind};
+
+use syntax_pos::Span;
+use errors::DiagnosticBuilder;
 
 use std::collections::{HashMap, HashSet};
 use std::cell::{Cell, RefCell};
@@ -107,8 +107,6 @@ enum ResolutionError<'a> {
     IsNotATrait(&'a str),
     /// error E0405: use of undeclared trait name
     UndeclaredTraitName(&'a str, SuggestedCandidates),
-    /// error E0406: undeclared associated type
-    UndeclaredAssociatedType,
     /// error E0407: method is not a member of trait
     MethodNotMemberOfTrait(Name, &'a str),
     /// error E0437: type is not a member of trait
@@ -123,24 +121,10 @@ enum ResolutionError<'a> {
     SelfUsedOutsideImplOrTrait,
     /// error E0412: use of undeclared
     UseOfUndeclared(&'a str, &'a str, SuggestedCandidates),
-    /// error E0413: cannot be named the same as an enum variant or unit-like struct in scope
-    DeclarationShadowsEnumVariantOrUnitLikeStruct(Name),
-    /// error E0414: only irrefutable patterns allowed here
-    ConstantForIrrefutableBinding(Name),
     /// error E0415: identifier is bound more than once in this parameter list
     IdentifierBoundMoreThanOnceInParameterList(&'a str),
     /// error E0416: identifier is bound more than once in the same pattern
     IdentifierBoundMoreThanOnceInSamePattern(&'a str),
-    /// error E0417: static variables cannot be referenced in a pattern
-    StaticVariableReference(&'a NameBinding<'a>),
-    /// error E0418: is not an enum variant, struct or const
-    NotAnEnumVariantStructOrConst(&'a str),
-    /// error E0419: unresolved enum variant, struct or const
-    UnresolvedEnumVariantStructOrConst(&'a str),
-    /// error E0420: is not an associated const
-    NotAnAssociatedConst(&'a str),
-    /// error E0421: unresolved associated const
-    UnresolvedAssociatedConst(&'a str),
     /// error E0422: does not name a struct
     DoesNotNameAStruct(&'a str),
     /// error E0423: is a struct variant name, but this expression uses it like a function name
@@ -148,11 +132,16 @@ enum ResolutionError<'a> {
     /// error E0424: `self` is not available in a static method
     SelfNotAvailableInStaticMethod,
     /// error E0425: unresolved name
-    UnresolvedName(&'a str, &'a str, UnresolvedNameContext<'a>),
+    UnresolvedName {
+        path: &'a str,
+        message: &'a str,
+        context: UnresolvedNameContext<'a>,
+        is_static_method: bool,
+        is_field: bool,
+        def: Def,
+    },
     /// error E0426: use of undeclared label
     UndeclaredLabel(&'a str),
-    /// error E0427: cannot use `ref` binding mode with ...
-    CannotUseRefBindingModeWith(&'a str),
     /// error E0429: `self` imports are only allowed within a { } list
     SelfImportsOnlyAllowedWithin,
     /// error E0430: `self` import can only appear once in the list
@@ -167,6 +156,12 @@ enum ResolutionError<'a> {
     CannotCaptureDynamicEnvironmentInFnItem,
     /// error E0435: attempt to use a non-constant value in a constant
     AttemptToUseNonConstantValueInConstant,
+    /// error E0530: X bindings cannot shadow Ys
+    BindingShadowsSomethingUnacceptable(&'a str, &'a str, Name),
+    /// error E0531: unresolved pattern path kind `name`
+    PatPathUnresolved(&'a str, &'a Path),
+    /// error E0532: expected pattern path kind, found another pattern path kind
+    PatPathUnexpected(&'a str, &'a str, &'a Path),
 }
 
 /// Context of where `ResolutionError::UnresolvedName` arose.
@@ -185,13 +180,13 @@ enum UnresolvedNameContext<'a> {
 }
 
 fn resolve_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
-                                 span: syntax::codemap::Span,
+                                 span: syntax_pos::Span,
                                  resolution_error: ResolutionError<'c>) {
     resolve_struct_error(resolver, span, resolution_error).emit();
 }
 
 fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
-                                        span: syntax::codemap::Span,
+                                        span: syntax_pos::Span,
                                         resolution_error: ResolutionError<'c>)
                                         -> DiagnosticBuilder<'a> {
     if !resolver.emit_errors {
@@ -235,9 +230,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
             err.span_label(span, &format!("`{}` is not in scope", name));
             err
         }
-        ResolutionError::UndeclaredAssociatedType => {
-            struct_span_err!(resolver.session, span, E0406, "undeclared associated type")
-        }
         ResolutionError::MethodNotMemberOfTrait(method, trait_) => {
             struct_span_err!(resolver.session,
                              span,
@@ -299,32 +291,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
             err.span_label(span, &format!("undefined or not in scope"));
             err
         }
-        ResolutionError::DeclarationShadowsEnumVariantOrUnitLikeStruct(name) => {
-            let mut err = struct_span_err!(resolver.session,
-                             span,
-                             E0413,
-                             "`{}` cannot be named the same as an enum variant \
-                              or unit-like struct in scope",
-                             name);
-            err.span_label(span,
-                &format!("has same name as enum variant or unit-like struct"));
-            err
-        }
-        ResolutionError::ConstantForIrrefutableBinding(name) => {
-            let mut err = struct_span_err!(resolver.session,
-                                           span,
-                                           E0414,
-                                       "let variables cannot be named the same as const variables");
-            err.span_label(span,
-                           &format!("cannot be named the same as a const variable"));
-            if let Some(binding) = resolver.current_module
-                                           .resolve_name_in_lexical_scope(name, ValueNS) {
-                let participle = if binding.is_import() { "imported" } else { "defined" };
-                err.span_label(binding.span, &format!("a constant `{}` is {} here",
-                               name, participle));
-            }
-            err
-        }
         ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => {
             let mut err = struct_span_err!(resolver.session,
                              span,
@@ -343,47 +309,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
             err.span_label(span, &format!("used in a pattern more than once"));
             err
         }
-        ResolutionError::StaticVariableReference(binding) => {
-            let mut err = struct_span_err!(resolver.session,
-                                           span,
-                                           E0417,
-                                           "static variables cannot be referenced in a \
-                                            pattern, use a `const` instead");
-            err.span_label(span, &format!("static variable used in pattern"));
-            if binding.span != codemap::DUMMY_SP {
-                let participle = if binding.is_import() { "imported" } else { "defined" };
-                err.span_label(binding.span, &format!("static variable {} here", participle));
-            }
-            err
-        }
-        ResolutionError::NotAnEnumVariantStructOrConst(name) => {
-            struct_span_err!(resolver.session,
-                             span,
-                             E0418,
-                             "`{}` is not an enum variant, struct or const",
-                             name)
-        }
-        ResolutionError::UnresolvedEnumVariantStructOrConst(name) => {
-            struct_span_err!(resolver.session,
-                             span,
-                             E0419,
-                             "unresolved enum variant, struct or const `{}`",
-                             name)
-        }
-        ResolutionError::NotAnAssociatedConst(name) => {
-            struct_span_err!(resolver.session,
-                             span,
-                             E0420,
-                             "`{}` is not an associated const",
-                             name)
-        }
-        ResolutionError::UnresolvedAssociatedConst(name) => {
-            struct_span_err!(resolver.session,
-                             span,
-                             E0421,
-                             "unresolved associated const `{}`",
-                             name)
-        }
         ResolutionError::DoesNotNameAStruct(name) => {
             struct_span_err!(resolver.session,
                              span,
@@ -406,32 +331,38 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
                              "`self` is not available in a static method. Maybe a `self` \
                              argument is missing?")
         }
-        ResolutionError::UnresolvedName(path, msg, context) => {
+        ResolutionError::UnresolvedName { path, message: msg, context, is_static_method,
+                                          is_field, def } => {
             let mut err = struct_span_err!(resolver.session,
                                            span,
                                            E0425,
                                            "unresolved name `{}`{}",
                                            path,
                                            msg);
-
             match context {
-                UnresolvedNameContext::Other => { } // no help available
+                UnresolvedNameContext::Other => {
+                    if msg.is_empty() && is_static_method && is_field {
+                        err.help("this is an associated function, you don't have access to \
+                                  this type's fields or methods");
+                    }
+                }
                 UnresolvedNameContext::PathIsMod(parent) => {
                     err.help(&match parent.map(|parent| &parent.node) {
                         Some(&ExprKind::Field(_, ident)) => {
-                            format!("To reference an item from the `{module}` module, \
+                            format!("to reference an item from the `{module}` module, \
                                      use `{module}::{ident}`",
                                     module = path,
                                     ident = ident.node)
                         }
                         Some(&ExprKind::MethodCall(ident, _, _)) => {
-                            format!("To call a function from the `{module}` module, \
+                            format!("to call a function from the `{module}` module, \
                                      use `{module}::{ident}(..)`",
                                     module = path,
                                     ident = ident.node)
                         }
                         _ => {
-                            format!("Module `{module}` cannot be used as an expression",
+                            format!("{def} `{module}` cannot be used as an expression",
+                                    def = def.kind_name(),
                                     module = path)
                         }
                     });
@@ -446,13 +377,6 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
                              "use of undeclared label `{}`",
                              name)
         }
-        ResolutionError::CannotUseRefBindingModeWith(descr) => {
-            struct_span_err!(resolver.session,
-                             span,
-                             E0427,
-                             "cannot use `ref` binding mode with {}",
-                             descr)
-        }
         ResolutionError::SelfImportsOnlyAllowedWithin => {
             struct_span_err!(resolver.session,
                              span,
@@ -497,6 +421,36 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
                              E0435,
                              "attempt to use a non-constant value in a constant")
         }
+        ResolutionError::BindingShadowsSomethingUnacceptable(what_binding, shadows_what, name) => {
+            let mut err = struct_span_err!(resolver.session,
+                                           span,
+                                           E0530,
+                                           "{}s cannot shadow {}s", what_binding, shadows_what);
+            err.span_label(span, &format!("cannot be named the same as a {}", shadows_what));
+            if let Success(binding) = resolver.current_module.resolve_name(name, ValueNS, true) {
+                let participle = if binding.is_import() { "imported" } else { "defined" };
+                err.span_label(binding.span, &format!("a {} `{}` is {} here",
+                                                      shadows_what, name, participle));
+            }
+            err
+        }
+        ResolutionError::PatPathUnresolved(expected_what, path) => {
+            struct_span_err!(resolver.session,
+                             span,
+                             E0531,
+                             "unresolved {} `{}`",
+                             expected_what,
+                             path.segments.last().unwrap().identifier)
+        }
+        ResolutionError::PatPathUnexpected(expected_what, found_what, path) => {
+            struct_span_err!(resolver.session,
+                             span,
+                             E0532,
+                             "expected {}, found {} `{}`",
+                             expected_what,
+                             found_what,
+                             path.segments.last().unwrap().identifier)
+        }
     }
 }
 
@@ -509,11 +463,33 @@ struct BindingInfo {
 // Map from the name in a pattern to its binding mode.
 type BindingMap = HashMap<Name, BindingInfo>;
 
-#[derive(Copy, Clone, PartialEq)]
-enum PatternBindingMode {
-    RefutableMode,
-    LocalIrrefutableMode,
-    ArgumentIrrefutableMode,
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum PatternSource {
+    Match,
+    IfLet,
+    WhileLet,
+    Let,
+    For,
+    FnParam,
+}
+
+impl PatternSource {
+    fn is_refutable(self) -> bool {
+        match self {
+            PatternSource::Match | PatternSource::IfLet | PatternSource::WhileLet => true,
+            PatternSource::Let | PatternSource::For | PatternSource::FnParam  => false,
+        }
+    }
+    fn descr(self) -> &'static str {
+        match self {
+            PatternSource::Match => "match binding",
+            PatternSource::IfLet => "if let binding",
+            PatternSource::WhileLet => "while let binding",
+            PatternSource::Let => "let binding",
+            PatternSource::For => "for binding",
+            PatternSource::FnParam => "function parameter",
+        }
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -522,7 +498,7 @@ pub enum Namespace {
     ValueNS,
 }
 
-impl<'a, 'v> Visitor<'v> for Resolver<'a> {
+impl<'a> Visitor for Resolver<'a> {
     fn visit_item(&mut self, item: &Item) {
         self.resolve_item(item);
     }
@@ -541,9 +517,6 @@ impl<'a, 'v> Visitor<'v> for Resolver<'a> {
     fn visit_ty(&mut self, ty: &Ty) {
         self.resolve_type(ty);
     }
-    fn visit_generics(&mut self, generics: &Generics) {
-        self.resolve_generics(generics);
-    }
     fn visit_poly_trait_ref(&mut self, tref: &ast::PolyTraitRef, m: &ast::TraitBoundModifier) {
         match self.resolve_trait_reference(tref.trait_ref.ref_id, &tref.trait_ref.path, 0) {
             Ok(def) => self.record_def(tref.trait_ref.ref_id, def),
@@ -584,9 +557,9 @@ impl<'a, 'v> Visitor<'v> for Resolver<'a> {
         });
     }
     fn visit_fn(&mut self,
-                function_kind: FnKind<'v>,
-                declaration: &'v FnDecl,
-                block: &'v Block,
+                function_kind: FnKind,
+                declaration: &FnDecl,
+                block: &Block,
                 _: Span,
                 node_id: NodeId) {
         let rib_kind = match function_kind {
@@ -596,7 +569,7 @@ impl<'a, 'v> Visitor<'v> for Resolver<'a> {
             }
             FnKind::Method(_, sig, _) => {
                 self.visit_generics(&sig.generics);
-                MethodRibKind
+                MethodRibKind(!sig.decl.has_self())
             }
             FnKind::Closure => ClosureRibKind(node_id),
         };
@@ -666,7 +639,9 @@ enum RibKind<'a> {
     // methods. Allow references to ty params that impl or trait
     // binds. Disallow any other upvars (including other ty params that are
     // upvars).
-    MethodRibKind,
+    //
+    // The boolean value represents the fact that this method is static or not.
+    MethodRibKind(bool),
 
     // We passed through an item scope. Disallow upvars.
     ItemRibKind,
@@ -689,22 +664,6 @@ enum ModulePrefixResult<'a> {
     PrefixFound(Module<'a>, usize),
 }
 
-#[derive(Copy, Clone)]
-enum AssocItemResolveResult {
-    /// Syntax such as `<T>::item`, which can't be resolved until type
-    /// checking.
-    TypecheckRequired,
-    /// We should have been able to resolve the associated item.
-    ResolveAttempt(Option<PathResolution>),
-}
-
-#[derive(Copy, Clone)]
-enum BareIdentifierPatternResolution {
-    FoundStructOrEnumVariant(Def),
-    FoundConst(Def, Name),
-    BareIdentifierPatternUnresolved,
-}
-
 /// One local scope.
 #[derive(Debug)]
 struct Rib<'a> {
@@ -777,7 +736,7 @@ pub struct ModuleS<'a> {
     resolutions: RefCell<HashMap<(Name, Namespace), &'a RefCell<NameResolution<'a>>>>,
     unresolved_imports: RefCell<Vec<&'a ImportDirective<'a>>>,
 
-    prelude: RefCell<Option<Module<'a>>>,
+    no_implicit_prelude: Cell<bool>,
 
     glob_importers: RefCell<Vec<(Module<'a>, &'a ImportDirective<'a>)>>,
     globs: RefCell<Vec<&'a ImportDirective<'a>>>,
@@ -806,7 +765,7 @@ impl<'a> ModuleS<'a> {
             extern_crate_id: None,
             resolutions: RefCell::new(HashMap::new()),
             unresolved_imports: RefCell::new(Vec::new()),
-            prelude: RefCell::new(None),
+            no_implicit_prelude: Cell::new(false),
             glob_importers: RefCell::new(Vec::new()),
             globs: RefCell::new((Vec::new())),
             traits: RefCell::new(None),
@@ -966,10 +925,12 @@ impl PrimitiveTypeTable {
 pub struct Resolver<'a> {
     session: &'a Session,
 
-    definitions: &'a mut Definitions,
+    pub definitions: Definitions,
 
     graph_root: Module<'a>,
 
+    prelude: Option<Module<'a>>,
+
     trait_item_map: FnvHashMap<(Name, DefId), bool /* is static method? */>,
 
     structs: FnvHashMap<DefId, Vec<Name>>,
@@ -1040,7 +1001,7 @@ pub struct Resolver<'a> {
     arenas: &'a ResolverArenas<'a>,
 }
 
-struct ResolverArenas<'a> {
+pub struct ResolverArenas<'a> {
     modules: arena::TypedArena<ModuleS<'a>>,
     local_modules: RefCell<Vec<Module<'a>>>,
     name_bindings: arena::TypedArena<NameBinding<'a>>,
@@ -1095,7 +1056,14 @@ impl<'a> hir::lowering::Resolver for Resolver<'a> {
             Err(false) => {
                 let path_name = &format!("{}", path);
                 let error =
-                    ResolutionError::UnresolvedName(path_name, "", UnresolvedNameContext::Other);
+                    ResolutionError::UnresolvedName {
+                        path: path_name,
+                        message: "",
+                        context: UnresolvedNameContext::Other,
+                        is_static_method: false,
+                        is_field: false,
+                        def: Def::Err,
+                    };
                 resolve_error(self, path.span, error);
                 Def::Err
             }
@@ -1107,11 +1075,11 @@ impl<'a> hir::lowering::Resolver for Resolver<'a> {
     }
 
     fn record_resolution(&mut self, id: NodeId, def: Def) {
-        self.def_map.insert(id, PathResolution { base_def: def, depth: 0 });
+        self.def_map.insert(id, PathResolution::new(def));
     }
 
     fn definitions(&mut self) -> Option<&mut Definitions> {
-        Some(self.definitions)
+        Some(&mut self.definitions)
     }
 }
 
@@ -1132,12 +1100,9 @@ impl Named for hir::PathSegment {
 }
 
 impl<'a> Resolver<'a> {
-    fn new(session: &'a Session,
-           definitions: &'a mut Definitions,
-           make_glob_map: MakeGlobMap,
-           arenas: &'a ResolverArenas<'a>)
-           -> Resolver<'a> {
-        let root_def_id = definitions.local_def_id(CRATE_NODE_ID);
+    pub fn new(session: &'a Session, make_glob_map: MakeGlobMap, arenas: &'a ResolverArenas<'a>)
+               -> Resolver<'a> {
+        let root_def_id = DefId::local(CRATE_DEF_INDEX);
         let graph_root =
             ModuleS::new(NoParentLink, Some(Def::Mod(root_def_id)), false, arenas);
         let graph_root = arenas.alloc_module(graph_root);
@@ -1147,11 +1112,12 @@ impl<'a> Resolver<'a> {
         Resolver {
             session: session,
 
-            definitions: definitions,
+            definitions: Definitions::new(),
 
             // The outermost module has def ID 0; this is not reflected in the
             // AST.
             graph_root: graph_root,
+            prelude: None,
 
             trait_item_map: FnvHashMap(),
             structs: FnvHashMap(),
@@ -1189,7 +1155,7 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    fn arenas() -> ResolverArenas<'a> {
+    pub fn arenas() -> ResolverArenas<'a> {
         ResolverArenas {
             modules: arena::TypedArena::new(),
             local_modules: RefCell::new(Vec::new()),
@@ -1199,6 +1165,15 @@ impl<'a> Resolver<'a> {
         }
     }
 
+    /// Entry point to crate resolution.
+    pub fn resolve_crate(&mut self, krate: &Crate) {
+        self.current_module = self.graph_root;
+        visit::walk_crate(self, krate);
+
+        check_unused::check_crate(self, krate);
+        self.report_privacy_errors();
+    }
+
     fn new_module(&self, parent_link: ParentLink<'a>, def: Option<Def>, external: bool)
                   -> Module<'a> {
         self.arenas.alloc_module(ModuleS::new(parent_link, def, external, self.arenas))
@@ -1434,7 +1409,15 @@ impl<'a> Resolver<'a> {
                 }
 
                 // We can only see through anonymous modules
-                if module.def.is_some() { return None; }
+                if module.def.is_some() {
+                    return match self.prelude {
+                        Some(prelude) if !module.no_implicit_prelude.get() => {
+                            prelude.resolve_name(name, ns, false).success()
+                                   .map(LexicalScopeBinding::Item)
+                        }
+                        _ => None,
+                    };
+                }
             }
         }
 
@@ -1521,11 +1504,7 @@ impl<'a> Resolver<'a> {
         debug!("(resolving name in module) resolving `{}` in `{}`", name, module_to_string(module));
 
         self.populate_module_if_necessary(module);
-        match use_lexical_scope {
-            true => module.resolve_name_in_lexical_scope(name, namespace)
-                          .map(Success).unwrap_or(Failed(None)),
-            false => module.resolve_name(name, namespace, false),
-        }.and_then(|binding| {
+        module.resolve_name(name, namespace, use_lexical_scope).and_then(|binding| {
             if record_used {
                 if let NameBindingKind::Import { directive, .. } = binding.kind {
                     self.used_imports.insert((directive.id, namespace));
@@ -1595,12 +1574,6 @@ impl<'a> Resolver<'a> {
         None
     }
 
-    fn resolve_crate(&mut self, krate: &Crate) {
-        debug!("(resolving crate) starting");
-        self.current_module = self.graph_root;
-        visit::walk_crate(self, krate);
-    }
-
     fn resolve_item(&mut self, item: &Item) {
         let name = item.ident.name;
 
@@ -1657,7 +1630,7 @@ impl<'a> Resolver<'a> {
                                     let type_parameters =
                                         HasTypeParameters(&sig.generics,
                                                           FnSpace,
-                                                          MethodRibKind);
+                                                          MethodRibKind(!sig.decl.has_self()));
                                     this.with_type_parameter_rib(type_parameters, |this| {
                                         visit::walk_trait_item(this, trait_item)
                                     });
@@ -1667,6 +1640,7 @@ impl<'a> Resolver<'a> {
                                         visit::walk_trait_item(this, trait_item)
                                     });
                                 }
+                                TraitItemKind::Macro(_) => panic!("unexpanded macro in resolve!"),
                             };
                         }
                     });
@@ -1695,7 +1669,7 @@ impl<'a> Resolver<'a> {
                                                                    TypeNS) {
                                 Ok(binding) => {
                                     let def = binding.def().unwrap();
-                                    self.record_def(item.id, PathResolution::new(def, 0));
+                                    self.record_def(item.id, PathResolution::new(def));
                                 }
                                 Err(true) => self.record_def(item.id, err_path_resolution()),
                                 Err(false) => {
@@ -1776,7 +1750,10 @@ impl<'a> Resolver<'a> {
         self.value_ribs.pop();
     }
 
-    fn resolve_function(&mut self, rib_kind: RibKind<'a>, declaration: &FnDecl, block: &Block) {
+    fn resolve_function(&mut self,
+                        rib_kind: RibKind<'a>,
+                        declaration: &FnDecl,
+                        block: &Block) {
         // Create a value rib for the function.
         self.value_ribs.push(Rib::new(rib_kind));
 
@@ -1786,7 +1763,7 @@ impl<'a> Resolver<'a> {
         // Add each argument to the rib.
         let mut bindings_list = HashMap::new();
         for argument in &declaration.inputs {
-            self.resolve_pattern(&argument.pat, ArgumentIrrefutableMode, &mut bindings_list);
+            self.resolve_pattern(&argument.pat, PatternSource::FnParam, &mut bindings_list);
 
             self.visit_ty(&argument.ty);
 
@@ -1831,10 +1808,10 @@ impl<'a> Resolver<'a> {
                             self.resolve_crate_relative_path(trait_path.span, segments, TypeNS)
                         } else {
                             self.resolve_module_relative_path(trait_path.span, segments, TypeNS)
-                        }.map(|binding| binding.span).unwrap_or(codemap::DUMMY_SP)
+                        }.map(|binding| binding.span).unwrap_or(syntax_pos::DUMMY_SP)
                     };
 
-                    if definition_site != codemap::DUMMY_SP {
+                    if definition_site != syntax_pos::DUMMY_SP {
                         err.span_label(definition_site,
                                        &format!("type aliases cannot be used for traits"));
                     }
@@ -1869,30 +1846,6 @@ impl<'a> Resolver<'a> {
         })
     }
 
-    fn resolve_generics(&mut self, generics: &Generics) {
-        for predicate in &generics.where_clause.predicates {
-            match predicate {
-                &ast::WherePredicate::BoundPredicate(_) |
-                &ast::WherePredicate::RegionPredicate(_) => {}
-                &ast::WherePredicate::EqPredicate(ref eq_pred) => {
-                    self.resolve_path(eq_pred.id, &eq_pred.path, 0, TypeNS).and_then(|path_res| {
-                        if let PathResolution { base_def: Def::TyParam(..), .. } = path_res {
-                            Ok(self.record_def(eq_pred.id, path_res))
-                        } else {
-                            Err(false)
-                        }
-                    }).map_err(|error_reported| {
-                        self.record_def(eq_pred.id, err_path_resolution());
-                        if error_reported { return }
-                        let error_variant = ResolutionError::UndeclaredAssociatedType;
-                        resolve_error(self, eq_pred.span, error_variant);
-                    }).unwrap_or(());
-                }
-            }
-        }
-        visit::walk_generics(self, generics);
-    }
-
     fn with_current_self_type<T, F>(&mut self, self_type: &Ty, f: F) -> T
         where F: FnOnce(&mut Resolver) -> T
     {
@@ -1983,7 +1936,7 @@ impl<'a> Resolver<'a> {
                                     let type_parameters =
                                         HasTypeParameters(&sig.generics,
                                                           FnSpace,
-                                                          MethodRibKind);
+                                                          MethodRibKind(!sig.decl.has_self()));
                                     this.with_type_parameter_rib(type_parameters, |this| {
                                         visit::walk_impl_item(this, impl_item);
                                     });
@@ -2027,7 +1980,7 @@ impl<'a> Resolver<'a> {
         walk_list!(self, visit_expr, &local.init);
 
         // Resolve the pattern.
-        self.resolve_pattern(&local.pat, LocalIrrefutableMode, &mut HashMap::new());
+        self.resolve_pattern(&local.pat, PatternSource::Let, &mut HashMap::new());
     }
 
     // build a map from pattern identifiers to binding-info's.
@@ -2096,7 +2049,7 @@ impl<'a> Resolver<'a> {
 
         let mut bindings_list = HashMap::new();
         for pattern in &arm.pats {
-            self.resolve_pattern(&pattern, RefutableMode, &mut bindings_list);
+            self.resolve_pattern(&pattern, PatternSource::Match, &mut bindings_list);
         }
 
         // This has to happen *after* we determine which
@@ -2139,26 +2092,22 @@ impl<'a> Resolver<'a> {
     fn resolve_type(&mut self, ty: &Ty) {
         match ty.node {
             TyKind::Path(ref maybe_qself, ref path) => {
-                let resolution = match self.resolve_possibly_assoc_item(ty.id,
-                                                                        maybe_qself.as_ref(),
-                                                                        path,
-                                                                        TypeNS) {
-                    // `<T>::a::b::c` is resolved by typeck alone.
-                    TypecheckRequired => {
-                        // Resolve embedded types.
-                        visit::walk_ty(self, ty);
-                        return;
-                    }
-                    ResolveAttempt(resolution) => resolution,
-                };
-
                 // This is a path in the type namespace. Walk through scopes
                 // looking for it.
-                if let Some(def) = resolution {
-                    // Write the result into the def map.
-                    debug!("(resolving type) writing resolution for `{}` (id {}) = {:?}",
-                           path_names_to_string(path, 0), ty.id, def);
-                    self.record_def(ty.id, def);
+                if let Some(def) = self.resolve_possibly_assoc_item(ty.id, maybe_qself.as_ref(),
+                                                                    path, TypeNS) {
+                    match def.base_def {
+                        Def::Mod(..) if def.depth == 0 => {
+                            self.session.span_err(path.span, "expected type, found module");
+                            self.record_def(ty.id, err_path_resolution());
+                        }
+                        _ => {
+                            // Write the result into the def map.
+                            debug!("(resolving type) writing resolution for `{}` (id {}) = {:?}",
+                                   path_names_to_string(path, 0), ty.id, def);
+                            self.record_def(ty.id, def);
+                        }
+                    }
                 } else {
                     self.record_def(ty.id, err_path_resolution());
 
@@ -2218,318 +2167,221 @@ impl<'a> Resolver<'a> {
         visit::walk_ty(self, ty);
     }
 
-    fn resolve_pattern(&mut self,
-                       pattern: &Pat,
-                       mode: PatternBindingMode,
-                       // Maps idents to the node ID for the (outermost)
-                       // pattern that binds them
-                       bindings_list: &mut HashMap<Name, NodeId>) {
-        let pat_id = pattern.id;
-        pattern.walk(&mut |pattern| {
-            match pattern.node {
-                PatKind::Ident(binding_mode, ref path1, ref at_rhs) => {
-                    // The meaning of PatKind::Ident with no type parameters
-                    // depends on whether an enum variant or unit-like struct
-                    // with that name is in scope. The probing lookup has to
-                    // be careful not to emit spurious errors. Only matching
-                    // patterns (match) can match nullary variants or
-                    // unit-like structs. For binding patterns (let
-                    // and the LHS of @-patterns), matching such a value is
-                    // simply disallowed (since it's rarely what you want).
-                    let const_ok = mode == RefutableMode && at_rhs.is_none();
-
-                    let ident = path1.node;
-                    let renamed = mtwt::resolve(ident);
-
-                    match self.resolve_bare_identifier_pattern(ident, pattern.span) {
-                        FoundStructOrEnumVariant(def) if const_ok => {
-                            debug!("(resolving pattern) resolving `{}` to struct or enum variant",
-                                   renamed);
-
-                            self.enforce_default_binding_mode(pattern,
-                                                              binding_mode,
-                                                              "an enum variant");
-                            self.record_def(pattern.id,
-                                            PathResolution {
-                                                base_def: def,
-                                                depth: 0,
-                                            });
-                        }
-                        FoundStructOrEnumVariant(..) => {
-                            resolve_error(
-                                self,
-                                pattern.span,
-                                ResolutionError::DeclarationShadowsEnumVariantOrUnitLikeStruct(
-                                    renamed)
-                            );
-                            self.record_def(pattern.id, err_path_resolution());
-                        }
-                        FoundConst(def, _) if const_ok => {
-                            debug!("(resolving pattern) resolving `{}` to constant", renamed);
-
-                            self.enforce_default_binding_mode(pattern, binding_mode, "a constant");
-                            self.record_def(pattern.id,
-                                            PathResolution {
-                                                base_def: def,
-                                                depth: 0,
-                                            });
-                        }
-                        FoundConst(_, name) => {
-                            resolve_error(
-                                self,
-                                pattern.span,
-                                ResolutionError::ConstantForIrrefutableBinding(name)
-                            );
-                            self.record_def(pattern.id, err_path_resolution());
-                        }
-                        BareIdentifierPatternUnresolved => {
-                            debug!("(resolving pattern) binding `{}`", renamed);
-
-                            let def_id = self.definitions.local_def_id(pattern.id);
-                            let def = Def::Local(def_id, pattern.id);
-
-                            // Record the definition so that later passes
-                            // will be able to distinguish variants from
-                            // locals in patterns.
-
-                            self.record_def(pattern.id,
-                                            PathResolution {
-                                                base_def: def,
-                                                depth: 0,
-                                            });
-
-                            // Add the binding to the local ribs, if it
-                            // doesn't already exist in the bindings list. (We
-                            // must not add it if it's in the bindings list
-                            // because that breaks the assumptions later
-                            // passes make about or-patterns.)
-                            if !bindings_list.contains_key(&renamed) {
-                                let this = &mut *self;
-                                let last_rib = this.value_ribs.last_mut().unwrap();
-                                last_rib.bindings.insert(renamed, def);
-                                bindings_list.insert(renamed, pat_id);
-                            } else if mode == ArgumentIrrefutableMode &&
-                               bindings_list.contains_key(&renamed) {
-                                // Forbid duplicate bindings in the same
-                                // parameter list.
-                                resolve_error(
-                                    self,
-                                    pattern.span,
-                                    ResolutionError::IdentifierBoundMoreThanOnceInParameterList(
-                                        &ident.name.as_str())
-                                );
-                            } else if bindings_list.get(&renamed) == Some(&pat_id) {
-                                // Then this is a duplicate variable in the
-                                // same disjunction, which is an error.
-                                resolve_error(
-                                    self,
-                                    pattern.span,
-                                    ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(
-                                        &ident.name.as_str())
-                                );
-                            }
-                            // Else, not bound in the same pattern: do
-                            // nothing.
-                        }
-                    }
+    fn fresh_binding(&mut self,
+                     ident: &ast::SpannedIdent,
+                     pat_id: NodeId,
+                     outer_pat_id: NodeId,
+                     pat_src: PatternSource,
+                     bindings: &mut HashMap<Name, NodeId>)
+                     -> PathResolution {
+        // Add the binding to the local ribs, if it
+        // doesn't already exist in the bindings map. (We
+        // must not add it if it's in the bindings map
+        // because that breaks the assumptions later
+        // passes make about or-patterns.)
+        let renamed = mtwt::resolve(ident.node);
+        let def = match bindings.get(&renamed).cloned() {
+            Some(id) if id == outer_pat_id => {
+                // `Variant(a, a)`, error
+                resolve_error(
+                    self,
+                    ident.span,
+                    ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(
+                        &ident.node.name.as_str())
+                );
+                Def::Err
+            }
+            Some(..) if pat_src == PatternSource::FnParam => {
+                // `fn f(a: u8, a: u8)`, error
+                resolve_error(
+                    self,
+                    ident.span,
+                    ResolutionError::IdentifierBoundMoreThanOnceInParameterList(
+                        &ident.node.name.as_str())
+                );
+                Def::Err
+            }
+            Some(..) if pat_src == PatternSource::Match => {
+                // `Variant1(a) | Variant2(a)`, ok
+                // Reuse definition from the first `a`.
+                self.value_ribs.last_mut().unwrap().bindings[&renamed]
+            }
+            Some(..) => {
+                span_bug!(ident.span, "two bindings with the same name from \
+                                       unexpected pattern source {:?}", pat_src);
+            }
+            None => {
+                // A completely fresh binding, add to the lists.
+                // FIXME: Later stages are not ready to deal with `Def::Err` here yet, so
+                // define `Invalid` bindings as `Def::Local`, just don't add them to the lists.
+                let def = Def::Local(self.definitions.local_def_id(pat_id), pat_id);
+                if ident.node.name != keywords::Invalid.name() {
+                    bindings.insert(renamed, outer_pat_id);
+                    self.value_ribs.last_mut().unwrap().bindings.insert(renamed, def);
                 }
+                def
+            }
+        };
 
-                PatKind::TupleStruct(ref path, _) | PatKind::Path(ref path) => {
-                    // This must be an enum variant, struct or const.
-                    let resolution = match self.resolve_possibly_assoc_item(pat_id,
-                                                                            None,
-                                                                            path,
-                                                                            ValueNS) {
-                        // The below shouldn't happen because all
-                        // qualified paths should be in PatKind::QPath.
-                        TypecheckRequired =>
-                            span_bug!(path.span,
-                                      "resolve_possibly_assoc_item claimed that a path \
-                                       in PatKind::Path or PatKind::TupleStruct \
-                                       requires typecheck to resolve, but qualified \
-                                       paths should be PatKind::QPath"),
-                        ResolveAttempt(resolution) => resolution,
-                    };
-                    if let Some(path_res) = resolution {
-                        match path_res.base_def {
-                            Def::Struct(..) if path_res.depth == 0 => {
-                                self.record_def(pattern.id, path_res);
-                            }
-                            Def::Variant(..) | Def::Const(..) => {
-                                self.record_def(pattern.id, path_res);
-                            }
-                            Def::Static(..) => {
-                                let segments = &path.segments;
-                                let binding = if path.global {
-                                    self.resolve_crate_relative_path(path.span, segments, ValueNS)
-                                } else {
-                                    self.resolve_module_relative_path(path.span, segments, ValueNS)
-                                }.unwrap();
+        PathResolution::new(def)
+    }
 
-                                let error = ResolutionError::StaticVariableReference(binding);
-                                resolve_error(self, path.span, error);
-                                self.record_def(pattern.id, err_path_resolution());
-                            }
-                            _ => {
-                                // If anything ends up here entirely resolved,
-                                // it's an error. If anything ends up here
-                                // partially resolved, that's OK, because it may
-                                // be a `T::CONST` that typeck will resolve.
-                                if path_res.depth == 0 {
-                                    resolve_error(
-                                        self,
-                                        path.span,
-                                        ResolutionError::NotAnEnumVariantStructOrConst(
-                                            &path.segments
-                                                 .last()
-                                                 .unwrap()
-                                                 .identifier
-                                                 .name
-                                                 .as_str())
-                                    );
-                                    self.record_def(pattern.id, err_path_resolution());
-                                } else {
-                                    let const_name = path.segments
-                                                         .last()
-                                                         .unwrap()
-                                                         .identifier
-                                                         .name;
-                                    let traits = self.get_traits_containing_item(const_name);
-                                    self.trait_map.insert(pattern.id, traits);
-                                    self.record_def(pattern.id, path_res);
-                                }
-                            }
-                        }
-                    } else if let Err(false) = self.resolve_path(pat_id, &path, 0, ValueNS) {
-                        resolve_error(
-                            self,
-                            path.span,
-                            ResolutionError::UnresolvedEnumVariantStructOrConst(
-                                &path.segments.last().unwrap().identifier.name.as_str())
-                        );
-                        self.record_def(pattern.id, err_path_resolution());
-                    }
-                    visit::walk_path(self, path);
+    fn resolve_pattern_path<ExpectedFn>(&mut self,
+                                        pat_id: NodeId,
+                                        qself: Option<&QSelf>,
+                                        path: &Path,
+                                        namespace: Namespace,
+                                        expected_fn: ExpectedFn,
+                                        expected_what: &str)
+        where ExpectedFn: FnOnce(Def) -> bool
+    {
+        let resolution = if let Some(resolution) = self.resolve_possibly_assoc_item(pat_id,
+                                                                        qself, path, namespace) {
+            if resolution.depth == 0 {
+                if expected_fn(resolution.base_def) {
+                    resolution
+                } else {
+                    resolve_error(
+                        self,
+                        path.span,
+                        ResolutionError::PatPathUnexpected(expected_what,
+                                                           resolution.kind_name(), path)
+                    );
+                    err_path_resolution()
+                }
+            } else {
+                // Not fully resolved associated item `T::A::B` or `<T as Tr>::A::B`
+                // or `<T>::A::B`. If `B` should be resolved in value namespace then
+                // it needs to be added to the trait map.
+                if namespace == ValueNS {
+                    let item_name = path.segments.last().unwrap().identifier.name;
+                    let traits = self.get_traits_containing_item(item_name);
+                    self.trait_map.insert(pat_id, traits);
                 }
+                resolution
+            }
+        } else {
+            if let Err(false) = self.resolve_path(pat_id, path, 0, namespace) {
+                resolve_error(
+                    self,
+                    path.span,
+                    ResolutionError::PatPathUnresolved(expected_what, path)
+                );
+            }
+            err_path_resolution()
+        };
 
-                PatKind::QPath(ref qself, ref path) => {
-                    // Associated constants only.
-                    let resolution = match self.resolve_possibly_assoc_item(pat_id,
-                                                                            Some(qself),
-                                                                            path,
-                                                                            ValueNS) {
-                        TypecheckRequired => {
-                            // All `<T>::CONST` should end up here, and will
-                            // require use of the trait map to resolve
-                            // during typechecking.
-                            let const_name = path.segments
-                                                 .last()
-                                                 .unwrap()
-                                                 .identifier
-                                                 .name;
-                            let traits = self.get_traits_containing_item(const_name);
-                            self.trait_map.insert(pattern.id, traits);
-                            visit::walk_pat(self, pattern);
-                            return true;
-                        }
-                        ResolveAttempt(resolution) => resolution,
-                    };
-                    if let Some(path_res) = resolution {
-                        match path_res.base_def {
-                            // All `<T as Trait>::CONST` should end up here, and
-                            // have the trait already selected.
-                            Def::AssociatedConst(..) => {
-                                self.record_def(pattern.id, path_res);
+        self.record_def(pat_id, resolution);
+    }
+
+    fn resolve_pattern(&mut self,
+                       pat: &Pat,
+                       pat_src: PatternSource,
+                       // Maps idents to the node ID for the
+                       // outermost pattern that binds them.
+                       bindings: &mut HashMap<Name, NodeId>) {
+        // Visit all direct subpatterns of this pattern.
+        let outer_pat_id = pat.id;
+        pat.walk(&mut |pat| {
+            match pat.node {
+                PatKind::Ident(bmode, ref ident, ref opt_pat) => {
+                    // First try to resolve the identifier as some existing
+                    // entity, then fall back to a fresh binding.
+                    let local_def = self.resolve_identifier(ident.node, ValueNS, true);
+                    let resolution = if let Some(LocalDef { def, .. }) = local_def {
+                        let always_binding = !pat_src.is_refutable() || opt_pat.is_some() ||
+                                             bmode != BindingMode::ByValue(Mutability::Immutable);
+                        match def {
+                            Def::Struct(..) | Def::Variant(..) |
+                            Def::Const(..) | Def::AssociatedConst(..) if !always_binding => {
+                                // A constant, unit variant, etc pattern.
+                                PathResolution::new(def)
                             }
-                            _ => {
+                            Def::Struct(..) | Def::Variant(..) |
+                            Def::Const(..) | Def::AssociatedConst(..) | Def::Static(..) => {
+                                // A fresh binding that shadows something unacceptable.
+                                let kind_name = PathResolution::new(def).kind_name();
                                 resolve_error(
                                     self,
-                                    path.span,
-                                    ResolutionError::NotAnAssociatedConst(
-                                        &path.segments.last().unwrap().identifier.name.as_str()
-                                    )
+                                    ident.span,
+                                    ResolutionError::BindingShadowsSomethingUnacceptable(
+                                        pat_src.descr(), kind_name, ident.node.name)
                                 );
-                                self.record_def(pattern.id, err_path_resolution());
+                                err_path_resolution()
+                            }
+                            Def::Local(..) | Def::Upvar(..) | Def::Fn(..) | Def::Err => {
+                                // These entities are explicitly allowed
+                                // to be shadowed by fresh bindings.
+                                self.fresh_binding(ident, pat.id, outer_pat_id,
+                                                   pat_src, bindings)
+                            }
+                            def => {
+                                span_bug!(ident.span, "unexpected definition for an \
+                                                       identifier in pattern {:?}", def);
                             }
                         }
                     } else {
-                        resolve_error(self,
-                                      path.span,
-                                      ResolutionError::UnresolvedAssociatedConst(&path.segments
-                                                                                      .last()
-                                                                                      .unwrap()
-                                                                                      .identifier
-                                                                                      .name
-                                                                                      .as_str()));
-                        self.record_def(pattern.id, err_path_resolution());
-                    }
-                    visit::walk_pat(self, pattern);
+                        // Fall back to a fresh binding.
+                        self.fresh_binding(ident, pat.id, outer_pat_id, pat_src, bindings)
+                    };
+
+                    self.record_def(pat.id, resolution);
                 }
 
-                PatKind::Struct(ref path, _, _) => {
-                    match self.resolve_path(pat_id, path, 0, TypeNS) {
-                        Ok(definition) => {
-                            self.record_def(pattern.id, definition);
+                PatKind::TupleStruct(ref path, _, _) => {
+                    self.resolve_pattern_path(pat.id, None, path, ValueNS, |def| {
+                        match def {
+                            Def::Struct(..) | Def::Variant(..) | Def::Err => true,
+                            _ => false,
                         }
-                        Err(true) => self.record_def(pattern.id, err_path_resolution()),
-                        Err(false) => {
-                            resolve_error(
-                                self,
-                                path.span,
-                                ResolutionError::DoesNotNameAStruct(
-                                    &path_names_to_string(path, 0))
-                            );
-                            self.record_def(pattern.id, err_path_resolution());
-                        }
-                    }
-                    visit::walk_path(self, path);
+                    }, "variant or struct");
                 }
 
-                PatKind::Lit(_) | PatKind::Range(..) => {
-                    visit::walk_pat(self, pattern);
+                PatKind::Path(ref qself, ref path) => {
+                    self.resolve_pattern_path(pat.id, qself.as_ref(), path, ValueNS, |def| {
+                        match def {
+                            Def::Struct(..) | Def::Variant(..) |
+                            Def::Const(..) | Def::AssociatedConst(..) | Def::Err => true,
+                            _ => false,
+                        }
+                    }, "variant, struct or constant");
                 }
 
-                _ => {
-                    // Nothing to do.
+                PatKind::Struct(ref path, _, _) => {
+                    self.resolve_pattern_path(pat.id, None, path, TypeNS, |def| {
+                        match def {
+                            Def::Struct(..) | Def::Variant(..) |
+                            Def::TyAlias(..) | Def::AssociatedTy(..) | Def::Err => true,
+                            _ => false,
+                        }
+                    }, "variant, struct or type alias");
                 }
+
+                _ => {}
             }
             true
         });
-    }
-
-    fn resolve_bare_identifier_pattern(&mut self, ident: ast::Ident, span: Span)
-                                       -> BareIdentifierPatternResolution {
-        let binding = match self.resolve_ident_in_lexical_scope(ident, ValueNS, true) {
-            Some(LexicalScopeBinding::Item(binding)) => binding,
-            _ => return BareIdentifierPatternUnresolved,
-        };
-        let def = binding.def().unwrap();
 
-        match def {
-            Def::Variant(..) | Def::Struct(..) => FoundStructOrEnumVariant(def),
-            Def::Const(..) | Def::AssociatedConst(..) => FoundConst(def, ident.name),
-            Def::Static(..) => {
-                let error = ResolutionError::StaticVariableReference(binding);
-                resolve_error(self, span, error);
-                BareIdentifierPatternUnresolved
-            }
-            _ => BareIdentifierPatternUnresolved,
-        }
+        visit::walk_pat(self, pat);
     }
 
     /// Handles paths that may refer to associated items
     fn resolve_possibly_assoc_item(&mut self,
                                    id: NodeId,
-                                   maybe_qself: Option<&ast::QSelf>,
+                                   maybe_qself: Option<&QSelf>,
                                    path: &Path,
                                    namespace: Namespace)
-                                   -> AssocItemResolveResult {
+                                   -> Option<PathResolution> {
         let max_assoc_types;
 
         match maybe_qself {
             Some(qself) => {
                 if qself.position == 0 {
-                    return TypecheckRequired;
+                    // FIXME: Create some fake resolution that can't possibly be a type.
+                    return Some(PathResolution {
+                        base_def: Def::Mod(self.definitions.local_def_id(ast::CRATE_NODE_ID)),
+                        depth: path.segments.len(),
+                    });
                 }
                 max_assoc_types = path.segments.len() - qself.position;
                 // Make sure the trait is valid.
@@ -2548,14 +2400,15 @@ impl<'a> Resolver<'a> {
                 break;
             }
             self.with_no_errors(|this| {
-                resolution = this.resolve_path(id, path, depth, TypeNS).ok();
+                let partial_resolution = this.resolve_path(id, path, depth, TypeNS).ok();
+                if let Some(Def::Mod(..)) = partial_resolution.map(|r| r.base_def) {
+                    // Modules cannot have associated items
+                } else {
+                    resolution = partial_resolution;
+                }
             });
         }
-        if let Some(Def::Mod(_)) = resolution.map(|r| r.base_def) {
-            // A module is not a valid type or value.
-            resolution = None;
-        }
-        ResolveAttempt(resolution)
+        resolution
     }
 
     /// Skips `path_depth` trailing segments, which is also reflected in the
@@ -2567,7 +2420,7 @@ impl<'a> Resolver<'a> {
         let span = path.span;
         let segments = &path.segments[..path.segments.len() - path_depth];
 
-        let mk_res = |def| PathResolution::new(def, path_depth);
+        let mk_res = |def| PathResolution { base_def: def, depth: path_depth };
 
         if path.global {
             let binding = self.resolve_crate_relative_path(span, segments, namespace);
@@ -2677,7 +2530,7 @@ impl<'a> Resolver<'a> {
                             def = Def::Upvar(node_def_id, node_id, depth, function_id);
                             seen.insert(node_id, depth);
                         }
-                        ItemRibKind | MethodRibKind => {
+                        ItemRibKind | MethodRibKind(_) => {
                             // This was an attempt to access an upvar inside a
                             // named function item. This is not allowed, so we
                             // report an error.
@@ -2699,7 +2552,7 @@ impl<'a> Resolver<'a> {
             Def::TyParam(..) | Def::SelfTy(..) => {
                 for rib in ribs {
                     match rib.kind {
-                        NormalRibKind | MethodRibKind | ClosureRibKind(..) |
+                        NormalRibKind | MethodRibKind(_) | ClosureRibKind(..) |
                         ModuleRibKind(..) => {
                             // Nothing to do. Continue.
                         }
@@ -2835,19 +2688,18 @@ impl<'a> Resolver<'a> {
 
         if let Some(node_id) = self.current_self_type.as_ref().and_then(extract_node_id) {
             // Look for a field with the same name in the current self_type.
-            match self.def_map.get(&node_id).map(|d| d.full_def()) {
-                Some(Def::Enum(did)) |
-                Some(Def::TyAlias(did)) |
-                Some(Def::Struct(did)) |
-                Some(Def::Variant(_, did)) => match self.structs.get(&did) {
-                    None => {}
-                    Some(fields) => {
-                        if fields.iter().any(|&field_name| name == field_name) {
-                            return Field;
+            if let Some(resolution) = self.def_map.get(&node_id) {
+                match resolution.base_def {
+                    Def::Enum(did) | Def::TyAlias(did) |
+                    Def::Struct(did) | Def::Variant(_, did) if resolution.depth == 0 => {
+                        if let Some(fields) = self.structs.get(&did) {
+                            if fields.iter().any(|&field_name| name == field_name) {
+                                return Field;
+                            }
                         }
                     }
-                },
-                _ => {} // Self type didn't resolve properly
+                    _ => {}
+                }
             }
         }
 
@@ -2904,24 +2756,10 @@ impl<'a> Resolver<'a> {
         // Next, resolve the node.
         match expr.node {
             ExprKind::Path(ref maybe_qself, ref path) => {
-                let resolution = match self.resolve_possibly_assoc_item(expr.id,
-                                                                        maybe_qself.as_ref(),
-                                                                        path,
-                                                                        ValueNS) {
-                    // `<T>::a::b::c` is resolved by typeck alone.
-                    TypecheckRequired => {
-                        let method_name = path.segments.last().unwrap().identifier.name;
-                        let traits = self.get_traits_containing_item(method_name);
-                        self.trait_map.insert(expr.id, traits);
-                        visit::walk_expr(self, expr);
-                        return;
-                    }
-                    ResolveAttempt(resolution) => resolution,
-                };
-
                 // This is a local path in the value namespace. Walk through
                 // scopes looking for it.
-                if let Some(path_res) = resolution {
+                if let Some(path_res) = self.resolve_possibly_assoc_item(expr.id,
+                                                            maybe_qself.as_ref(), path, ValueNS) {
                     // Check if struct variant
                     let is_struct_variant = if let Def::Variant(_, variant_id) = path_res.base_def {
                         self.structs.contains_key(&variant_id)
@@ -2992,9 +2830,13 @@ impl<'a> Resolver<'a> {
                             // `resolve_path` already reported the error
                         } else {
                             let mut method_scope = false;
+                            let mut is_static = false;
                             self.value_ribs.iter().rev().all(|rib| {
                                 method_scope = match rib.kind {
-                                    MethodRibKind => true,
+                                    MethodRibKind(is_static_) => {
+                                        is_static = is_static_;
+                                        true
+                                    }
                                     ItemRibKind | ConstantItemRibKind => false,
                                     _ => return true, // Keep advancing
                                 };
@@ -3008,25 +2850,33 @@ impl<'a> Resolver<'a> {
                                               ResolutionError::SelfNotAvailableInStaticMethod);
                             } else {
                                 let last_name = path.segments.last().unwrap().identifier.name;
-                                let mut msg = match self.find_fallback_in_self_type(last_name) {
+                                let (mut msg, is_field) =
+                                    match self.find_fallback_in_self_type(last_name) {
                                     NoSuggestion => {
                                         // limit search to 5 to reduce the number
                                         // of stupid suggestions
-                                        match self.find_best_match(&path_name) {
+                                        (match self.find_best_match(&path_name) {
                                             SuggestionType::Macro(s) => {
                                                 format!("the macro `{}`", s)
                                             }
                                             SuggestionType::Function(s) => format!("`{}`", s),
                                             SuggestionType::NotFound => "".to_string(),
-                                        }
+                                        }, false)
+                                    }
+                                    Field => {
+                                        (if is_static && method_scope {
+                                            "".to_string()
+                                        } else {
+                                            format!("`self.{}`", path_name)
+                                        }, true)
                                     }
-                                    Field => format!("`self.{}`", path_name),
-                                    TraitItem => format!("to call `self.{}`", path_name),
+                                    TraitItem => (format!("to call `self.{}`", path_name), false),
                                     TraitMethod(path_str) =>
-                                        format!("to call `{}::{}`", path_str, path_name),
+                                        (format!("to call `{}::{}`", path_str, path_name), false),
                                 };
 
                                 let mut context =  UnresolvedNameContext::Other;
+                                let mut def = Def::Err;
                                 if !msg.is_empty() {
                                     msg = format!(". Did you mean {}?", msg);
                                 } else {
@@ -3039,7 +2889,10 @@ impl<'a> Resolver<'a> {
                                     match self.resolve_module_path(&name_path[..],
                                                                    UseLexicalScope,
                                                                    expr.span) {
-                                        Success(_) => {
+                                        Success(e) => {
+                                            if let Some(def_type) = e.def {
+                                                def = def_type;
+                                            }
                                             context = UnresolvedNameContext::PathIsMod(parent);
                                         },
                                         _ => {},
@@ -3048,8 +2901,14 @@ impl<'a> Resolver<'a> {
 
                                 resolve_error(self,
                                               expr.span,
-                                              ResolutionError::UnresolvedName(
-                                                  &path_name, &msg, context));
+                                              ResolutionError::UnresolvedName {
+                                                  path: &path_name,
+                                                  message: &msg,
+                                                  context: context,
+                                                  is_static_method: method_scope && is_static,
+                                                  is_field: is_field,
+                                                  def: def,
+                                              });
                             }
                         }
                     }
@@ -3086,14 +2945,14 @@ impl<'a> Resolver<'a> {
 
                     {
                         let rib = this.label_ribs.last_mut().unwrap();
-                        rib.bindings.insert(mtwt::resolve(label), def);
+                        rib.bindings.insert(mtwt::resolve(label.node), def);
                     }
 
                     visit::walk_expr(this, expr);
                 })
             }
 
-            ExprKind::Break(Some(label)) | ExprKind::Again(Some(label)) => {
+            ExprKind::Break(Some(label)) | ExprKind::Continue(Some(label)) => {
                 match self.search_label(mtwt::resolve(label.node)) {
                     None => {
                         self.record_def(expr.id, err_path_resolution());
@@ -3103,11 +2962,7 @@ impl<'a> Resolver<'a> {
                     }
                     Some(def @ Def::Label(_)) => {
                         // Since this def is a label, it is never read.
-                        self.record_def(expr.id,
-                                        PathResolution {
-                                            base_def: def,
-                                            depth: 0,
-                                        })
+                        self.record_def(expr.id, PathResolution::new(def))
                     }
                     Some(_) => {
                         span_bug!(expr.span, "label wasn't mapped to a label def!")
@@ -3119,7 +2974,7 @@ impl<'a> Resolver<'a> {
                 self.visit_expr(subexpression);
 
                 self.value_ribs.push(Rib::new(NormalRibKind));
-                self.resolve_pattern(pattern, RefutableMode, &mut HashMap::new());
+                self.resolve_pattern(pattern, PatternSource::IfLet, &mut HashMap::new());
                 self.visit_block(if_block);
                 self.value_ribs.pop();
 
@@ -3129,9 +2984,9 @@ impl<'a> Resolver<'a> {
             ExprKind::WhileLet(ref pattern, ref subexpression, ref block, label) => {
                 self.visit_expr(subexpression);
                 self.value_ribs.push(Rib::new(NormalRibKind));
-                self.resolve_pattern(pattern, RefutableMode, &mut HashMap::new());
+                self.resolve_pattern(pattern, PatternSource::WhileLet, &mut HashMap::new());
 
-                self.resolve_labeled_block(label, expr.id, block);
+                self.resolve_labeled_block(label.map(|l| l.node), expr.id, block);
 
                 self.value_ribs.pop();
             }
@@ -3139,9 +2994,9 @@ impl<'a> Resolver<'a> {
             ExprKind::ForLoop(ref pattern, ref subexpression, ref block, label) => {
                 self.visit_expr(subexpression);
                 self.value_ribs.push(Rib::new(NormalRibKind));
-                self.resolve_pattern(pattern, LocalIrrefutableMode, &mut HashMap::new());
+                self.resolve_pattern(pattern, PatternSource::For, &mut HashMap::new());
 
-                self.resolve_labeled_block(label, expr.id, block);
+                self.resolve_labeled_block(label.map(|l| l.node), expr.id, block);
 
                 self.value_ribs.pop();
             }
@@ -3215,7 +3070,7 @@ impl<'a> Resolver<'a> {
         let mut search_module = self.current_module;
         loop {
             // Look for trait children.
-            let mut search_in_module = |module: Module<'a>| {
+            let mut search_in_module = |this: &mut Self, module: Module<'a>| {
                 let mut traits = module.traits.borrow_mut();
                 if traits.is_none() {
                     let mut collected_traits = Vec::new();
@@ -3230,23 +3085,25 @@ impl<'a> Resolver<'a> {
 
                 for &(trait_name, binding) in traits.as_ref().unwrap().iter() {
                     let trait_def_id = binding.def().unwrap().def_id();
-                    if self.trait_item_map.contains_key(&(name, trait_def_id)) {
+                    if this.trait_item_map.contains_key(&(name, trait_def_id)) {
                         let mut import_id = None;
                         if let NameBindingKind::Import { directive, .. } = binding.kind {
                             let id = directive.id;
-                            self.maybe_unused_trait_imports.insert(id);
+                            this.maybe_unused_trait_imports.insert(id);
                             import_id = Some(id);
                         }
                         add_trait_info(&mut found_traits, trait_def_id, import_id, name);
-                        self.record_use(trait_name, binding);
+                        this.record_use(trait_name, binding);
                     }
                 }
             };
-            search_in_module(search_module);
+            search_in_module(self, search_module);
 
             match search_module.parent_link {
                 NoParentLink | ModuleParentLink(..) => {
-                    search_module.prelude.borrow().map(search_in_module);
+                    if !search_module.no_implicit_prelude.get() {
+                        self.prelude.map(|prelude| search_in_module(self, prelude));
+                    }
                     break;
                 }
                 BlockParentLink(parent_module, _) => {
@@ -3338,7 +3195,9 @@ impl<'a> Resolver<'a> {
                     if !in_module_is_extern || name_binding.vis == ty::Visibility::Public {
                         // add the module to the lookup
                         let is_extern = in_module_is_extern || name_binding.is_extern_crate();
-                        worklist.push((module, path_segments, is_extern));
+                        if !worklist.iter().any(|&(m, _, _)| m.def == module.def) {
+                            worklist.push((module, path_segments, is_extern));
+                        }
                     }
                 }
             })
@@ -3357,20 +3216,6 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    fn enforce_default_binding_mode(&mut self,
-                                    pat: &Pat,
-                                    pat_binding_mode: BindingMode,
-                                    descr: &str) {
-        match pat_binding_mode {
-            BindingMode::ByValue(_) => {}
-            BindingMode::ByRef(..) => {
-                resolve_error(self,
-                              pat.span,
-                              ResolutionError::CannotUseRefBindingModeWith(descr));
-            }
-        }
-    }
-
     fn resolve_visibility(&mut self, vis: &ast::Visibility) -> ty::Visibility {
         let (path, id) = match *vis {
             ast::Visibility::Public => return ty::Visibility::Public,
@@ -3386,11 +3231,11 @@ impl<'a> Resolver<'a> {
         };
 
         let segments: Vec<_> = path.segments.iter().map(|seg| seg.identifier.name).collect();
+        let mut path_resolution = err_path_resolution();
         let vis = match self.resolve_module_path(&segments, DontUseLexicalScope, path.span) {
             Success(module) => {
                 let def = module.def.unwrap();
-                let path_resolution = PathResolution { base_def: def, depth: 0 };
-                self.def_map.insert(id, path_resolution);
+                path_resolution = PathResolution::new(def);
                 ty::Visibility::Restricted(self.definitions.as_local_node_id(def.def_id()).unwrap())
             }
             Failed(Some((span, msg))) => {
@@ -3402,6 +3247,7 @@ impl<'a> Resolver<'a> {
                 ty::Visibility::Public
             }
         };
+        self.def_map.insert(id, path_resolution);
         if !self.is_accessible(vis) {
             let msg = format!("visibilities can only be restricted to ancestor modules");
             self.session.span_err(path.span, &msg);
@@ -3490,7 +3336,7 @@ impl<'a> Resolver<'a> {
             },
         };
 
-        if old_binding.span != codemap::DUMMY_SP {
+        if old_binding.span != syntax_pos::DUMMY_SP {
             err.span_label(old_binding.span, &format!("previous {} of `{}` here", noun, name));
         }
         err.emit();
@@ -3602,47 +3448,13 @@ fn module_to_string(module: Module) -> String {
 }
 
 fn err_path_resolution() -> PathResolution {
-    PathResolution {
-        base_def: Def::Err,
-        depth: 0,
-    }
+    PathResolution::new(Def::Err)
 }
 
-
 #[derive(PartialEq,Copy, Clone)]
 pub enum MakeGlobMap {
     Yes,
     No,
 }
 
-/// Entry point to crate resolution.
-pub fn resolve_crate<'a, 'b>(resolver: &'b mut Resolver<'a>, krate: &'b Crate) {
-    // Currently, we ignore the name resolution data structures for
-    // the purposes of dependency tracking. Instead we will run name
-    // resolution and include its output in the hash of each item,
-    // much like we do for macro expansion. In other words, the hash
-    // reflects not just its contents but the results of name
-    // resolution on those contents. Hopefully we'll push this back at
-    // some point.
-    let _ignore = resolver.session.dep_graph.in_ignore();
-
-    resolver.build_reduced_graph(krate);
-    resolve_imports::resolve_imports(resolver);
-    resolver.resolve_crate(krate);
-
-    check_unused::check_crate(resolver, krate);
-    resolver.report_privacy_errors();
-}
-
-pub fn with_resolver<'a, T, F>(session: &'a Session,
-                               definitions: &'a mut Definitions,
-                               make_glob_map: MakeGlobMap,
-                               f: F) -> T
-    where F: for<'b> FnOnce(Resolver<'b>) -> T,
-{
-    let arenas = Resolver::arenas();
-    let resolver = Resolver::new(session, definitions, make_glob_map, &arenas);
-    f(resolver)
-}
-
 __build_diagnostic_array! { librustc_resolve, DIAGNOSTICS }
index 9bd16117f9a8e25ccbcb14abc123b7bc383d89c2..16a59fbb800024afcd24c43de94a8b15307da684 100644 (file)
@@ -25,11 +25,17 @@ use rustc::lint;
 use rustc::hir::def::*;
 
 use syntax::ast::{NodeId, Name};
-use syntax::codemap::{Span, DUMMY_SP};
 use syntax::util::lev_distance::find_best_match_for_name;
+use syntax_pos::{Span, DUMMY_SP};
 
 use std::cell::{Cell, RefCell};
 
+impl<'a> Resolver<'a> {
+    pub fn resolve_imports(&mut self) {
+        ImportResolver { resolver: self }.resolve_imports();
+    }
+}
+
 /// Contains data for specific types of import directives.
 #[derive(Clone, Debug)]
 pub enum ImportDirectiveSubclass {
@@ -257,15 +263,6 @@ impl<'a> ::ModuleS<'a> {
         Failed(None)
     }
 
-    // Invariant: this may not be called until import resolution is complete.
-    pub fn resolve_name_in_lexical_scope(&self, name: Name, ns: Namespace)
-                                         -> Option<&'a NameBinding<'a>> {
-        self.resolution(name, ns).borrow().binding
-            .or_else(|| self.prelude.borrow().and_then(|prelude| {
-                prelude.resolve_name(name, ns, false).success()
-            }))
-    }
-
     // Define the name or return the existing binding if there is a collision.
     pub fn try_define_child(&self, name: Name, ns: Namespace, binding: NameBinding<'a>)
                             -> Result<(), &'a NameBinding<'a>> {
@@ -607,7 +604,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
             Some(def) => def,
             None => value_result.success().and_then(NameBinding::def).unwrap(),
         };
-        let path_resolution = PathResolution { base_def: def, depth: 0 };
+        let path_resolution = PathResolution::new(def);
         self.resolver.def_map.insert(directive.id, path_resolution);
 
         debug!("(resolving single import) successfully resolved import");
@@ -633,7 +630,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
         self.resolver.populate_module_if_necessary(target_module);
 
         if let GlobImport { is_prelude: true } = directive.subclass {
-            *module_.prelude.borrow_mut() = Some(target_module);
+            self.resolver.prelude = Some(target_module);
             return Success(());
         }
 
@@ -653,7 +650,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
 
         // Record the destination of this import
         if let Some(did) = target_module.def_id() {
-            let resolution = PathResolution { base_def: Def::Mod(did), depth: 0 };
+            let resolution = PathResolution::new(Def::Mod(did));
             self.resolver.def_map.insert(directive.id, resolution);
         }
 
@@ -731,8 +728,3 @@ fn import_directive_subclass_to_string(subclass: &ImportDirectiveSubclass) -> St
         GlobImport { .. } => "*".to_string(),
     }
 }
-
-pub fn resolve_imports(resolver: &mut Resolver) {
-    let mut import_resolver = ImportResolver { resolver: resolver };
-    import_resolver.resolve_imports();
-}
index c786b4d711c39157329ad96d61b7b6f1fb021414..3d66e5a3007874cecbeaeeff98ddd43e33b481c7 100644 (file)
@@ -13,3 +13,4 @@ log = { path = "../liblog" }
 rustc = { path = "../librustc" }
 syntax = { path = "../libsyntax" }
 serialize = { path = "../libserialize" }
+syntax_pos = { path = "../libsyntax_pos" }
\ No newline at end of file
index 2275a1c5c65f2720714d9a5861602d7bbdfc4bc3..493f7669337fe17a26cce8851220b2d313a5dc9f 100644 (file)
@@ -15,7 +15,7 @@
 
 use rustc::hir::def_id::DefId;
 use syntax::ast::{CrateNum, NodeId};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 pub struct CrateData {
     pub name: String,
@@ -102,6 +102,8 @@ pub struct EnumData {
     pub qualname: String,
     pub span: Span,
     pub scope: NodeId,
+    pub variants: Vec<NodeId>,
+
 }
 
 /// Data for extern crates.
@@ -212,6 +214,7 @@ pub struct MethodData {
     pub span: Span,
     pub scope: NodeId,
     pub value: String,
+    pub decl_id: Option<DefId>,
 }
 
 /// Data for modules.
@@ -223,6 +226,7 @@ pub struct ModData {
     pub span: Span,
     pub scope: NodeId,
     pub filename: String,
+    pub items: Vec<NodeId>,
 }
 
 /// Data for a reference to a module.
@@ -242,7 +246,8 @@ pub struct StructData {
     pub ctor_id: NodeId,
     pub qualname: String,
     pub scope: NodeId,
-    pub value: String
+    pub value: String,
+    pub fields: Vec<NodeId>,
 }
 
 #[derive(Debug, RustcEncodable)]
@@ -263,7 +268,8 @@ pub struct TraitData {
     pub name: String,
     pub qualname: String,
     pub scope: NodeId,
-    pub value: String
+    pub value: String,
+    pub items: Vec<NodeId>,
 }
 
 #[derive(Debug, RustcEncodable)]
@@ -317,6 +323,7 @@ pub struct UseGlobData {
 #[derive(Debug, RustcEncodable)]
 pub struct VariableData {
     pub id: NodeId,
+    pub kind: VariableKind,
     pub name: String,
     pub qualname: String,
     pub span: Span,
@@ -325,6 +332,14 @@ pub struct VariableData {
     pub type_value: String,
 }
 
+#[derive(Debug, RustcEncodable)]
+pub enum VariableKind {
+    Static,
+    Const,
+    Local,
+    Field,
+}
+
 /// Data for the use of some item (e.g., the use of a local variable, which
 /// will refer to that variables declaration (by ref_id)).
 #[derive(Debug, RustcEncodable)]
index 4d79ddfe8cbe30773d94043ab9975e8299202c43..c1960eeee46b8fb7eccede5f0fda8f5f911e96b4 100644 (file)
 use rustc::hir::def::Def;
 use rustc::hir::def_id::DefId;
 use rustc::session::Session;
-use rustc::ty::{self, TyCtxt};
+use rustc::ty::{self, TyCtxt, ImplOrTraitItem, ImplOrTraitItemContainer};
 
 use std::collections::HashSet;
 use std::hash::*;
 
 use syntax::ast::{self, NodeId, PatKind};
-use syntax::codemap::*;
 use syntax::parse::token::{self, keywords};
 use syntax::visit::{self, Visitor};
 use syntax::print::pprust::{path_to_string, ty_to_string, bounds_to_string, generics_to_string};
 use syntax::ptr::P;
+use syntax::codemap::Spanned;
+use syntax_pos::*;
 
 use super::{escape, generated_code, SaveContext, PathCollector};
 use super::data::*;
@@ -269,14 +270,10 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
 
     // looks up anything, not just a type
     fn lookup_type_ref(&self, ref_id: NodeId) -> Option<DefId> {
-        if !self.tcx.def_map.borrow().contains_key(&ref_id) {
-            bug!("def_map has no key for {} in lookup_type_ref", ref_id);
-        }
-        let def = self.tcx.def_map.borrow().get(&ref_id).unwrap().full_def();
-        match def {
+        match self.tcx.expect_def(ref_id) {
             Def::PrimTy(..) => None,
             Def::SelfTy(..) => None,
-            _ => Some(def.def_id()),
+            def => Some(def.def_id()),
         }
     }
 
@@ -290,13 +287,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
             return;
         }
 
-        let def_map = self.tcx.def_map.borrow();
-        if !def_map.contains_key(&ref_id) {
-            span_bug!(span,
-                      "def_map has no key for {} in lookup_def_kind",
-                      ref_id);
-        }
-        let def = def_map.get(&ref_id).unwrap().full_def();
+        let def = self.tcx.expect_def(ref_id);
         match def {
             Def::Mod(_) |
             Def::ForeignMod(_) => {
@@ -366,6 +357,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 if !self.span.filter_generated(sub_span, p.span) {
                     self.dumper.variable(VariableData {
                         id: id,
+                        kind: VariableKind::Local,
                         span: sub_span.expect("No span found for variable"),
                         name: path_to_string(p),
                         qualname: format!("{}::{}", qualname, path_to_string(p)),
@@ -390,24 +382,42 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
 
             let sig_str = ::make_signature(&sig.decl, &sig.generics);
             if body.is_some() {
-                if !self.span.filter_generated(Some(method_data.span), span) {
-                    let mut data = method_data.clone();
-                    data.value = sig_str;
-                    self.dumper.function(data.lower(self.tcx));
-                }
                 self.process_formals(&sig.decl.inputs, &method_data.qualname);
-            } else {
-                if !self.span.filter_generated(Some(method_data.span), span) {
-                    self.dumper.method(MethodData {
-                        id: method_data.id,
-                        name: method_data.name,
-                        span: method_data.span,
-                        scope: method_data.scope,
-                        qualname: method_data.qualname.clone(),
-                        value: sig_str,
-                    }.lower(self.tcx));
-                }
             }
+
+            // If the method is defined in an impl, then try and find the corresponding
+            // method decl in a trait, and if there is one, make a decl_id for it. This
+            // requires looking up the impl, then the trait, then searching for a method
+            // with the right name.
+            if !self.span.filter_generated(Some(method_data.span), span) {
+                let container =
+                    self.tcx.impl_or_trait_item(self.tcx.map.local_def_id(id)).container();
+                let decl_id = if let ImplOrTraitItemContainer::ImplContainer(id) = container {
+                    self.tcx.trait_id_of_impl(id).and_then(|id| {
+                        for item in &**self.tcx.trait_items(id) {
+                            if let &ImplOrTraitItem::MethodTraitItem(ref m) = item {
+                                if m.name == name {
+                                    return Some(m.def_id);
+                                }
+                            }
+                        }
+                        None
+                    })
+                } else {
+                    None
+                };
+
+                self.dumper.method(MethodData {
+                    id: method_data.id,
+                    name: method_data.name,
+                    span: method_data.span,
+                    scope: method_data.scope,
+                    qualname: method_data.qualname.clone(),
+                    value: sig_str,
+                    decl_id: decl_id,
+                }.lower(self.tcx));
+            }
+
             self.process_generic_params(&sig.generics, span, &method_data.qualname, id);
         }
 
@@ -529,6 +539,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         if !self.span.filter_generated(sub_span, span) {
             self.dumper.variable(VariableData {
                 span: sub_span.expect("No span found for variable"),
+                kind: VariableKind::Const,
                 id: id,
                 name: name.to_string(),
                 qualname: qualname,
@@ -552,17 +563,18 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         let qualname = format!("::{}", self.tcx.node_path_str(item.id));
 
         let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Struct);
-        let val = if let ast::ItemKind::Struct(ast::VariantData::Struct(ref fields, _), _) =
-                    item.node {
+        let (val, fields) =
+            if let ast::ItemKind::Struct(ast::VariantData::Struct(ref fields, _), _) = item.node
+        {
             let fields_str = fields.iter()
                                    .enumerate()
                                    .map(|(i, f)| f.ident.map(|i| i.to_string())
                                                   .unwrap_or(i.to_string()))
                                    .collect::<Vec<_>>()
                                    .join(", ");
-            format!("{} {{ {} }}", name, fields_str)
+            (format!("{} {{ {} }}", name, fields_str), fields.iter().map(|f| f.id).collect())
         } else {
-            String::new()
+            (String::new(), vec![])
         };
 
         if !self.span.filter_generated(sub_span, item.span) {
@@ -573,7 +585,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 ctor_id: def.id(),
                 qualname: qualname.clone(),
                 scope: self.cur_scope,
-                value: val
+                value: val,
+                fields: fields,
             }.lower(self.tcx));
         }
 
@@ -728,7 +741,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
                 name: name,
                 qualname: qualname.clone(),
                 scope: self.cur_scope,
-                value: val
+                value: val,
+                items: methods.iter().map(|i| i.id).collect(),
             }.lower(self.tcx));
         }
 
@@ -853,9 +867,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
         }
 
         // Modules or types in the path prefix.
-        let def_map = self.tcx.def_map.borrow();
-        let def = def_map.get(&id).unwrap().full_def();
-        match def {
+        match self.tcx.expect_def(id) {
             Def::Method(did) => {
                 let ti = self.tcx.impl_or_trait_item(did);
                 if let ty::MethodTraitItem(m) = ti {
@@ -924,8 +936,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
             PatKind::Struct(ref path, ref fields, _) => {
                 visit::walk_path(self, path);
                 let adt = self.tcx.node_id_to_type(p.id).ty_adt_def().unwrap();
-                let def = self.tcx.def_map.borrow()[&p.id].full_def();
-                let variant = adt.variant_of_def(def);
+                let variant = adt.variant_of_def(self.tcx.expect_def(p.id));
 
                 for &Spanned { node: ref field, span } in fields {
                     let sub_span = self.span.span_for_first_ident(span);
@@ -971,6 +982,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
             if !self.span.filter_generated(sub_span, p.span) {
                 self.dumper.variable(VariableData {
                     span: sub_span.expect("No span found for variable"),
+                    kind: VariableKind::Local,
                     id: id,
                     name: path_to_string(p),
                     qualname: format!("{}${}", path_to_string(p), id),
@@ -1026,7 +1038,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
     }
 }
 
-impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx, 'll, D> {
+impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D> {
     fn visit_item(&mut self, item: &ast::Item) {
         use syntax::ast::ItemKind::*;
         self.process_macro_use(item.span, item.id);
@@ -1204,7 +1216,8 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                                     trait_item.span);
             }
             ast::TraitItemKind::Const(_, None) |
-            ast::TraitItemKind::Type(..) => {}
+            ast::TraitItemKind::Type(..) |
+            ast::TraitItemKind::Macro(_) => {}
         }
     }
 
@@ -1269,7 +1282,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
             ast::ExprKind::Struct(ref path, ref fields, ref base) => {
                 let hir_expr = self.save_ctxt.tcx.map.expect_expr(ex.id);
                 let adt = self.tcx.expr_ty(&hir_expr).ty_adt_def().unwrap();
-                let def = self.tcx.resolve_expr(&hir_expr);
+                let def = self.tcx.expect_def(hir_expr.id);
                 self.process_struct_lit(ex, path, fields, adt.variant_of_def(def), base)
             }
             ast::ExprKind::MethodCall(_, _, ref args) => self.process_method_call(ex, args),
@@ -1366,12 +1379,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
 
         // process collected paths
         for &(id, ref p, immut, ref_kind) in &collector.collected_paths {
-            let def_map = self.tcx.def_map.borrow();
-            if !def_map.contains_key(&id) {
-                span_bug!(p.span, "def_map has no key for {} in visit_arm", id);
-            }
-            let def = def_map.get(&id).unwrap().full_def();
-            match def {
+            match self.tcx.expect_def(id) {
                 Def::Local(_, id) => {
                     let value = if immut == ast::Mutability::Immutable {
                         self.span.snippet(p.span).to_string()
@@ -1384,6 +1392,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                     if !self.span.filter_generated(Some(p.span), p.span) {
                         self.dumper.variable(VariableData {
                             span: p.span,
+                            kind: VariableKind::Local,
                             id: id,
                             name: path_to_string(p),
                             qualname: format!("{}${}", path_to_string(p), id),
@@ -1401,8 +1410,8 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
                 Def::Static(_, _) |
                 Def::Const(..) |
                 Def::AssociatedConst(..) => {}
-                _ => error!("unexpected definition kind when processing collected paths: {:?}",
-                            def),
+                def => error!("unexpected definition kind when processing collected paths: {:?}",
+                              def),
             }
         }
 
@@ -1414,8 +1423,7 @@ impl<'v, 'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor<'v> for DumpVisitor<'l, 'tcx,
     }
 
     fn visit_stmt(&mut self, s: &ast::Stmt) {
-        let id = s.node.id();
-        self.process_macro_use(s.span, id.unwrap());
+        self.process_macro_use(s.span, s.id);
         visit::walk_stmt(self, s)
     }
 
index dc51c41f296f20dd00b6eee37b684199ba072735..65e4f7e869b0d01f3157df9b1b1ac71ac3dc8633 100644 (file)
@@ -12,9 +12,10 @@ use rustc::hir::def_id::{DefId, DefIndex};
 use rustc::hir::map::Map;
 use rustc::ty::TyCtxt;
 use syntax::ast::{CrateNum, NodeId};
-use syntax::codemap::{Span, CodeMap};
+use syntax::codemap::CodeMap;
+use syntax_pos::Span;
 
-use super::data;
+use data;
 
 // FIXME: this should be pub(crate), but the current snapshot doesn't allow it yet
 pub trait Lower {
@@ -90,6 +91,7 @@ pub struct EnumData {
     pub qualname: String,
     pub span: SpanData,
     pub scope: DefId,
+    pub variants: Vec<DefId>
 }
 
 impl Lower for data::EnumData {
@@ -103,6 +105,7 @@ impl Lower for data::EnumData {
             qualname: self.qualname,
             span: SpanData::from_span(self.span, tcx.sess.codemap()),
             scope: make_def_id(self.scope, &tcx.map),
+            variants: self.variants.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
         }
     }
 }
@@ -319,6 +322,7 @@ pub struct MethodData {
     pub span: SpanData,
     pub scope: DefId,
     pub value: String,
+    pub decl_id: Option<DefId>,
 }
 
 impl Lower for data::MethodData {
@@ -332,6 +336,7 @@ impl Lower for data::MethodData {
             id: make_def_id(self.id, &tcx.map),
             qualname: self.qualname,
             value: self.value,
+            decl_id: self.decl_id,
         }
     }
 }
@@ -345,6 +350,7 @@ pub struct ModData {
     pub span: SpanData,
     pub scope: DefId,
     pub filename: String,
+    pub items: Vec<DefId>,
 }
 
 impl Lower for data::ModData {
@@ -358,6 +364,7 @@ impl Lower for data::ModData {
             span: SpanData::from_span(self.span, tcx.sess.codemap()),
             scope: make_def_id(self.scope, &tcx.map),
             filename: self.filename,
+            items: self.items.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
         }
     }
 }
@@ -392,7 +399,8 @@ pub struct StructData {
     pub ctor_id: DefId,
     pub qualname: String,
     pub scope: DefId,
-    pub value: String
+    pub value: String,
+    pub fields: Vec<DefId>,
 }
 
 impl Lower for data::StructData {
@@ -406,7 +414,8 @@ impl Lower for data::StructData {
             ctor_id: make_def_id(self.ctor_id, &tcx.map),
             qualname: self.qualname,
             scope: make_def_id(self.scope, &tcx.map),
-            value: self.value
+            value: self.value,
+            fields: self.fields.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
         }
     }
 }
@@ -445,7 +454,8 @@ pub struct TraitData {
     pub id: DefId,
     pub qualname: String,
     pub scope: DefId,
-    pub value: String
+    pub value: String,
+    pub items: Vec<DefId>,
 }
 
 impl Lower for data::TraitData {
@@ -459,6 +469,7 @@ impl Lower for data::TraitData {
             qualname: self.qualname,
             scope: make_def_id(self.scope, &tcx.map),
             value: self.value,
+            items: self.items.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
         }
     }
 }
@@ -585,6 +596,7 @@ impl Lower for data::UseGlobData {
 pub struct VariableData {
     pub id: DefId,
     pub name: String,
+    pub kind: data::VariableKind,
     pub qualname: String,
     pub span: SpanData,
     pub scope: DefId,
@@ -598,6 +610,7 @@ impl Lower for data::VariableData {
     fn lower(self, tcx: TyCtxt) -> VariableData {
         VariableData {
             id: make_def_id(self.id, &tcx.map),
+            kind: self.kind,
             name: self.name,
             qualname: self.qualname,
             span: SpanData::from_span(self.span, tcx.sess.codemap()),
index 610a9ac2ad6ca18127d2b4b1aec303b526c33568..b1955cbd7b8016cc9b0d85ea99b2f52d17c7286a 100644 (file)
@@ -13,8 +13,9 @@ use std::io::Write;
 use rustc::hir::def_id::DefId;
 use rustc_serialize::json::as_json;
 
-use super::external_data::*;
-use super::dump::Dump;
+use external_data::*;
+use data::VariableKind;
+use dump::Dump;
 
 pub struct JsonDumper<'b, W: Write + 'b> {
     output: &'b mut W,
@@ -180,6 +181,8 @@ struct Def {
     name: String,
     qualname: String,
     value: String,
+    children: Vec<Id>,
+    decl_id: Option<Id>,
 }
 
 #[derive(Debug, RustcEncodable)]
@@ -194,14 +197,19 @@ enum DefKind {
     Trait,
     // value = type + generics
     Function,
+    // value = type + generics
+    Method,
     // No id, no value.
     Macro,
     // value = file_name
     Mod,
     // value = aliased type
     Type,
-    // value = type and init expression
-    Variable,
+    // value = type and init expression (for all variable kinds).
+    Local,
+    Static,
+    Const,
+    Field,
 }
 
 impl From<EnumData> for Def {
@@ -213,6 +221,8 @@ impl From<EnumData> for Def {
             name: data.name,
             qualname: data.qualname,
             value: data.value,
+            children: data.variants.into_iter().map(|id| From::from(id)).collect(),
+            decl_id: None,
         }
     }
 }
@@ -226,6 +236,8 @@ impl From<TupleVariantData> for Def {
             name: data.name,
             qualname: data.qualname,
             value: data.value,
+            children: vec![],
+            decl_id: None,
         }
     }
 }
@@ -238,6 +250,8 @@ impl From<StructVariantData> for Def {
             name: data.name,
             qualname: data.qualname,
             value: data.value,
+            children: vec![],
+            decl_id: None,
         }
     }
 }
@@ -250,6 +264,8 @@ impl From<StructData> for Def {
             name: data.name,
             qualname: data.qualname,
             value: data.value,
+            children: data.fields.into_iter().map(|id| From::from(id)).collect(),
+            decl_id: None,
         }
     }
 }
@@ -262,6 +278,8 @@ impl From<TraitData> for Def {
             name: data.name,
             qualname: data.qualname,
             value: data.value,
+            children: data.items.into_iter().map(|id| From::from(id)).collect(),
+            decl_id: None,
         }
     }
 }
@@ -274,18 +292,22 @@ impl From<FunctionData> for Def {
             name: data.name,
             qualname: data.qualname,
             value: data.value,
+            children: vec![],
+            decl_id: None,
         }
     }
 }
 impl From<MethodData> for Def {
     fn from(data: MethodData) -> Def {
         Def {
-            kind: DefKind::Function,
+            kind: DefKind::Method,
             id: From::from(data.id),
             span: data.span,
             name: data.name,
             qualname: data.qualname,
             value: data.value,
+            children: vec![],
+            decl_id: data.decl_id.map(|id| From::from(id)),
         }
     }
 }
@@ -298,6 +320,8 @@ impl From<MacroData> for Def {
             name: data.name,
             qualname: data.qualname,
             value: String::new(),
+            children: vec![],
+            decl_id: None,
         }
     }
 }
@@ -310,6 +334,8 @@ impl From<ModData> for Def {
             name: data.name,
             qualname: data.qualname,
             value: data.filename,
+            children: data.items.into_iter().map(|id| From::from(id)).collect(),
+            decl_id: None,
         }
     }
 }
@@ -322,18 +348,27 @@ impl From<TypeDefData> for Def {
             name: data.name,
             qualname: data.qualname,
             value: data.value,
+            children: vec![],
+            decl_id: None,
         }
     }
 }
 impl From<VariableData> for Def {
     fn from(data: VariableData) -> Def {
         Def {
-            kind: DefKind::Variable,
+            kind: match data.kind {
+                VariableKind::Static => DefKind::Static,
+                VariableKind::Const => DefKind::Const,
+                VariableKind::Local => DefKind::Local,
+                VariableKind::Field => DefKind::Field,
+            },
             id: From::from(data.id),
             span: data.span,
             name: data.name,
             qualname: data.qualname,
             value: data.value,
+            children: vec![],
+            decl_id: None,
         }
     }
 }
index 8c00a5699939824f2f1882c94d1ef66ada74d37b..0580c51d9a17a745db2140e59ef8b3885624849c 100644 (file)
@@ -27,6 +27,7 @@
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
 extern crate serialize as rustc_serialize;
+extern crate syntax_pos;
 
 mod csv_dumper;
 mod json_dumper;
@@ -38,7 +39,7 @@ pub mod external_data;
 pub mod span_utils;
 
 use rustc::hir;
-use rustc::hir::map::NodeItem;
+use rustc::hir::map::{Node, NodeItem};
 use rustc::hir::def::Def;
 use rustc::hir::def_id::DefId;
 use rustc::session::config::CrateType::CrateTypeExecutable;
@@ -49,10 +50,11 @@ use std::fs::{self, File};
 use std::path::{Path, PathBuf};
 
 use syntax::ast::{self, NodeId, PatKind};
-use syntax::codemap::*;
 use syntax::parse::token::{self, keywords};
 use syntax::visit::{self, Visitor};
 use syntax::print::pprust::{ty_to_string, arg_to_string};
+use syntax::codemap::MacroAttribute;
+use syntax_pos::*;
 
 pub use self::csv_dumper::CsvDumper;
 pub use self::json_dumper::JsonDumper;
@@ -123,15 +125,14 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
     pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> {
         match item.node {
             ast::ItemKind::Fn(ref decl, _, _, _, ref generics, _) => {
-                let name = self.tcx.node_path_str(item.id);
-                let qualname = format!("::{}", name);
+                let qualname = format!("::{}", self.tcx.node_path_str(item.id));
                 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn);
                 filter!(self.span_utils, sub_span, item.span, None);
 
 
                 Some(Data::FunctionData(FunctionData {
                     id: item.id,
-                    name: name,
+                    name: item.ident.to_string(),
                     qualname: qualname,
                     declaration: None,
                     span: sub_span.unwrap(),
@@ -154,6 +155,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 filter!(self.span_utils, sub_span, item.span, None);
                 Some(Data::VariableData(VariableData {
                     id: item.id,
+                    kind: VariableKind::Static,
                     name: item.ident.to_string(),
                     qualname: qualname,
                     span: sub_span.unwrap(),
@@ -168,6 +170,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 filter!(self.span_utils, sub_span, item.span, None);
                 Some(Data::VariableData(VariableData {
                     id: item.id,
+                    kind: VariableKind::Const,
                     name: item.ident.to_string(),
                     qualname: qualname,
                     span: sub_span.unwrap(),
@@ -191,6 +194,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     span: sub_span.unwrap(),
                     scope: self.enclosing_scope(item.id),
                     filename: filename,
+                    items: m.items.iter().map(|i| i.id).collect(),
                 }))
             }
             ast::ItemKind::Enum(ref def, _) => {
@@ -210,6 +214,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                     span: sub_span.unwrap(),
                     qualname: qualname,
                     scope: self.enclosing_scope(item.id),
+                    variants: def.variants.iter().map(|v| v.node.data.id()).collect(),
                 }))
             }
             ast::ItemKind::Impl(_, _, _, ref trait_ref, ref typ, _) => {
@@ -267,6 +272,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             filter!(self.span_utils, sub_span, field.span, None);
             Some(VariableData {
                 id: field.id,
+                kind: VariableKind::Field,
                 name: ident.to_string(),
                 qualname: qualname,
                 span: sub_span.unwrap(),
@@ -293,12 +299,10 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                             let mut result = String::from("<");
                             result.push_str(&rustc::hir::print::ty_to_string(&ty));
 
-                            match self.tcx.trait_of_item(self.tcx.map.local_def_id(id)) {
-                                Some(def_id) => {
-                                    result.push_str(" as ");
-                                    result.push_str(&self.tcx.item_path_str(def_id));
-                                }
-                                None => {}
+                            if let Some(def_id) = self.tcx
+                                    .trait_of_item(self.tcx.map.local_def_id(id)) {
+                                result.push_str(" as ");
+                                result.push_str(&self.tcx.item_path_str(def_id));
                             }
                             result.push_str(">");
                             result
@@ -392,7 +396,14 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         }
         match expr.node {
             ast::ExprKind::Field(ref sub_ex, ident) => {
-                let hir_node = self.tcx.map.expect_expr(sub_ex.id);
+                let hir_node = match self.tcx.map.find(sub_ex.id) {
+                    Some(Node::NodeExpr(expr)) => expr,
+                    _ => {
+                        debug!("Missing or weird node for sub-expression {} in {:?}",
+                               sub_ex.id, expr);
+                        return None;
+                    }
+                };
                 match self.tcx.expr_ty_adjusted(&hir_node).sty {
                     ty::TyStruct(def, _) => {
                         let f = def.struct_variant().field_named(ident.node.name);
@@ -412,7 +423,6 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 }
             }
             ast::ExprKind::Struct(ref path, _, _) => {
-                let hir_node = self.tcx.map.expect_expr(expr.id);
                 match self.tcx.expr_ty_adjusted(&hir_node).sty {
                     ty::TyStruct(def, _) => {
                         let sub_span = self.span_utils.span_for_last_ident(path.span);
@@ -460,11 +470,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
     }
 
     pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Data> {
-        let def_map = self.tcx.def_map.borrow();
-        if !def_map.contains_key(&id) {
-            span_bug!(path.span, "def_map has no key for {} in visit_expr", id);
-        }
-        let def = def_map.get(&id).unwrap().full_def();
+        let def = self.tcx.expect_def(id);
         let sub_span = self.span_utils.span_for_last_ident(path.span);
         filter!(self.span_utils, sub_span, path.span, None);
         match def {
@@ -632,13 +638,9 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
     }
 
     fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
-        if !self.tcx.def_map.borrow().contains_key(&ref_id) {
-            bug!("def_map has no key for {} in lookup_type_ref", ref_id);
-        }
-        let def = self.tcx.def_map.borrow().get(&ref_id).unwrap().full_def();
-        match def {
+        match self.tcx.expect_def(ref_id) {
             Def::PrimTy(_) | Def::SelfTy(..) => None,
-            _ => Some(def.def_id()),
+            def => Some(def.def_id()),
         }
     }
 
@@ -689,16 +691,15 @@ impl PathCollector {
     }
 }
 
-impl<'v> Visitor<'v> for PathCollector {
+impl Visitor for PathCollector {
     fn visit_pat(&mut self, p: &ast::Pat) {
         match p.node {
             PatKind::Struct(ref path, _, _) => {
                 self.collected_paths.push((p.id, path.clone(),
                                            ast::Mutability::Mutable, recorder::TypeRef));
             }
-            PatKind::TupleStruct(ref path, _) |
-            PatKind::Path(ref path) |
-            PatKind::QPath(_, ref path) => {
+            PatKind::TupleStruct(ref path, _, _) |
+            PatKind::Path(_, ref path) => {
                 self.collected_paths.push((p.id, path.clone(),
                                            ast::Mutability::Mutable, recorder::VarRef));
             }
index 3028fb1bfa423e87cb924aff05ae4bef36ea9b9f..953c65549195a9ee00739cafa6f145c7221b23c2 100644 (file)
@@ -17,9 +17,9 @@ use std::env;
 use std::path::Path;
 
 use syntax::ast;
-use syntax::codemap::*;
 use syntax::parse::lexer::{self, Reader, StringReader};
 use syntax::parse::token::{self, keywords, Token};
+use syntax_pos::*;
 
 #[derive(Clone)]
 pub struct SpanUtils<'a> {
@@ -81,7 +81,7 @@ impl<'a> SpanUtils<'a> {
         // are incompatible with spans over other filemaps.
         let filemap = self.sess
                           .codemap()
-                          .new_filemap(String::from("<anon-dxr>"), self.snippet(span));
+                          .new_filemap(String::from("<anon-dxr>"), None, self.snippet(span));
         let s = self.sess;
         lexer::StringReader::new(s.diagnostic(), filemap)
     }
index 9a0580472b4017f779a097efe1a1f1a08e821525..38f9e7ab0c51cce9537b5e3822487ef1d84cc86a 100644 (file)
@@ -19,9 +19,10 @@ rustc_back = { path = "../librustc_back" }
 rustc_const_eval = { path = "../librustc_const_eval" }
 rustc_const_math = { path = "../librustc_const_math" }
 rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_errors = { path = "../librustc_errors" }
 rustc_incremental = { path = "../librustc_incremental" }
 rustc_llvm = { path = "../librustc_llvm" }
-rustc_mir = { path = "../librustc_mir" }
 rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" }
 serialize = { path = "../libserialize" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
\ No newline at end of file
index dbc277f243267e707dd95b86d4f45b96b0d60ccb..3ef6e29a6f83894da32612e3ba11b5fa4fcd780a 100644 (file)
@@ -230,7 +230,7 @@ use std::fmt;
 use std::rc::Rc;
 use rustc::hir::{self, PatKind};
 use syntax::ast::{self, DUMMY_NODE_ID, NodeId};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::hir::fold::Folder;
 use syntax::ptr::P;
 
@@ -424,12 +424,11 @@ impl<'a, 'p, 'blk, 'tcx> fmt::Debug for Match<'a, 'p, 'blk, 'tcx> {
 
 fn has_nested_bindings(m: &[Match], col: usize) -> bool {
     for br in m {
-        match br.pats[col].node {
-            PatKind::Ident(_, _, Some(_)) => return true,
-            _ => ()
+        if let PatKind::Binding(_, _, Some(..)) = br.pats[col].node {
+            return true
         }
     }
-    return false;
+    false
 }
 
 // As noted in `fn match_datum`, we should eventually pass around a
@@ -481,7 +480,7 @@ fn expand_nested_bindings<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         let mut pat = br.pats[col];
         loop {
             pat = match pat.node {
-                PatKind::Ident(_, ref path, Some(ref inner)) => {
+                PatKind::Binding(_, ref path, Some(ref inner)) => {
                     bound_ptrs.push((path.node, val.val));
                     &inner
                 },
@@ -501,30 +500,29 @@ fn expand_nested_bindings<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 }
 
 fn enter_match<'a, 'b, 'p, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
-                                          dm: &RefCell<DefMap>,
                                           m: &[Match<'a, 'p, 'blk, 'tcx>],
                                           col: usize,
                                           val: MatchInput,
                                           mut e: F)
                                           -> Vec<Match<'a, 'p, 'blk, 'tcx>> where
-    F: FnMut(&[&'p hir::Pat]) -> Option<Vec<&'p hir::Pat>>,
+    F: FnMut(&[(&'p hir::Pat, Option<Ty<'tcx>>)])
+             -> Option<Vec<(&'p hir::Pat, Option<Ty<'tcx>>)>>,
 {
     debug!("enter_match(bcx={}, m={:?}, col={}, val={:?})",
            bcx.to_str(), m, col, val);
     let _indenter = indenter();
 
     m.iter().filter_map(|br| {
-        e(&br.pats).map(|pats| {
+        let pats : Vec<_> = br.pats.iter().map(|p| (*p, None)).collect();
+        e(&pats).map(|pats| {
             let this = br.pats[col];
             let mut bound_ptrs = br.bound_ptrs.clone();
             match this.node {
-                PatKind::Ident(_, ref path, None) => {
-                    if pat_is_binding(&dm.borrow(), &this) {
-                        bound_ptrs.push((path.node, val.val));
-                    }
+                PatKind::Binding(_, ref path, None) => {
+                    bound_ptrs.push((path.node, val.val));
                 }
                 PatKind::Vec(ref before, Some(ref slice), ref after) => {
-                    if let PatKind::Ident(_, ref path, None) = slice.node {
+                    if let PatKind::Binding(_, ref path, None) = slice.node {
                         let subslice_val = bind_subslice_pat(
                             bcx, this.id, val,
                             before.len(), after.len());
@@ -534,7 +532,7 @@ fn enter_match<'a, 'b, 'p, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
                 _ => {}
             }
             Match {
-                pats: pats,
+                pats: pats.into_iter().map(|p| p.0).collect(),
                 data: br.data,
                 bound_ptrs: bound_ptrs,
                 pat_renaming_map: br.pat_renaming_map,
@@ -544,7 +542,6 @@ fn enter_match<'a, 'b, 'p, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
 }
 
 fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                     dm: &RefCell<DefMap>,
                                      m: &[Match<'a, 'p, 'blk, 'tcx>],
                                      col: usize,
                                      val: MatchInput)
@@ -554,13 +551,14 @@ fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let _indenter = indenter();
 
     // Collect all of the matches that can match against anything.
-    enter_match(bcx, dm, m, col, val, |pats| {
-        if pat_is_binding_or_wild(&dm.borrow(), &pats[col]) {
-            let mut r = pats[..col].to_vec();
-            r.extend_from_slice(&pats[col + 1..]);
-            Some(r)
-        } else {
-            None
+    enter_match(bcx, m, col, val, |pats| {
+        match pats[col].0.node {
+            PatKind::Binding(..) | PatKind::Wild => {
+                let mut r = pats[..col].to_vec();
+                r.extend_from_slice(&pats[col + 1..]);
+                Some(r)
+            }
+            _ => None
         }
     })
 }
@@ -596,7 +594,6 @@ fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 fn enter_opt<'a, 'p, 'blk, 'tcx>(
              bcx: Block<'blk, 'tcx>,
              _: ast::NodeId,
-             dm: &RefCell<DefMap>,
              m: &[Match<'a, 'p, 'blk, 'tcx>],
              opt: &Opt,
              col: usize,
@@ -628,7 +625,7 @@ fn enter_opt<'a, 'p, 'blk, 'tcx>(
         tcx: bcx.tcx(),
         param_env: param_env,
     };
-    enter_match(bcx, dm, m, col, val, |pats|
+    enter_match(bcx, m, col, val, |pats|
         check_match::specialize(&mcx, &pats[..], &ctor, col, variant_size)
     )
 }
@@ -659,12 +656,9 @@ fn get_branches<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             PatKind::Lit(ref l) => {
                 ConstantValue(ConstantExpr(&l), debug_loc)
             }
-            PatKind::Ident(..) | PatKind::Path(..) |
-            PatKind::TupleStruct(..) | PatKind::Struct(..) => {
-                // This is either an enum variant or a variable binding.
-                let opt_def = tcx.def_map.borrow().get(&cur.id).map(|d| d.full_def());
-                match opt_def {
-                    Some(Def::Variant(enum_id, var_id)) => {
+            PatKind::Path(..) | PatKind::TupleStruct(..) | PatKind::Struct(..) => {
+                match tcx.expect_def(cur.id) {
+                    Def::Variant(enum_id, var_id) => {
                         let variant = tcx.lookup_adt_def(enum_id).variant_with_id(var_id);
                         Variant(Disr::from(variant.disr_val),
                                 adt::represent_node(bcx, cur.id),
@@ -736,9 +730,16 @@ fn bind_subslice_pat(bcx: Block,
     let (base, len) = vec_datum.get_vec_base_and_len(bcx);
 
     let slice_begin = InBoundsGEP(bcx, base, &[C_uint(bcx.ccx(), offset_left)]);
-    let slice_len_offset = C_uint(bcx.ccx(), offset_left + offset_right);
+    let diff = offset_left + offset_right;
+    if let ty::TyArray(ty, n) = vec_ty_contents.sty {
+        let array_ty = bcx.tcx().mk_array(ty, n-diff);
+        let llty_array = type_of::type_of(bcx.ccx(), array_ty);
+        return PointerCast(bcx, slice_begin, llty_array.ptr_to());
+    }
+
+    let slice_len_offset = C_uint(bcx.ccx(), diff);
     let slice_len = Sub(bcx, len, slice_len_offset, DebugLoc::None);
-    let slice_ty = bcx.tcx().mk_imm_ref(bcx.tcx().mk_region(ty::ReStatic),
+    let slice_ty = bcx.tcx().mk_imm_ref(bcx.tcx().mk_region(ty::ReErased),
                                          bcx.tcx().mk_slice(unit_ty));
     let scratch = rvalue_scratch_datum(bcx, slice_ty, "");
     Store(bcx, slice_begin, expr::get_dataptr(bcx, scratch.val));
@@ -792,10 +793,9 @@ fn any_irrefutable_adt_pat(tcx: TyCtxt, m: &[Match], col: usize) -> bool {
     m.iter().any(|br| {
         let pat = br.pats[col];
         match pat.node {
-            PatKind::Tup(_) => true,
-            PatKind::Struct(..) | PatKind::TupleStruct(..) |
-            PatKind::Path(..) | PatKind::Ident(_, _, None) => {
-                match tcx.def_map.borrow().get(&pat.id).unwrap().full_def() {
+            PatKind::Tuple(..) => true,
+            PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => {
+                match tcx.expect_def(pat.id) {
                     Def::Struct(..) | Def::TyAlias(..) => true,
                     _ => false,
                 }
@@ -839,7 +839,7 @@ impl FailureHandler {
 fn pick_column_to_specialize(def_map: &RefCell<DefMap>, m: &[Match]) -> Option<usize> {
     fn pat_score(def_map: &RefCell<DefMap>, pat: &hir::Pat) -> usize {
         match pat.node {
-            PatKind::Ident(_, _, Some(ref inner)) => pat_score(def_map, &inner),
+            PatKind::Binding(_, _, Some(ref inner)) => pat_score(def_map, &inner),
             _ if pat_is_refutable(&def_map.borrow(), pat) => 1,
             _ => 0
         }
@@ -888,7 +888,7 @@ fn compare_values<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                rhs_t: Ty<'tcx>,
                                debug_loc: DebugLoc)
                                -> Result<'blk, 'tcx> {
-        let did = langcall(bcx,
+        let did = langcall(bcx.tcx(),
                            None,
                            &format!("comparison of `{}`", rhs_t),
                            StrEqFnLangItem);
@@ -1153,7 +1153,6 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                                                  has_genuine_default: bool) {
     let fcx = bcx.fcx;
     let tcx = bcx.tcx();
-    let dm = &tcx.def_map;
 
     let mut vals_left = vals[0..col].to_vec();
     vals_left.extend_from_slice(&vals[col + 1..]);
@@ -1214,7 +1213,12 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         }
         Some(field_vals)
     } else if any_uniq_pat(m, col) || any_region_pat(m, col) {
-        Some(vec!(Load(bcx, val.val)))
+        let ptr = if type_is_fat_ptr(bcx.tcx(), left_ty) {
+            val.val
+        } else {
+            Load(bcx, val.val)
+        };
+        Some(vec!(ptr))
     } else {
         match left_ty.sty {
             ty::TyArray(_, n) => {
@@ -1226,7 +1230,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     };
     match adt_vals {
         Some(field_vals) => {
-            let pats = enter_match(bcx, dm, m, col, val, |pats|
+            let pats = enter_match(bcx, m, col, val, |pats|
                 check_match::specialize(&mcx, pats,
                                         &Constructor::Single, col,
                                         field_vals.len())
@@ -1287,7 +1291,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         C_int(ccx, 0) // Placeholder for when not using a switch
     };
 
-    let defaults = enter_default(else_cx, dm, m, col, val);
+    let defaults = enter_default(else_cx, m, col, val);
     let exhaustive = chk.is_infallible() && defaults.is_empty();
     let len = opts.len();
 
@@ -1391,7 +1395,7 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             }
             ConstantValue(..) | ConstantRange(..) => ()
         }
-        let opt_ms = enter_opt(opt_cx, pat_id, dm, m, opt, col, size, val);
+        let opt_ms = enter_opt(opt_cx, pat_id, m, opt, col, size, val);
         let mut opt_vals: Vec<_> = unpacked.into_iter()
             .map(|v|MatchInput::from_val(v))
             .collect();
@@ -1439,19 +1443,19 @@ pub fn trans_match<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 /// Checks whether the binding in `discr` is assigned to anywhere in the expression `body`
 fn is_discr_reassigned(bcx: Block, discr: &hir::Expr, body: &hir::Expr) -> bool {
     let (vid, field) = match discr.node {
-        hir::ExprPath(..) => match bcx.def(discr.id) {
+        hir::ExprPath(..) => match bcx.tcx().expect_def(discr.id) {
             Def::Local(_, vid) | Def::Upvar(_, vid, _, _) => (vid, None),
             _ => return false
         },
         hir::ExprField(ref base, field) => {
-            let vid = match bcx.tcx().def_map.borrow().get(&base.id).map(|d| d.full_def()) {
+            let vid = match bcx.tcx().expect_def_or_none(base.id) {
                 Some(Def::Local(_, vid)) | Some(Def::Upvar(_, vid, _, _)) => vid,
                 _ => return false
             };
             (vid, Some(mc::NamedField(field.node)))
         },
         hir::ExprTupField(ref base, field) => {
-            let vid = match bcx.tcx().def_map.borrow().get(&base.id).map(|d| d.full_def()) {
+            let vid = match bcx.tcx().expect_def_or_none(base.id) {
                 Some(Def::Local(_, vid)) | Some(Def::Upvar(_, vid, _, _)) => vid,
                 _ => return false
             };
@@ -1517,10 +1521,9 @@ fn create_bindings_map<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pat: &hir::Pat,
     // Note that we use the names because each binding will have many ids
     // from the various alternatives.
     let ccx = bcx.ccx();
-    let tcx = bcx.tcx();
     let reassigned = is_discr_reassigned(bcx, discr, body);
     let mut bindings_map = FnvHashMap();
-    pat_bindings(&tcx.def_map, &pat, |bm, p_id, span, path1| {
+    pat_bindings(&pat, |bm, p_id, span, path1| {
         let name = path1.node;
         let variable_ty = node_id_type(bcx, p_id);
         let llvariable_ty = type_of::type_of(ccx, variable_ty);
@@ -1663,7 +1666,7 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         // create dummy memory for the variables if we have no
         // value to store into them immediately
         let tcx = bcx.tcx();
-        pat_bindings(&tcx.def_map, pat, |_, p_id, _, path1| {
+        pat_bindings(pat, |_, p_id, _, path1| {
             let scope = cleanup::var_scope(tcx, p_id);
             bcx = mk_binding_alloca(
                 bcx, p_id, path1.node, scope, (),
@@ -1703,17 +1706,13 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             //
             // In such cases, the more general path is unsafe, because
             // it assumes it is matching against a valid value.
-            match simple_name(pat) {
-                Some(name) => {
-                    let var_scope = cleanup::var_scope(tcx, local.id);
-                    return mk_binding_alloca(
-                        bcx, pat.id, name, var_scope, (),
-                        "_match::store_local",
-                        |(), bcx, Datum { val: v, .. }| expr::trans_into(bcx, &init_expr,
-                                                                         expr::SaveIn(v)));
-                }
-
-                None => {}
+            if let Some(name) = simple_name(pat) {
+                let var_scope = cleanup::var_scope(tcx, local.id);
+                return mk_binding_alloca(
+                    bcx, pat.id, name, var_scope, (),
+                    "_match::store_local",
+                    |(), bcx, Datum { val: v, .. }| expr::trans_into(bcx, &init_expr,
+                                                                     expr::SaveIn(v)));
             }
 
             // General path.
@@ -1796,82 +1795,77 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let tcx = bcx.tcx();
     let ccx = bcx.ccx();
     match pat.node {
-        PatKind::Ident(pat_binding_mode, ref path1, ref inner) => {
-            if pat_is_binding(&tcx.def_map.borrow(), &pat) {
-                // Allocate the stack slot where the value of this
-                // binding will live and place it into the appropriate
-                // map.
-                bcx = mk_binding_alloca(
-                    bcx, pat.id, path1.node, cleanup_scope, (),
-                    "_match::bind_irrefutable_pat",
-                    |(), bcx, Datum { val: llval, ty, kind: _ }| {
-                        match pat_binding_mode {
-                            hir::BindByValue(_) => {
-                                // By value binding: move the value that `val`
-                                // points at into the binding's stack slot.
-                                let d = val.to_datum(ty);
-                                d.store_to(bcx, llval)
-                            }
+        PatKind::Binding(pat_binding_mode, ref path1, ref inner) => {
+            // Allocate the stack slot where the value of this
+            // binding will live and place it into the appropriate
+            // map.
+            bcx = mk_binding_alloca(bcx, pat.id, path1.node, cleanup_scope, (),
+                                    "_match::bind_irrefutable_pat",
+                                    |(), bcx, Datum { val: llval, ty, kind: _ }| {
+                match pat_binding_mode {
+                    hir::BindByValue(_) => {
+                        // By value binding: move the value that `val`
+                        // points at into the binding's stack slot.
+                        let d = val.to_datum(ty);
+                        d.store_to(bcx, llval)
+                    }
 
-                            hir::BindByRef(_) => {
-                                // By ref binding: the value of the variable
-                                // is the pointer `val` itself or fat pointer referenced by `val`
-                                if type_is_fat_ptr(bcx.tcx(), ty) {
-                                    expr::copy_fat_ptr(bcx, val.val, llval);
-                                }
-                                else {
-                                    Store(bcx, val.val, llval);
-                                }
-
-                                bcx
-                            }
+                    hir::BindByRef(_) => {
+                        // By ref binding: the value of the variable
+                        // is the pointer `val` itself or fat pointer referenced by `val`
+                        if type_is_fat_ptr(bcx.tcx(), ty) {
+                            expr::copy_fat_ptr(bcx, val.val, llval);
                         }
-                    });
-            }
+                        else {
+                            Store(bcx, val.val, llval);
+                        }
+
+                        bcx
+                    }
+                }
+            });
 
             if let Some(ref inner_pat) = *inner {
                 bcx = bind_irrefutable_pat(bcx, &inner_pat, val, cleanup_scope);
             }
         }
-        PatKind::TupleStruct(_, ref sub_pats) => {
-            let opt_def = bcx.tcx().def_map.borrow().get(&pat.id).map(|d| d.full_def());
-            match opt_def {
-                Some(Def::Variant(enum_id, var_id)) => {
+        PatKind::TupleStruct(_, ref sub_pats, ddpos) => {
+            match bcx.tcx().expect_def(pat.id) {
+                Def::Variant(enum_id, var_id) => {
                     let repr = adt::represent_node(bcx, pat.id);
                     let vinfo = ccx.tcx().lookup_adt_def(enum_id).variant_with_id(var_id);
                     let args = extract_variant_args(bcx,
                                                     &repr,
                                                     Disr::from(vinfo.disr_val),
                                                     val);
-                    if let Some(ref sub_pat) = *sub_pats {
-                        for (i, &argval) in args.vals.iter().enumerate() {
-                            bcx = bind_irrefutable_pat(
-                                bcx,
-                                &sub_pat[i],
-                                MatchInput::from_val(argval),
-                                cleanup_scope);
-                        }
+                    for (i, subpat) in sub_pats.iter()
+                                               .enumerate_and_adjust(vinfo.fields.len(), ddpos) {
+                        bcx = bind_irrefutable_pat(
+                            bcx,
+                            subpat,
+                            MatchInput::from_val(args.vals[i]),
+                            cleanup_scope);
                     }
                 }
-                Some(Def::Struct(..)) => {
-                    match *sub_pats {
-                        None => {
-                            // This is a unit-like struct. Nothing to do here.
+                Def::Struct(..) => {
+                    let expected_len = match *ccx.tcx().pat_ty(&pat) {
+                        ty::TyS{sty: ty::TyStruct(adt_def, _), ..} => {
+                            adt_def.struct_variant().fields.len()
                         }
-                        Some(ref elems) => {
-                            // This is the tuple struct case.
-                            let repr = adt::represent_node(bcx, pat.id);
-                            let val = adt::MaybeSizedValue::sized(val.val);
-                            for (i, elem) in elems.iter().enumerate() {
-                                let fldptr = adt::trans_field_ptr(bcx, &repr,
-                                                                  val, Disr(0), i);
-                                bcx = bind_irrefutable_pat(
-                                    bcx,
-                                    &elem,
-                                    MatchInput::from_val(fldptr),
-                                    cleanup_scope);
-                            }
+                        ref ty => {
+                            span_bug!(pat.span, "tuple struct pattern unexpected type {:?}", ty);
                         }
+                    };
+
+                    let repr = adt::represent_node(bcx, pat.id);
+                    let val = adt::MaybeSizedValue::sized(val.val);
+                    for (i, elem) in sub_pats.iter().enumerate_and_adjust(expected_len, ddpos) {
+                        let fldptr = adt::trans_field_ptr(bcx, &repr, val, Disr(0), i);
+                        bcx = bind_irrefutable_pat(
+                            bcx,
+                            &elem,
+                            MatchInput::from_val(fldptr),
+                            cleanup_scope);
                     }
                 }
                 _ => {
@@ -1919,16 +1913,21 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                            cleanup_scope);
             }
         }
-        PatKind::Tup(ref elems) => {
-            let repr = adt::represent_node(bcx, pat.id);
-            let val = adt::MaybeSizedValue::sized(val.val);
-            for (i, elem) in elems.iter().enumerate() {
-                let fldptr = adt::trans_field_ptr(bcx, &repr, val, Disr(0), i);
-                bcx = bind_irrefutable_pat(
-                    bcx,
-                    &elem,
-                    MatchInput::from_val(fldptr),
-                    cleanup_scope);
+        PatKind::Tuple(ref elems, ddpos) => {
+            match tcx.node_id_to_type(pat.id).sty {
+                ty::TyTuple(ref tys) => {
+                    let repr = adt::represent_node(bcx, pat.id);
+                    let val = adt::MaybeSizedValue::sized(val.val);
+                    for (i, elem) in elems.iter().enumerate_and_adjust(tys.len(), ddpos) {
+                        let fldptr = adt::trans_field_ptr(bcx, &repr, val, Disr(0), i);
+                        bcx = bind_irrefutable_pat(
+                            bcx,
+                            &elem,
+                            MatchInput::from_val(fldptr),
+                            cleanup_scope);
+                    }
+                }
+                ref sty => span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", sty),
             }
         }
         PatKind::Box(ref inner) => {
@@ -1937,7 +1936,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             let val = if type_is_fat_ptr(tcx, pat_ty) {
                 // We need to check for this, as the pattern could be binding
                 // a fat pointer by-value.
-                if let PatKind::Ident(hir::BindByRef(_),_,_) = inner.node {
+                if let PatKind::Binding(hir::BindByRef(..),_,_) = inner.node {
                     val.val
                 } else {
                     Load(bcx, val.val)
@@ -1956,7 +1955,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             let val = if type_is_fat_ptr(tcx, pat_ty) {
                 // We need to check for this, as the pattern could be binding
                 // a fat pointer by-value.
-                if let PatKind::Ident(hir::BindByRef(_),_,_) = inner.node {
+                if let PatKind::Binding(hir::BindByRef(..),_,_) = inner.node {
                     val.val
                 } else {
                     Load(bcx, val.val)
@@ -1997,8 +1996,8 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                         cleanup_scope)
                 });
         }
-        PatKind::Path(..) | PatKind::QPath(..) | PatKind::Wild | PatKind::Lit(_) |
-        PatKind::Range(_, _) => ()
+        PatKind::Path(..) | PatKind::QPath(..) | PatKind::Wild |
+        PatKind::Lit(..) | PatKind::Range(..) => ()
     }
     return bcx;
 }
index a4792ea328f08b64e13a25ca8ab82d07ef1fabef..23c4258caf7bd9c5ca873ba44d9c4ac28880f2d0 100644 (file)
@@ -191,9 +191,8 @@ pub fn represent_type<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                 t: Ty<'tcx>)
                                 -> Rc<Repr<'tcx>> {
     debug!("Representing: {}", t);
-    match cx.adt_reprs().borrow().get(&t) {
-        Some(repr) => return repr.clone(),
-        None => {}
+    if let Some(repr) = cx.adt_reprs().borrow().get(&t) {
+        return repr.clone();
     }
 
     let repr = Rc::new(represent_type_uncached(cx, t));
index d4930f37dcd5e5c3f06551f9047d05979f173dbf..01e9970dc76c349d5b499a85056f9a3d4c4d9d72 100644 (file)
@@ -11,7 +11,6 @@
 
 use libc::c_uint;
 use llvm::{self, ValueRef};
-use session::config::NoDebugInfo;
 pub use syntax::attr::InlineAttr;
 use syntax::ast;
 use context::CrateContext;
@@ -74,25 +73,28 @@ pub fn naked(val: ValueRef, is_naked: bool) {
     }
 }
 
-/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute])
-/// attributes.
-pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) {
-    use syntax::attr::*;
-    inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs));
-
+pub fn set_frame_pointer_elimination(ccx: &CrateContext, llfn: ValueRef) {
     // FIXME: #11906: Omitting frame pointers breaks retrieving the value of a
     // parameter.
-    let no_fp_elim = (ccx.sess().opts.debuginfo != NoDebugInfo) ||
-                     !ccx.sess().target.target.options.eliminate_frame_pointer;
-    if no_fp_elim {
+    if ccx.sess().must_not_eliminate_frame_pointers() {
         unsafe {
             let attr = "no-frame-pointer-elim\0".as_ptr() as *const _;
             let val = "true\0".as_ptr() as *const _;
             llvm::LLVMAddFunctionAttrStringValue(llfn,
                                                  llvm::FunctionIndex as c_uint,
-                                                 attr, val);
+                                                 attr,
+                                                 val);
         }
     }
+}
+
+/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute])
+/// attributes.
+pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) {
+    use syntax::attr::*;
+    inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs));
+
+    set_frame_pointer_elimination(ccx, llfn);
 
     for attr in attrs {
         if attr.check_name("cold") {
index aea61da18a0a1c59a67721b43670c6fc147e442b..29019f3683dead592681278afbbce89fcf349461 100644 (file)
 
 //! A helper class for dealing with static archives
 
-use std::env;
 use std::ffi::{CString, CStr, OsString};
-use std::fs::{self, File};
-use std::io::prelude::*;
 use std::io;
 use std::mem;
 use std::path::{Path, PathBuf};
-use std::process::{Command, Output, Stdio};
 use std::ptr;
 use std::str;
 
@@ -25,7 +21,6 @@ use libc;
 use llvm::archive_ro::{ArchiveRO, Child};
 use llvm::{self, ArchiveKind};
 use rustc::session::Session;
-use rustc_back::tempdir::TempDir;
 
 pub struct ArchiveConfig<'a> {
     pub sess: &'a Session,
@@ -41,7 +36,6 @@ pub struct ArchiveConfig<'a> {
 #[must_use = "must call build() to finish building the archive"]
 pub struct ArchiveBuilder<'a> {
     config: ArchiveConfig<'a>,
-    work_dir: TempDir,
     removals: Vec<String>,
     additions: Vec<Addition>,
     should_update_symbols: bool,
@@ -55,17 +49,10 @@ enum Addition {
     },
     Archive {
         archive: ArchiveRO,
-        archive_name: String,
         skip: Box<FnMut(&str) -> bool>,
     },
 }
 
-enum Action<'a> {
-    Remove(&'a [String]),
-    AddObjects(&'a [&'a PathBuf], bool),
-    UpdateSymbols,
-}
-
 pub fn find_library(name: &str, search_paths: &[PathBuf], sess: &Session)
                     -> PathBuf {
     // On Windows, static libraries sometimes show up as libfoo.a and other
@@ -102,7 +89,6 @@ impl<'a> ArchiveBuilder<'a> {
     pub fn new(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> {
         ArchiveBuilder {
             config: config,
-            work_dir: TempDir::new("rsar").unwrap(),
             removals: Vec::new(),
             additions: Vec::new(),
             should_update_symbols: false,
@@ -148,7 +134,7 @@ impl<'a> ArchiveBuilder<'a> {
     pub fn add_native_library(&mut self, name: &str) {
         let location = find_library(name, &self.config.lib_search_paths,
                                     self.config.sess);
-        self.add_archive(&location, name, |_| false).unwrap_or_else(|e| {
+        self.add_archive(&location, |_| false).unwrap_or_else(|e| {
             self.config.sess.fatal(&format!("failed to add native library {}: {}",
                                             location.to_string_lossy(), e));
         });
@@ -172,14 +158,14 @@ impl<'a> ArchiveBuilder<'a> {
         let metadata_filename =
             self.config.sess.cstore.metadata_filename().to_owned();
 
-        self.add_archive(rlib, &name[..], move |fname: &str| {
+        self.add_archive(rlib, move |fname: &str| {
             let skip_obj = lto && fname.starts_with(&obj_start)
                 && fname.ends_with(".o");
             skip_obj || fname.ends_with(bc_ext) || fname == metadata_filename
         })
     }
 
-    fn add_archive<F>(&mut self, archive: &Path, name: &str, skip: F)
+    fn add_archive<F>(&mut self, archive: &Path, skip: F)
                       -> io::Result<()>
         where F: FnMut(&str) -> bool + 'static
     {
@@ -190,7 +176,6 @@ impl<'a> ArchiveBuilder<'a> {
         };
         self.additions.push(Addition::Archive {
             archive: archive,
-            archive_name: name.to_string(),
             skip: Box::new(skip),
         });
         Ok(())
@@ -214,234 +199,23 @@ impl<'a> ArchiveBuilder<'a> {
     /// Combine the provided files, rlibs, and native libraries into a single
     /// `Archive`.
     pub fn build(&mut self) {
-        let res = match self.llvm_archive_kind() {
-            Some(kind) => self.build_with_llvm(kind),
-            None => self.build_with_ar_cmd(),
-        };
-        if let Err(e) = res {
-            self.config.sess.fatal(&format!("failed to build archive: {}", e));
-        }
-    }
-
-    pub fn llvm_archive_kind(&self) -> Option<ArchiveKind> {
-        if unsafe { llvm::LLVMVersionMinor() < 7 } {
-            return None
-        }
-
-        // Currently LLVM only supports writing archives in the 'gnu' format.
-        match &self.config.sess.target.target.options.archive_format[..] {
-            "gnu" => Some(ArchiveKind::K_GNU),
-            "mips64" => Some(ArchiveKind::K_MIPS64),
-            "bsd" => Some(ArchiveKind::K_BSD),
-            "coff" => Some(ArchiveKind::K_COFF),
-            _ => None,
-        }
-    }
-
-    pub fn using_llvm(&self) -> bool {
-        self.llvm_archive_kind().is_some()
-    }
-
-    fn build_with_ar_cmd(&mut self) -> io::Result<()> {
-        let removals = mem::replace(&mut self.removals, Vec::new());
-        let additions = mem::replace(&mut self.additions, Vec::new());
-        let should_update_symbols = mem::replace(&mut self.should_update_symbols,
-                                                 false);
-
-        // Don't use fs::copy because libs may be installed as read-only and we
-        // want to modify this archive, so we use `io::copy` to not preserve
-        // permission bits.
-        if let Some(ref s) = self.config.src {
-            io::copy(&mut File::open(s)?,
-                     &mut File::create(&self.config.dst)?)?;
-        }
-
-        if removals.len() > 0 {
-            self.run(None, Action::Remove(&removals));
-        }
-
-        let mut members = Vec::new();
-        for addition in additions {
-            match addition {
-                Addition::File { path, name_in_archive } => {
-                    let dst = self.work_dir.path().join(&name_in_archive);
-                    fs::copy(&path, &dst)?;
-                    members.push(PathBuf::from(name_in_archive));
-                }
-                Addition::Archive { archive, archive_name, mut skip } => {
-                    self.add_archive_members(&mut members, archive,
-                                             &archive_name, &mut *skip)?;
-                }
-            }
-        }
-
-        // Get an absolute path to the destination, so `ar` will work even
-        // though we run it from `self.work_dir`.
-        let mut objects = Vec::new();
-        let mut total_len = self.config.dst.to_string_lossy().len();
-
-        if members.is_empty() {
-            if should_update_symbols {
-                self.run(Some(self.work_dir.path()), Action::UpdateSymbols);
-            }
-            return Ok(())
-        }
-
-        // Don't allow the total size of `args` to grow beyond 32,000 bytes.
-        // Windows will raise an error if the argument string is longer than
-        // 32,768, and we leave a bit of extra space for the program name.
-        const ARG_LENGTH_LIMIT: usize = 32_000;
-
-        for member_name in &members {
-            let len = member_name.to_string_lossy().len();
-
-            // `len + 1` to account for the space that's inserted before each
-            // argument.  (Windows passes command-line arguments as a single
-            // string, not an array of strings.)
-            if total_len + len + 1 > ARG_LENGTH_LIMIT {
-                // Add the archive members seen so far, without updating the
-                // symbol table.
-                self.run(Some(self.work_dir.path()),
-                         Action::AddObjects(&objects, false));
-
-                objects.clear();
-                total_len = self.config.dst.to_string_lossy().len();
-            }
-
-            objects.push(member_name);
-            total_len += len + 1;
-        }
-
-        // Add the remaining archive members, and update the symbol table if
-        // necessary.
-        self.run(Some(self.work_dir.path()),
-                         Action::AddObjects(&objects, should_update_symbols));
-        Ok(())
-    }
-
-    fn add_archive_members(&mut self, members: &mut Vec<PathBuf>,
-                           archive: ArchiveRO, name: &str,
-                           skip: &mut FnMut(&str) -> bool) -> io::Result<()> {
-        // Next, we must rename all of the inputs to "guaranteed unique names".
-        // We write each file into `self.work_dir` under its new unique name.
-        // The reason for this renaming is that archives are keyed off the name
-        // of the files, so if two files have the same name they will override
-        // one another in the archive (bad).
-        //
-        // We skip any files explicitly desired for skipping, and we also skip
-        // all SYMDEF files as these are just magical placeholders which get
-        // re-created when we make a new archive anyway.
-        for file in archive.iter() {
-            let file = file.map_err(string_to_io_error)?;
-            if !is_relevant_child(&file) {
-                continue
-            }
-            let filename = file.name().unwrap();
-            if skip(filename) {
-                continue
+        let kind = match self.llvm_archive_kind() {
+            Ok(kind) => kind,
+            Err(kind) => {
+                self.config.sess.fatal(&format!("Don't know how to build archive of type: {}",
+                                                kind));
             }
-            let filename = Path::new(filename).file_name().unwrap()
-                                              .to_str().unwrap();
-
-            // Archives on unix systems typically do not have slashes in
-            // filenames as the `ar` utility generally only uses the last
-            // component of a path for the filename list in the archive. On
-            // Windows, however, archives assembled with `lib.exe` will preserve
-            // the full path to the file that was placed in the archive,
-            // including path separators.
-            //
-            // The code below is munging paths so it'll go wrong pretty quickly
-            // if there's some unexpected slashes in the filename, so here we
-            // just chop off everything but the filename component. Note that
-            // this can cause duplicate filenames, but that's also handled below
-            // as well.
-            let filename = Path::new(filename).file_name().unwrap()
-                                              .to_str().unwrap();
-
-            // An archive can contain files of the same name multiple times, so
-            // we need to be sure to not have them overwrite one another when we
-            // extract them. Consequently we need to find a truly unique file
-            // name for us!
-            let mut new_filename = String::new();
-            for n in 0.. {
-                let n = if n == 0 {String::new()} else {format!("-{}", n)};
-                new_filename = format!("r{}-{}-{}", n, name, filename);
-
-                // LLDB (as mentioned in back::link) crashes on filenames of
-                // exactly
-                // 16 bytes in length. If we're including an object file with
-                //    exactly 16-bytes of characters, give it some prefix so
-                //    that it's not 16 bytes.
-                new_filename = if new_filename.len() == 16 {
-                    format!("lldb-fix-{}", new_filename)
-                } else {
-                    new_filename
-                };
-
-                let present = members.iter().filter_map(|p| {
-                    p.file_name().and_then(|f| f.to_str())
-                }).any(|s| s == new_filename);
-                if !present {
-                    break
-                }
-            }
-            let dst = self.work_dir.path().join(&new_filename);
-            File::create(&dst)?.write_all(file.data())?;
-            members.push(PathBuf::from(new_filename));
-        }
-        Ok(())
-    }
-
-    fn run(&self, cwd: Option<&Path>, action: Action) -> Output {
-        let abs_dst = env::current_dir().unwrap().join(&self.config.dst);
-        let ar = &self.config.ar_prog;
-        let mut cmd = Command::new(ar);
-        cmd.env("PATH", &self.config.command_path);
-        cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
-        self.prepare_ar_action(&mut cmd, &abs_dst, action);
-        info!("{:?}", cmd);
+        };
 
-        if let Some(p) = cwd {
-            cmd.current_dir(p);
-            info!("inside {:?}", p.display());
+        if let Err(e) = self.build_with_llvm(kind) {
+            self.config.sess.fatal(&format!("failed to build archive: {}", e));
         }
 
-        let sess = &self.config.sess;
-        match cmd.spawn() {
-            Ok(prog) => {
-                let o = prog.wait_with_output().unwrap();
-                if !o.status.success() {
-                    sess.struct_err(&format!("{:?} failed with: {}", cmd, o.status))
-                        .note(&format!("stdout ---\n{}",
-                                       str::from_utf8(&o.stdout).unwrap()))
-                        .note(&format!("stderr ---\n{}",
-                                       str::from_utf8(&o.stderr).unwrap()))
-                        .emit();
-                    sess.abort_if_errors();
-                }
-                o
-            },
-            Err(e) => {
-                sess.fatal(&format!("could not exec `{}`: {}",
-                                    self.config.ar_prog, e));
-            }
-        }
     }
 
-    fn prepare_ar_action(&self, cmd: &mut Command, dst: &Path, action: Action) {
-        match action {
-            Action::Remove(files) => {
-                cmd.arg("d").arg(dst).args(files);
-            }
-            Action::AddObjects(objs, update_symbols) => {
-                cmd.arg(if update_symbols {"crs"} else {"crS"})
-                   .arg(dst)
-                   .args(objs);
-            }
-            Action::UpdateSymbols => {
-                cmd.arg("s").arg(dst);
-            }
-        }
+    fn llvm_archive_kind(&self) -> Result<ArchiveKind, &str> {
+        let kind = &self.config.sess.target.target.options.archive_format[..];
+        kind.parse().map_err(|_| kind)
     }
 
     fn build_with_llvm(&mut self, kind: ArchiveKind) -> io::Result<()> {
@@ -480,7 +254,7 @@ impl<'a> ArchiveBuilder<'a> {
                         strings.push(path);
                         strings.push(name);
                     }
-                    Addition::Archive { archive, archive_name: _, mut skip } => {
+                    Addition::Archive { archive, mut skip } => {
                         for child in archive.iter() {
                             let child = child.map_err(string_to_io_error)?;
                             if !is_relevant_child(&child) {
index 53cc03198292e825c191688e554eeb0f323b3a06..744712b22b060ef3cafafef22dc282fb8329daa2 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use super::archive::{ArchiveBuilder, ArchiveConfig};
-use super::linker::{Linker, GnuLinker, MsvcLinker};
+use super::linker::Linker;
 use super::rpath::RPathConfig;
 use super::rpath;
 use super::msvc;
@@ -42,8 +42,8 @@ use std::process::Command;
 use std::str;
 use flate;
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::attr::AttrMetaMethods;
+use syntax_pos::Span;
 
 // RLIB LLVM-BYTECODE OBJECT LAYOUT
 // Version 1
@@ -136,14 +136,17 @@ pub fn build_link_meta<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     return r;
 }
 
-pub fn get_linker(sess: &Session) -> (String, Command) {
+// The third parameter is for an extra path to add to PATH for MSVC
+// cross linkers for host toolchain DLL dependencies
+pub fn get_linker(sess: &Session) -> (String, Command, Option<PathBuf>) {
     if let Some(ref linker) = sess.opts.cg.linker {
-        (linker.clone(), Command::new(linker))
+        (linker.clone(), Command::new(linker), None)
     } else if sess.target.target.options.is_like_msvc {
-        ("link.exe".to_string(), msvc::link_exe_cmd(sess))
+        let (cmd, host) = msvc::link_exe_cmd(sess);
+        ("link.exe".to_string(), cmd, host)
     } else {
         (sess.target.target.options.linker.clone(),
-         Command::new(&sess.target.target.options.linker))
+         Command::new(&sess.target.target.options.linker), None)
     }
 }
 
@@ -153,7 +156,7 @@ pub fn get_ar_prog(sess: &Session) -> String {
     })
 }
 
-fn command_path(sess: &Session) -> OsString {
+fn command_path(sess: &Session, extra: Option<PathBuf>) -> OsString {
     // The compiler's sysroot often has some bundled tools, so add it to the
     // PATH for the child.
     let mut new_path = sess.host_filesearch(PathKind::All)
@@ -161,9 +164,7 @@ fn command_path(sess: &Session) -> OsString {
     if let Some(path) = env::var_os("PATH") {
         new_path.extend(env::split_paths(&path));
     }
-    if sess.target.target.options.is_like_msvc {
-        new_path.extend(msvc::host_dll_path());
-    }
+    new_path.extend(extra);
     env::join_paths(new_path).unwrap()
 }
 
@@ -188,6 +189,11 @@ pub fn link_binary(sess: &Session,
 
     let mut out_filenames = Vec::new();
     for &crate_type in sess.crate_types.borrow().iter() {
+        // Ignore executable crates if we have -Z no-trans, as they will error.
+        if sess.opts.no_trans && crate_type == config::CrateTypeExecutable {
+            continue;
+        }
+
         if invalid_output_for_target(sess, crate_type) {
            bug!("invalid output type `{:?}` for target os `{}`",
                 crate_type, sess.opts.target_triple);
@@ -374,7 +380,7 @@ fn archive_config<'a>(sess: &'a Session,
         src: input.map(|p| p.to_path_buf()),
         lib_search_paths: archive_search_paths(sess),
         ar_prog: get_ar_prog(sess),
-        command_path: command_path(sess),
+        command_path: command_path(sess, None),
     }
 }
 
@@ -407,13 +413,6 @@ fn link_rlib<'a>(sess: &'a Session,
     // symbol table of the archive.
     ab.update_symbols();
 
-    // For OSX/iOS, we must be careful to update symbols only when adding
-    // object files.  We're about to start adding non-object files, so run
-    // `ar` now to process the object files.
-    if sess.target.target.options.is_like_osx && !ab.using_llvm() {
-        ab.build();
-    }
-
     // Note that it is important that we add all of our non-object "magical
     // files" *after* all of the object files in the archive. The reason for
     // this is as follows:
@@ -510,7 +509,7 @@ fn link_rlib<'a>(sess: &'a Session,
             // After adding all files to the archive, we need to update the
             // symbol table of the archive. This currently dies on OSX (see
             // #11162), and isn't necessary there anyway
-            if !sess.target.target.options.is_like_osx || ab.using_llvm() {
+            if !sess.target.target.options.is_like_osx {
                 ab.update_symbols();
             }
         }
@@ -570,9 +569,6 @@ fn write_rlib_bytecode_object_v1(writer: &mut Write,
 fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
                   tempdir: &Path) {
     let mut ab = link_rlib(sess, None, objects, out_filename, tempdir);
-    if sess.target.target.options.is_like_osx && !ab.using_llvm() {
-        ab.build();
-    }
     if !sess.target.target.options.no_compiler_rt {
         ab.add_native_library("compiler-rt");
     }
@@ -621,8 +617,8 @@ fn link_natively(sess: &Session,
     info!("preparing {:?} from {:?} to {:?}", crate_type, objects, out_filename);
 
     // The invocations of cc share some flags across platforms
-    let (pname, mut cmd) = get_linker(sess);
-    cmd.env("PATH", command_path(sess));
+    let (pname, mut cmd, extra) = get_linker(sess);
+    cmd.env("PATH", command_path(sess, extra));
 
     let root = sess.target_filesearch(PathKind::Native).get_lib_path();
     cmd.args(&sess.target.target.options.pre_link_args);
@@ -637,13 +633,9 @@ fn link_natively(sess: &Session,
     }
 
     {
-        let mut linker = if sess.target.target.options.is_like_msvc {
-            Box::new(MsvcLinker { cmd: &mut cmd, sess: &sess }) as Box<Linker>
-        } else {
-            Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box<Linker>
-        };
+        let mut linker = trans.linker_info.to_linker(&mut cmd, &sess);
         link_args(&mut *linker, sess, crate_type, tmpdir,
-                  objects, out_filename, trans, outputs);
+                  objects, out_filename, outputs);
         if !sess.target.target.options.no_compiler_rt {
             linker.link_staticlib("compiler-rt");
         }
@@ -691,7 +683,16 @@ fn link_natively(sess: &Session,
             info!("linker stdout:\n{}", escape_string(&prog.stdout[..]));
         },
         Err(e) => {
-            sess.fatal(&format!("could not exec the linker `{}`: {}", pname, e));
+            sess.struct_err(&format!("could not exec the linker `{}`: {}", pname, e))
+                .note(&format!("{:?}", &cmd))
+                .emit();
+            if sess.target.target.options.is_like_msvc && e.kind() == io::ErrorKind::NotFound {
+                sess.note_without_error("the msvc targets depend on the msvc linker \
+                    but `link.exe` was not found");
+                sess.note_without_error("please ensure that VS 2013 or VS 2015 was installed \
+                    with the Visual C++ option");
+            }
+            sess.abort_if_errors();
         }
     }
 
@@ -712,7 +713,6 @@ fn link_args(cmd: &mut Linker,
              tmpdir: &Path,
              objects: &[PathBuf],
              out_filename: &Path,
-             trans: &CrateTranslation,
              outputs: &OutputFilenames) {
 
     // The default library location, we need this to find the runtime.
@@ -731,7 +731,7 @@ fn link_args(cmd: &mut Linker,
     // If we're building a dynamic library then some platforms need to make sure
     // that all symbols are exported correctly from the dynamic library.
     if crate_type != config::CrateTypeExecutable {
-        cmd.export_symbols(sess, trans, tmpdir, crate_type);
+        cmd.export_symbols(tmpdir, crate_type);
     }
 
     // When linking a dynamic library, we put the metadata into a section of the
index 50f6366e85c8680f8a9608146b99d190e729ec4e..cb990ead8e81e94ae0de9f5982d8817517e2e9d6 100644 (file)
@@ -15,13 +15,50 @@ use std::io::prelude::*;
 use std::path::{Path, PathBuf};
 use std::process::Command;
 
+use context::SharedCrateContext;
+use monomorphize::Instance;
+
 use back::archive;
 use middle::dependency_format::Linkage;
 use session::Session;
 use session::config::CrateType;
 use session::config;
 use syntax::ast;
-use CrateTranslation;
+
+/// For all the linkers we support, and information they might
+/// need out of the shared crate context before we get rid of it.
+pub struct LinkerInfo {
+    dylib_exports: Vec<String>,
+    cdylib_exports: Vec<String>
+}
+
+impl<'a, 'tcx> LinkerInfo {
+    pub fn new(scx: &SharedCrateContext<'a, 'tcx>,
+               reachable: &[String]) -> LinkerInfo {
+        LinkerInfo {
+            dylib_exports: exported_symbols(scx, reachable, CrateType::CrateTypeDylib),
+            cdylib_exports: exported_symbols(scx, reachable, CrateType::CrateTypeCdylib)
+        }
+    }
+
+    pub fn to_linker(&'a self,
+                     cmd: &'a mut Command,
+                     sess: &'a Session) -> Box<Linker+'a> {
+        if sess.target.target.options.is_like_msvc {
+            Box::new(MsvcLinker {
+                cmd: cmd,
+                sess: sess,
+                info: self
+            }) as Box<Linker>
+        } else {
+            Box::new(GnuLinker {
+                cmd: cmd,
+                sess: sess,
+                info: self
+            }) as Box<Linker>
+        }
+    }
+}
 
 /// Linker abstraction used by back::link to build up the command to invoke a
 /// linker.
@@ -53,16 +90,13 @@ pub trait Linker {
     fn hint_dynamic(&mut self);
     fn whole_archives(&mut self);
     fn no_whole_archives(&mut self);
-    fn export_symbols(&mut self,
-                      sess: &Session,
-                      trans: &CrateTranslation,
-                      tmpdir: &Path,
-                      crate_type: CrateType);
+    fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType);
 }
 
 pub struct GnuLinker<'a> {
-    pub cmd: &'a mut Command,
-    pub sess: &'a Session,
+    cmd: &'a mut Command,
+    sess: &'a Session,
+    info: &'a LinkerInfo
 }
 
 impl<'a> GnuLinker<'a> {
@@ -201,11 +235,7 @@ impl<'a> Linker for GnuLinker<'a> {
         self.cmd.arg("-Wl,-Bdynamic");
     }
 
-    fn export_symbols(&mut self,
-                      sess: &Session,
-                      trans: &CrateTranslation,
-                      tmpdir: &Path,
-                      crate_type: CrateType) {
+    fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) {
         // If we're compiling a dylib, then we let symbol visibility in object
         // files to take care of whether they're exported or not.
         //
@@ -225,13 +255,13 @@ impl<'a> Linker for GnuLinker<'a> {
         };
         let res = (|| -> io::Result<()> {
             let mut f = BufWriter::new(File::create(&path)?);
-            for sym in exported_symbols(sess, trans, crate_type) {
+            for sym in &self.info.cdylib_exports {
                 writeln!(f, "{}{}", prefix, sym)?;
             }
             Ok(())
         })();
         if let Err(e) = res {
-            sess.fatal(&format!("failed to write lib.def file: {}", e));
+            self.sess.fatal(&format!("failed to write lib.def file: {}", e));
         }
         let mut arg = OsString::new();
         if self.sess.target.target.options.is_like_osx {
@@ -245,8 +275,9 @@ impl<'a> Linker for GnuLinker<'a> {
 }
 
 pub struct MsvcLinker<'a> {
-    pub cmd: &'a mut Command,
-    pub sess: &'a Session,
+    cmd: &'a mut Command,
+    sess: &'a Session,
+    info: &'a LinkerInfo
 }
 
 impl<'a> Linker for MsvcLinker<'a> {
@@ -366,8 +397,6 @@ impl<'a> Linker for MsvcLinker<'a> {
     // in which case they may continue to transitively be used and hence need
     // their symbols exported.
     fn export_symbols(&mut self,
-                      sess: &Session,
-                      trans: &CrateTranslation,
                       tmpdir: &Path,
                       crate_type: CrateType) {
         let path = tmpdir.join("lib.def");
@@ -378,15 +407,18 @@ impl<'a> Linker for MsvcLinker<'a> {
             // straight to exports.
             writeln!(f, "LIBRARY")?;
             writeln!(f, "EXPORTS")?;
-
-            for sym in exported_symbols(sess, trans, crate_type) {
-                writeln!(f, "  {}", sym)?;
+            let symbols = if crate_type == CrateType::CrateTypeCdylib {
+                &self.info.cdylib_exports
+            } else {
+                &self.info.dylib_exports
+            };
+            for symbol in symbols {
+                writeln!(f, "  {}", symbol)?;
             }
-
             Ok(())
         })();
         if let Err(e) = res {
-            sess.fatal(&format!("failed to write lib.def file: {}", e));
+            self.sess.fatal(&format!("failed to write lib.def file: {}", e));
         }
         let mut arg = OsString::from("/DEF:");
         arg.push(path);
@@ -394,10 +426,23 @@ impl<'a> Linker for MsvcLinker<'a> {
     }
 }
 
-fn exported_symbols(sess: &Session,
-                    trans: &CrateTranslation,
-                    crate_type: CrateType) -> Vec<String> {
-    let mut symbols = trans.reachable.iter().cloned().collect::<Vec<_>>();
+fn exported_symbols(scx: &SharedCrateContext,
+                    reachable: &[String],
+                    crate_type: CrateType)
+                    -> Vec<String> {
+    if !scx.sess().crate_types.borrow().contains(&crate_type) {
+        return vec![];
+    }
+
+    // See explanation in GnuLinker::export_symbols, for
+    // why we don't ever need dylib symbols on non-MSVC.
+    if crate_type == CrateType::CrateTypeDylib {
+        if !scx.sess().target.target.options.is_like_msvc {
+            return vec![];
+        }
+    }
+
+    let mut symbols = reachable.to_vec();
 
     // If we're producing anything other than a dylib then the `reachable` array
     // above is the exhaustive set of symbols we should be exporting.
@@ -409,10 +454,10 @@ fn exported_symbols(sess: &Session,
         return symbols
     }
 
-    let cstore = &sess.cstore;
-    let formats = sess.dependency_formats.borrow();
-    let upstream_symbols = formats[&crate_type].iter();
-    symbols.extend(upstream_symbols.enumerate().filter_map(|(i, f)| {
+    let cstore = &scx.sess().cstore;
+    let formats = scx.sess().dependency_formats.borrow();
+    let deps = formats[&crate_type].iter();
+    symbols.extend(deps.enumerate().filter_map(|(i, f)| {
         if *f == Linkage::Static {
             Some((i + 1) as ast::CrateNum)
         } else {
@@ -420,9 +465,8 @@ fn exported_symbols(sess: &Session,
         }
     }).flat_map(|cnum| {
         cstore.reachable_ids(cnum)
-    }).map(|did| {
-        cstore.item_symbol(did)
+    }).map(|did| -> String {
+        Instance::mono(scx, did).symbol_name(scx)
     }));
-
-    return symbols
+    symbols
 }
diff --git a/src/librustc_trans/back/msvc/arch.rs b/src/librustc_trans/back/msvc/arch.rs
new file mode 100644 (file)
index 0000000..c10312a
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(non_camel_case_types, non_snake_case)]
+
+use libc::c_void;
+use std::mem;
+
+type DWORD = u32;
+type WORD = u16;
+type LPVOID = *mut c_void;
+type DWORD_PTR = usize;
+
+const PROCESSOR_ARCHITECTURE_INTEL: WORD = 0;
+const PROCESSOR_ARCHITECTURE_AMD64: WORD = 9;
+
+#[repr(C)]
+struct SYSTEM_INFO {
+    wProcessorArchitecture: WORD,
+    _wReserved: WORD,
+    _dwPageSize: DWORD,
+    _lpMinimumApplicationAddress: LPVOID,
+    _lpMaximumApplicationAddress: LPVOID,
+    _dwActiveProcessorMask: DWORD_PTR,
+    _dwNumberOfProcessors: DWORD,
+    _dwProcessorType: DWORD,
+    _dwAllocationGranularity: DWORD,
+    _wProcessorLevel: WORD,
+    _wProcessorRevision: WORD,
+}
+
+extern "system" {
+    fn GetNativeSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
+}
+
+pub enum Arch {
+    X86,
+    Amd64,
+}
+
+pub fn host_arch() -> Option<Arch> {
+    let mut info = unsafe { mem::zeroed() };
+    unsafe { GetNativeSystemInfo(&mut info) };
+    match info.wProcessorArchitecture {
+        PROCESSOR_ARCHITECTURE_INTEL => Some(Arch::X86),
+        PROCESSOR_ARCHITECTURE_AMD64 => Some(Arch::Amd64),
+        _ => None,
+    }
+}
index 0112da57cc0a6bed711e7ee16618b85b9dc1b406..16aef6ee8ca3543047ba07c85d867f9eae1de881 100644 (file)
 //! paths/files is based on Microsoft's logic in their vcvars bat files, but
 //! comments can also be found below leading through the various code paths.
 
+// A simple macro to make this option mess easier to read
+macro_rules! otry {
+    ($expr:expr) => (match $expr {
+        Some(val) => val,
+        None => return None,
+    })
+}
+
 #[cfg(windows)]
 mod registry;
+#[cfg(windows)]
+mod arch;
 
 #[cfg(windows)]
 mod platform {
@@ -42,111 +52,134 @@ mod platform {
     use std::path::{Path, PathBuf};
     use std::process::Command;
     use session::Session;
-    use super::registry::{LOCAL_MACHINE};
+    use super::arch::{host_arch, Arch};
+    use super::registry::LOCAL_MACHINE;
 
-    // Cross toolchains depend on dlls from the host toolchain
-    // We can't just add it to the Command's PATH in `link_exe_cmd` because it
-    // is later overridden so we publicly expose it here instead
-    pub fn host_dll_path() -> Option<PathBuf> {
-        get_vc_dir().and_then(|(_, vcdir)| {
-            host_dll_subdir().map(|sub| {
-                vcdir.join("bin").join(sub)
+    // First we need to figure out whether the environment is already correctly
+    // configured by vcvars. We do this by looking at the environment variable
+    // `VCINSTALLDIR` which is always set by vcvars, and unlikely to be set
+    // otherwise. If it is defined, then we find `link.exe` in `PATH and trust
+    // that everything else is configured correctly.
+    //
+    // If `VCINSTALLDIR` wasn't defined (or we couldn't find the linker where
+    // it claimed it should be), then we resort to finding everything
+    // ourselves. First we find where the latest version of MSVC is installed
+    // and what version it is. Then based on the version we find the
+    // appropriate SDKs.
+    //
+    // If despite our best efforts we are still unable to find MSVC then we
+    // just blindly call `link.exe` and hope for the best.
+    //
+    // This code only supports VC 11 through 15. For versions older than that
+    // the user will need to manually execute the appropriate vcvars bat file
+    // and it should hopefully work.
+    //
+    // The second member of the tuple we return is the directory for the host
+    // linker toolchain, which is necessary when using the cross linkers.
+    pub fn link_exe_cmd(sess: &Session) -> (Command, Option<PathBuf>) {
+        let arch = &sess.target.target.arch;
+        env::var_os("VCINSTALLDIR").and_then(|_| {
+            debug!("Detected that vcvars was already run.");
+            let path = otry!(env::var_os("PATH"));
+            // Mingw has its own link which is not the link we want so we
+            // look for `cl.exe` too as a precaution.
+            env::split_paths(&path).find(|path| {
+                path.join("cl.exe").is_file()
+                    && path.join("link.exe").is_file()
+            }).map(|path| {
+                (Command::new(path.join("link.exe")), None)
             })
+        }).or_else(|| {
+            None.or_else(|| {
+                find_msvc_latest(arch, "15.0")
+            }).or_else(|| {
+                find_msvc_latest(arch, "14.0")
+            }).or_else(|| {
+                find_msvc_12(arch)
+            }).or_else(|| {
+                find_msvc_11(arch)
+            }).map(|(cmd, path)| (cmd, Some(path)))
+        }).unwrap_or_else(|| {
+            debug!("Failed to locate linker.");
+            (Command::new("link.exe"), None)
         })
     }
 
-    pub fn link_exe_cmd(sess: &Session) -> Command {
-        let arch = &sess.target.target.arch;
-        let (binsub, libsub, vclibsub) =
-            match (bin_subdir(arch), lib_subdir(arch), vc_lib_subdir(arch)) {
-            (Some(x), Some(y), Some(z)) => (x, y, z),
-            _ => return Command::new("link.exe"),
-        };
+    // For MSVC 14 or newer we need to find the Universal CRT as well as either
+    // the Windows 10 SDK or Windows 8.1 SDK.
+    fn find_msvc_latest(arch: &str, ver: &str) -> Option<(Command, PathBuf)> {
+        let vcdir = otry!(get_vc_dir(ver));
+        let (mut cmd, host) = otry!(get_linker(&vcdir, arch));
+        let sub = otry!(lib_subdir(arch));
+        let ucrt = otry!(get_ucrt_dir());
+        debug!("Found Universal CRT {:?}", ucrt);
+        add_lib(&mut cmd, &ucrt.join("ucrt").join(sub));
+        if let Some(dir) = get_sdk10_dir() {
+            debug!("Found Win10 SDK {:?}", dir);
+            add_lib(&mut cmd, &dir.join("um").join(sub));
+        } else if let Some(dir) = get_sdk81_dir() {
+            debug!("Found Win8.1 SDK {:?}", dir);
+            add_lib(&mut cmd, &dir.join("um").join(sub));
+        } else {
+            return None
+        }
+        Some((cmd, host))
+    }
 
-        // First we need to figure out whether the environment is already correctly
-        // configured by vcvars. We do this by looking at the environment variable
-        // `VCINSTALLDIR` which is always set by vcvars, and unlikely to be set
-        // otherwise. If it is defined, then we derive the path to `link.exe` from
-        // that and trust that everything else is configured correctly.
-        //
-        // If `VCINSTALLDIR` wasn't defined (or we couldn't find the linker where it
-        // claimed it should be), then we resort to finding everything ourselves.
-        // First we find where the latest version of MSVC is installed and what
-        // version it is. Then based on the version we find the appropriate SDKs.
-        //
-        // For MSVC 14 (VS 2015) we look for the Win10 SDK and failing that we look
-        // for the Win8.1 SDK. We also look for the Universal CRT.
-        //
-        // For MSVC 12 (VS 2013) we look for the Win8.1 SDK.
-        //
-        // For MSVC 11 (VS 2012) we look for the Win8 SDK.
-        //
-        // For all other versions the user has to execute the appropriate vcvars bat
-        // file themselves to configure the environment.
-        //
-        // If despite our best efforts we are still unable to find MSVC then we just
-        // blindly call `link.exe` and hope for the best.
-        return env::var_os("VCINSTALLDIR").and_then(|dir| {
-            debug!("Environment already configured by user. Assuming it works.");
-            let mut p = PathBuf::from(dir);
-            p.push("bin");
-            p.push(binsub);
-            p.push("link.exe");
-            if !p.is_file() { return None }
-            Some(Command::new(p))
-        }).or_else(|| {
-            get_vc_dir().and_then(|(ver, vcdir)| {
-                debug!("Found VC installation directory {:?}", vcdir);
-                let linker = vcdir.clone().join("bin").join(binsub).join("link.exe");
-                if !linker.is_file() { return None }
-                let mut cmd = Command::new(linker);
-                add_lib(&mut cmd, &vcdir.join("lib").join(vclibsub));
-                if ver == "14.0" {
-                    if let Some(dir) = get_ucrt_dir() {
-                        debug!("Found Universal CRT {:?}", dir);
-                        add_lib(&mut cmd, &dir.join("ucrt").join(libsub));
-                    }
-                    if let Some(dir) = get_sdk10_dir() {
-                        debug!("Found Win10 SDK {:?}", dir);
-                        add_lib(&mut cmd, &dir.join("um").join(libsub));
-                    } else if let Some(dir) = get_sdk81_dir() {
-                        debug!("Found Win8.1 SDK {:?}", dir);
-                        add_lib(&mut cmd, &dir.join("um").join(libsub));
-                    }
-                } else if ver == "12.0" {
-                    if let Some(dir) = get_sdk81_dir() {
-                        debug!("Found Win8.1 SDK {:?}", dir);
-                        add_lib(&mut cmd, &dir.join("um").join(libsub));
-                    }
-                } else { // ver == "11.0"
-                    if let Some(dir) = get_sdk8_dir() {
-                        debug!("Found Win8 SDK {:?}", dir);
-                        add_lib(&mut cmd, &dir.join("um").join(libsub));
-                    }
-                }
-                Some(cmd)
-            })
-        }).unwrap_or_else(|| {
-            debug!("Failed to locate linker.");
-            Command::new("link.exe")
-        });
+    // For MSVC 12 we need to find the Windows 8.1 SDK.
+    fn find_msvc_12(arch: &str) -> Option<(Command, PathBuf)> {
+        let vcdir = otry!(get_vc_dir("12.0"));
+        let (mut cmd, host) = otry!(get_linker(&vcdir, arch));
+        let sub = otry!(lib_subdir(arch));
+        let sdk81 = otry!(get_sdk81_dir());
+        debug!("Found Win8.1 SDK {:?}", sdk81);
+        add_lib(&mut cmd, &sdk81.join("um").join(sub));
+        Some((cmd, host))
+    }
+
+    // For MSVC 11 we need to find the Windows 8 SDK.
+    fn find_msvc_11(arch: &str) -> Option<(Command, PathBuf)> {
+        let vcdir = otry!(get_vc_dir("11.0"));
+        let (mut cmd, host) = otry!(get_linker(&vcdir, arch));
+        let sub = otry!(lib_subdir(arch));
+        let sdk8 = otry!(get_sdk8_dir());
+        debug!("Found Win8 SDK {:?}", sdk8);
+        add_lib(&mut cmd, &sdk8.join("um").join(sub));
+        Some((cmd, host))
     }
-    // A convenience function to make the above code simpler
+
+    // A convenience function to append library paths.
     fn add_lib(cmd: &mut Command, lib: &Path) {
         let mut arg: OsString = "/LIBPATH:".into();
         arg.push(lib);
         cmd.arg(arg);
     }
 
-    // To find MSVC we look in a specific registry key for the newest of the
-    // three versions that we support.
-    fn get_vc_dir() -> Option<(&'static str, PathBuf)> {
-        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7".as_ref())
-        .ok().and_then(|key| {
-            ["14.0", "12.0", "11.0"].iter().filter_map(|ver| {
-                key.query_str(ver).ok().map(|p| (*ver, p.into()))
-            }).next()
-        })
+    // Given a possible MSVC installation directory, we look for the linker and
+    // then add the MSVC library path.
+    fn get_linker(path: &Path, arch: &str) -> Option<(Command, PathBuf)> {
+        debug!("Looking for linker in {:?}", path);
+        bin_subdir(arch).into_iter().map(|(sub, host)| {
+            (path.join("bin").join(sub).join("link.exe"),
+             path.join("bin").join(host))
+        }).filter(|&(ref path, _)| {
+            path.is_file()
+        }).map(|(path, host)| {
+            (Command::new(path), host)
+        }).filter_map(|(mut cmd, host)| {
+            let sub = otry!(vc_lib_subdir(arch));
+            add_lib(&mut cmd, &path.join("lib").join(sub));
+            Some((cmd, host))
+        }).next()
+    }
+
+    // To find MSVC we look in a specific registry key for the version we are
+    // trying to find.
+    fn get_vc_dir(ver: &str) -> Option<PathBuf> {
+        let key = otry!(LOCAL_MACHINE
+            .open(r"SOFTWARE\Microsoft\VisualStudio\SxS\VC7".as_ref()).ok());
+        let path = otry!(key.query_str(ver).ok());
+        Some(path.into())
     }
 
     // To find the Universal CRT we look in a specific registry key for where
@@ -154,46 +187,42 @@ mod platform {
     // find the newest version. While this sort of sorting isn't ideal,  it is
     // what vcvars does so that's good enough for us.
     fn get_ucrt_dir() -> Option<PathBuf> {
-        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Windows Kits\Installed Roots".as_ref())
-        .ok().and_then(|key| {
-            key.query_str("KitsRoot10").ok()
-        }).and_then(|root| {
-            fs::read_dir(Path::new(&root).join("Lib")).ok()
-        }).and_then(|readdir| {
-            let mut dirs: Vec<_> = readdir.filter_map(|dir| {
-                dir.ok()
-            }).map(|dir| {
-                dir.path()
-            }).filter(|dir| {
-                dir.components().last().and_then(|c| {
-                    c.as_os_str().to_str()
-                }).map(|c| c.starts_with("10.")).unwrap_or(false)
-            }).collect();
-            dirs.sort();
-            dirs.pop()
-        })
+        let key = otry!(LOCAL_MACHINE
+            .open(r"SOFTWARE\Microsoft\Windows Kits\Installed Roots".as_ref()).ok());
+        let root = otry!(key.query_str("KitsRoot10").ok());
+        let readdir = otry!(fs::read_dir(Path::new(&root).join("lib")).ok());
+        readdir.filter_map(|dir| {
+            dir.ok()
+        }).map(|dir| {
+            dir.path()
+        }).filter(|dir| {
+            dir.components().last().and_then(|c| {
+                c.as_os_str().to_str()
+            }).map(|c| {
+                c.starts_with("10.") && dir.join("ucrt").is_dir()
+            }).unwrap_or(false)
+        }).max()
     }
 
     // Vcvars finds the correct version of the Windows 10 SDK by looking
-    // for the include um/Windows.h because sometimes a given version will
+    // for the include `um\Windows.h` because sometimes a given version will
     // only have UCRT bits without the rest of the SDK. Since we only care about
-    // libraries and not includes, we just look for the folder `um` in the lib
-    // section. Like we do for the Universal CRT, we sort the possibilities
+    // libraries and not includes, we instead look for `um\x64\kernel32.lib`.
+    // Since the 32-bit and 64-bit libraries are always installed together we
+    // only need to bother checking x64, making this code a tiny bit simpler.
+    // Like we do for the Universal CRT, we sort the possibilities
     // asciibetically to find the newest one as that is what vcvars does.
     fn get_sdk10_dir() -> Option<PathBuf> {
-        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0".as_ref())
-        .ok().and_then(|key| {
-            key.query_str("InstallationFolder").ok()
-        }).and_then(|root| {
-            fs::read_dir(Path::new(&root).join("lib")).ok()
-        }).and_then(|readdir| {
-            let mut dirs: Vec<_> = readdir.filter_map(|dir| dir.ok())
-                .map(|dir| dir.path()).collect();
-            dirs.sort();
-            dirs.into_iter().rev().filter(|dir| {
-                dir.join("um").is_dir()
-            }).next()
-        })
+        let key = otry!(LOCAL_MACHINE
+            .open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0".as_ref()).ok());
+        let root = otry!(key.query_str("InstallationFolder").ok());
+        let readdir = otry!(fs::read_dir(Path::new(&root).join("lib")).ok());
+        let mut dirs: Vec<_> = readdir.filter_map(|dir| dir.ok())
+            .map(|dir| dir.path()).collect();
+        dirs.sort();
+        dirs.into_iter().rev().filter(|dir| {
+            dir.join("um").join("x64").join("kernel32.lib").is_file()
+        }).next()
     }
 
     // Interestingly there are several subdirectories, `win7` `win8` and
@@ -201,21 +230,17 @@ mod platform {
     // applies to us. Note that if we were targetting kernel mode drivers
     // instead of user mode applications, we would care.
     fn get_sdk81_dir() -> Option<PathBuf> {
-        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1".as_ref())
-        .ok().and_then(|key| {
-            key.query_str("InstallationFolder").ok()
-        }).map(|root| {
-            Path::new(&root).join("lib").join("winv6.3")
-        })
+        let key = otry!(LOCAL_MACHINE
+            .open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1".as_ref()).ok());
+        let root = otry!(key.query_str("InstallationFolder").ok());
+        Some(Path::new(&root).join("lib").join("winv6.3"))
     }
 
     fn get_sdk8_dir() -> Option<PathBuf> {
-        LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0".as_ref())
-        .ok().and_then(|key| {
-            key.query_str("InstallationFolder").ok()
-        }).map(|root| {
-            Path::new(&root).join("lib").join("win8")
-        })
+        let key = otry!(LOCAL_MACHINE
+            .open(r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0".as_ref()).ok());
+        let root = otry!(key.query_str("InstallationFolder").ok());
+        Some(Path::new(&root).join("lib").join("win8"))
     }
 
     // When choosing the linker toolchain to use, we have to choose the one
@@ -223,31 +248,27 @@ mod platform {
     // where someone on 32-bit Windows is trying to cross compile to 64-bit and
     // it tries to invoke the native 64-bit linker which won't work.
     //
-    // FIXME - This currently functions based on the host architecture of rustc
-    // itself but it should instead detect the bitness of the OS itself.
+    // For the return value of this function, the first member of the tuple is
+    // the folder of the linker we will be invoking, while the second member
+    // is the folder of the host toolchain for that linker which is essential
+    // when using a cross linker. We return a Vec since on x64 there are often
+    // two linkers that can target the architecture we desire. The 64-bit host
+    // linker is preferred, and hence first, due to 64-bit allowing it more
+    // address space to work with and potentially being faster.
     //
     // FIXME - Figure out what happens when the host architecture is arm.
-    //
-    // FIXME - Some versions of MSVC may not come with all these toolchains.
-    // Consider returning an array of toolchains and trying them one at a time
-    // until the linker is found.
-    fn bin_subdir(arch: &str) -> Option<&'static str> {
-        if cfg!(target_arch = "x86_64") {
-            match arch {
-                "x86" => Some("amd64_x86"),
-                "x86_64" => Some("amd64"),
-                "arm" => Some("amd64_arm"),
-                _ => None,
-            }
-        } else if cfg!(target_arch = "x86") {
-            match arch {
-                "x86" => Some(""),
-                "x86_64" => Some("x86_amd64"),
-                "arm" => Some("x86_arm"),
-                _ => None,
-            }
-        } else { None }
+    fn bin_subdir(arch: &str) -> Vec<(&'static str, &'static str)> {
+        match (arch, host_arch()) {
+            ("x86", Some(Arch::X86)) => vec![("", "")],
+            ("x86", Some(Arch::Amd64)) => vec![("amd64_x86", "amd64"), ("", "")],
+            ("x86_64", Some(Arch::X86)) => vec![("x86_amd64", "")],
+            ("x86_64", Some(Arch::Amd64)) => vec![("amd64", "amd64"), ("x86_amd64", "")],
+            ("arm", Some(Arch::X86)) => vec![("x86_arm", "")],
+            ("arm", Some(Arch::Amd64)) => vec![("amd64_arm", "amd64"), ("x86_arm", "")],
+            _ => vec![],
+        }
     }
+
     fn lib_subdir(arch: &str) -> Option<&'static str> {
         match arch {
             "x86" => Some("x86"),
@@ -256,6 +277,7 @@ mod platform {
             _ => None,
         }
     }
+
     // MSVC's x86 libraries are not in a subfolder
     fn vc_lib_subdir(arch: &str) -> Option<&'static str> {
         match arch {
@@ -265,11 +287,6 @@ mod platform {
             _ => None,
         }
     }
-    fn host_dll_subdir() -> Option<&'static str> {
-        if cfg!(target_arch = "x86_64") { Some("amd64") }
-        else if cfg!(target_arch = "x86") { Some("") }
-        else { None }
-    }
 }
 
 // If we're not on Windows, then there's no registry to search through and MSVC
@@ -279,9 +296,9 @@ mod platform {
     use std::path::PathBuf;
     use std::process::Command;
     use session::Session;
-    pub fn link_exe_cmd(_sess: &Session) -> Command {
-        Command::new("link.exe")
+    pub fn link_exe_cmd(_sess: &Session) -> (Command, Option<PathBuf>) {
+        (Command::new("link.exe"), None)
     }
-    pub fn host_dll_path() -> Option<PathBuf> { None }
 }
+
 pub use self::platform::*;
index 0cf82d66b2b682add653f9340e784bdf517739e8..170c8f75b5056c5ab9054bd7562623a197fabea4 100644 (file)
 //! virtually impossible. Thus, symbol hash generation exclusively relies on
 //! DefPaths which are much more robust in the face of changes to the code base.
 
-use common::{CrateContext, gensym_name};
+use common::{CrateContext, SharedCrateContext, gensym_name};
 use monomorphize::Instance;
 use util::sha2::{Digest, Sha256};
 
-use rustc::middle::cstore;
+use rustc::middle::{cstore, weak_lang_items};
 use rustc::hir::def_id::DefId;
+use rustc::hir::map as hir_map;
 use rustc::ty::{self, TyCtxt, TypeFoldable};
-use rustc::ty::item_path::{ItemPathBuffer, RootMode};
+use rustc::ty::item_path::{self, ItemPathBuffer, RootMode};
 use rustc::hir::map::definitions::{DefPath, DefPathData};
 
 use std::fmt::Write;
+use syntax::attr;
 use syntax::parse::token::{self, InternedString};
 use serialize::hex::ToHex;
 
@@ -116,10 +118,14 @@ pub fn def_id_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) ->
     def_path_to_string(tcx, &def_path)
 }
 
-pub fn def_path_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_path: &DefPath) -> String {
+fn def_path_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_path: &DefPath) -> String {
     let mut s = String::with_capacity(def_path.data.len() * 16);
 
-    s.push_str(&tcx.crate_name(def_path.krate));
+    if def_path.krate == cstore::LOCAL_CRATE {
+        s.push_str(&tcx.crate_name(def_path.krate));
+    } else {
+        s.push_str(&tcx.sess.cstore.original_crate_name(def_path.krate));
+    }
     s.push_str("/");
     s.push_str(&tcx.crate_disambiguator(def_path.krate));
 
@@ -134,7 +140,7 @@ pub fn def_path_to_string<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_path: &DefP
     s
 }
 
-fn get_symbol_hash<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+fn get_symbol_hash<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
 
                              // path to the item this name is for
                              def_path: &DefPath,
@@ -152,9 +158,9 @@ fn get_symbol_hash<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     debug!("get_symbol_hash(def_path={:?}, parameters={:?})",
            def_path, parameters);
 
-    let tcx = ccx.tcx();
+    let tcx = scx.tcx();
 
-    let mut hash_state = ccx.symbol_hasher().borrow_mut();
+    let mut hash_state = scx.symbol_hasher().borrow_mut();
 
     hash_state.reset();
 
@@ -187,66 +193,100 @@ fn get_symbol_hash<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     }
 }
 
-fn exported_name_with_opt_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                           instance: &Instance<'tcx>,
-                                           suffix: Option<&str>)
-                                           -> String {
-    let &Instance { def: mut def_id, ref substs } = instance;
+impl<'a, 'tcx> Instance<'tcx> {
+    pub fn symbol_name(self, scx: &SharedCrateContext<'a, 'tcx>) -> String {
+        let Instance { def: def_id, ref substs } = self;
+
+        debug!("symbol_name(def_id={:?}, substs={:?})",
+               def_id, substs);
 
-    debug!("exported_name_with_opt_suffix(def_id={:?}, substs={:?}, suffix={:?})",
-           def_id, substs, suffix);
+        let node_id = scx.tcx().map.as_local_node_id(def_id);
 
-    if let Some(node_id) = ccx.tcx().map.as_local_node_id(def_id) {
-        if let Some(&src_def_id) = ccx.external_srcs().borrow().get(&node_id) {
-            def_id = src_def_id;
+        if let Some(id) = node_id {
+            if scx.sess().plugin_registrar_fn.get() == Some(id) {
+                let svh = &scx.link_meta().crate_hash;
+                let idx = def_id.index;
+                return scx.sess().generate_plugin_registrar_symbol(svh, idx);
+            }
         }
-    }
 
-    let def_path = ccx.tcx().def_path(def_id);
-    assert_eq!(def_path.krate, def_id.krate);
-
-    // We want to compute the "type" of this item. Unfortunately, some
-    // kinds of items (e.g., closures) don't have an entry in the
-    // item-type array. So walk back up the find the closest parent
-    // that DOES have an entry.
-    let mut ty_def_id = def_id;
-    let instance_ty;
-    loop {
-        let key = ccx.tcx().def_key(ty_def_id);
-        match key.disambiguated_data.data {
-            DefPathData::TypeNs(_) |
-            DefPathData::ValueNs(_) => {
-                instance_ty = ccx.tcx().lookup_item_type(ty_def_id);
-                break;
+        // FIXME(eddyb) Precompute a custom symbol name based on attributes.
+        let attrs = scx.tcx().get_attrs(def_id);
+        let is_foreign = if let Some(id) = node_id {
+            match scx.tcx().map.get(id) {
+                hir_map::NodeForeignItem(_) => true,
+                _ => false
             }
-            _ => {
-                // if we're making a symbol for something, there ought
-                // to be a value or type-def or something in there
-                // *somewhere*
-                ty_def_id.index = key.parent.unwrap_or_else(|| {
-                    bug!("finding type for {:?}, encountered def-id {:?} with no \
-                         parent", def_id, ty_def_id);
-                });
+        } else {
+            scx.sess().cstore.is_foreign_item(def_id)
+        };
+
+        if let Some(name) = weak_lang_items::link_name(&attrs) {
+            return name.to_string();
+        }
+
+        if is_foreign {
+            if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") {
+                return name.to_string();
             }
+            // Don't mangle foreign items.
+            return scx.tcx().item_name(def_id).as_str().to_string();
         }
-    }
 
-    // Erase regions because they may not be deterministic when hashed
-    // and should not matter anyhow.
-    let instance_ty = ccx.tcx().erase_regions(&instance_ty.ty);
+        if let Some(name) = attr::find_export_name_attr(scx.sess().diagnostic(), &attrs) {
+            // Use provided name
+            return name.to_string();
+        }
 
-    let hash = get_symbol_hash(ccx, &def_path, instance_ty, substs.types.as_slice());
+        if attr::contains_name(&attrs, "no_mangle") {
+            // Don't mangle
+            return scx.tcx().item_name(def_id).as_str().to_string();
+        }
 
-    let mut buffer = SymbolPathBuffer {
-        names: Vec::with_capacity(def_path.data.len())
-    };
-    ccx.tcx().push_item_path(&mut buffer, def_id);
+        let def_path = scx.tcx().def_path(def_id);
+
+        // We want to compute the "type" of this item. Unfortunately, some
+        // kinds of items (e.g., closures) don't have an entry in the
+        // item-type array. So walk back up the find the closest parent
+        // that DOES have an entry.
+        let mut ty_def_id = def_id;
+        let instance_ty;
+        loop {
+            let key = scx.tcx().def_key(ty_def_id);
+            match key.disambiguated_data.data {
+                DefPathData::TypeNs(_) |
+                DefPathData::ValueNs(_) => {
+                    instance_ty = scx.tcx().lookup_item_type(ty_def_id);
+                    break;
+                }
+                _ => {
+                    // if we're making a symbol for something, there ought
+                    // to be a value or type-def or something in there
+                    // *somewhere*
+                    ty_def_id.index = key.parent.unwrap_or_else(|| {
+                        bug!("finding type for {:?}, encountered def-id {:?} with no \
+                             parent", def_id, ty_def_id);
+                    });
+                }
+            }
+        }
 
-    if let Some(suffix) = suffix {
-        buffer.push(suffix);
-    }
+        // Erase regions because they may not be deterministic when hashed
+        // and should not matter anyhow.
+        let instance_ty = scx.tcx().erase_regions(&instance_ty.ty);
+
+        let hash = get_symbol_hash(scx, &def_path, instance_ty, substs.types.as_slice());
+
+        let mut buffer = SymbolPathBuffer {
+            names: Vec::with_capacity(def_path.data.len())
+        };
 
-    mangle(buffer.names.into_iter(), Some(&hash[..]))
+        item_path::with_forced_absolute_paths(|| {
+            scx.tcx().push_item_path(&mut buffer, def_id);
+        });
+
+        mangle(buffer.names.into_iter(), Some(&hash[..]))
+    }
 }
 
 struct SymbolPathBuffer {
@@ -264,19 +304,6 @@ impl ItemPathBuffer for SymbolPathBuffer {
     }
 }
 
-pub fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                               instance: &Instance<'tcx>)
-                               -> String {
-    exported_name_with_opt_suffix(ccx, instance, None)
-}
-
-pub fn exported_name_with_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                           instance: &Instance<'tcx>,
-                                           suffix: &str)
-                                           -> String {
-   exported_name_with_opt_suffix(ccx, instance, Some(suffix))
-}
-
 /// Only symbols that are invisible outside their compilation unit should use a
 /// name generated by this function.
 pub fn internal_name_from_type_and_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
@@ -289,7 +316,7 @@ pub fn internal_name_from_type_and_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>
         data: vec![],
         krate: cstore::LOCAL_CRATE,
     };
-    let hash = get_symbol_hash(ccx, &def_path, t, &[]);
+    let hash = get_symbol_hash(ccx.shared(), &def_path, t, &[]);
     mangle(path.iter().cloned(), Some(&hash[..]))
 }
 
index cf81777be261d4895af8c5c4841bb7d3c7f686d8..ec20381d1890d1f55f5d6e543104503ac4542ae5 100644 (file)
@@ -19,9 +19,9 @@ use llvm::SMDiagnosticRef;
 use {CrateTranslation, ModuleTranslation};
 use util::common::time;
 use util::common::path2cstr;
-use syntax::codemap::MultiSpan;
-use syntax::errors::{self, Handler, Level, RenderSpan};
-use syntax::errors::emitter::CoreEmitter;
+use errors::{self, Handler, Level, RenderSpan};
+use errors::emitter::CoreEmitter;
+use syntax_pos::MultiSpan;
 
 use std::collections::HashMap;
 use std::ffi::{CStr, CString};
@@ -357,7 +357,7 @@ struct HandlerFreeVars<'a> {
 unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>,
                                                msg: &'b str,
                                                cookie: c_uint) {
-    use syntax::codemap::ExpnId;
+    use syntax_pos::ExpnId;
 
     match cgcx.lto_ctxt {
         Some((sess, _)) => {
@@ -616,11 +616,19 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
         }
     }
 
-    llvm::LLVMDisposeModule(llmod);
-    llvm::LLVMContextDispose(llcx);
     llvm::LLVMRustDisposeTargetMachine(tm);
 }
 
+
+pub fn cleanup_llvm(trans: &CrateTranslation) {
+    for module in trans.modules.iter() {
+        unsafe {
+            llvm::LLVMDisposeModule(module.llmod);
+            llvm::LLVMContextDispose(module.llcx);
+        }
+    }
+}
+
 pub fn run_passes(sess: &Session,
                   trans: &CrateTranslation,
                   output_types: &HashMap<OutputType, Option<PathBuf>>,
@@ -970,7 +978,7 @@ fn run_work_multithreaded(sess: &Session,
 }
 
 pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
-    let (pname, mut cmd) = get_linker(sess);
+    let (pname, mut cmd, _) = get_linker(sess);
 
     cmd.arg("-c").arg("-o").arg(&outputs.path(OutputType::Object))
                            .arg(&outputs.temp_path(OutputType::Assembly));
index d4f0786e9efd660d051ecc9088799ea095d2c303..590220f0c8b6478c172254f830e950fcf7804f31 100644 (file)
@@ -30,14 +30,14 @@ pub use self::ValueOrigin::*;
 use super::CrateTranslation;
 use super::ModuleTranslation;
 
-use back::{link, symbol_names};
+use back::link;
+use back::linker::LinkerInfo;
 use lint;
 use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param};
 use llvm;
 use rustc::cfg;
 use rustc::hir::def_id::DefId;
 use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
-use middle::weak_lang_items;
 use rustc::hir::pat_util::simple_name;
 use rustc::ty::subst::{self, Substs};
 use rustc::traits;
@@ -75,7 +75,6 @@ use debuginfo::{self, DebugLoc, ToDebugLoc};
 use declare;
 use expr;
 use glue;
-use inline;
 use machine;
 use machine::{llalign_of_min, llsize_of, llsize_of_real};
 use meth;
@@ -101,7 +100,7 @@ use std::cell::{Cell, RefCell};
 use std::collections::{HashMap, HashSet};
 use std::str;
 use std::{i8, i16, i32, i64};
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
 use syntax::parse::token::InternedString;
 use syntax::attr::AttrMetaMethods;
 use syntax::attr;
@@ -136,11 +135,8 @@ pub struct _InsnCtxt {
 impl Drop for _InsnCtxt {
     fn drop(&mut self) {
         TASK_LOCAL_INSN_KEY.with(|slot| {
-            match slot.borrow_mut().as_mut() {
-                Some(ctx) => {
-                    ctx.pop();
-                }
-                None => {}
+            if let Some(ctx) = slot.borrow_mut().as_mut() {
+                ctx.pop();
             }
         })
     }
@@ -769,12 +765,12 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                              rhs: ValueRef,
                                              rhs_t: Ty<'tcx>)
                                              -> Block<'blk, 'tcx> {
-    let (zero_text, overflow_text) = if divrem.node == hir::BiDiv {
-        ("attempted to divide by zero",
-         "attempted to divide with overflow")
+    use rustc_const_math::{ConstMathErr, Op};
+
+    let (zero_err, overflow_err) = if divrem.node == hir::BiDiv {
+        (ConstMathErr::DivisionByZero, ConstMathErr::Overflow(Op::Div))
     } else {
-        ("attempted remainder with a divisor of zero",
-         "attempted remainder with overflow")
+        (ConstMathErr::RemainderByZero, ConstMathErr::Overflow(Op::Rem))
     };
     let debug_loc = call_info.debug_loc();
 
@@ -802,7 +798,7 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
         }
     };
     let bcx = with_cond(cx, is_zero, |bcx| {
-        controlflow::trans_fail(bcx, call_info, InternedString::new(zero_text))
+        controlflow::trans_fail(bcx, call_info, InternedString::new(zero_err.description()))
     });
 
     // To quote LLVM's documentation for the sdiv instruction:
@@ -828,7 +824,8 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                               C_integral(llty, min, true),
                               debug_loc);
             with_cond(bcx, is_min, |bcx| {
-                controlflow::trans_fail(bcx, call_info, InternedString::new(overflow_text))
+                controlflow::trans_fail(bcx, call_info,
+                                        InternedString::new(overflow_err.description()))
             })
         })
     } else {
@@ -1409,19 +1406,17 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
     pub fn new(ccx: &'blk CrateContext<'blk, 'tcx>,
                llfndecl: ValueRef,
                fn_ty: FnType,
-               definition: Option<(Instance<'tcx>, &ty::FnSig<'tcx>, Abi)>,
+               definition: Option<(Instance<'tcx>, &ty::FnSig<'tcx>, Abi, ast::NodeId)>,
                block_arena: &'blk TypedArena<common::BlockS<'blk, 'tcx>>)
                -> FunctionContext<'blk, 'tcx> {
-        let (param_substs, def_id) = match definition {
-            Some((instance, _, _)) => {
+        let (param_substs, def_id, inlined_id) = match definition {
+            Some((instance, _, _, inlined_id)) => {
                 common::validate_substs(instance.substs);
-                (instance.substs, Some(instance.def))
+                (instance.substs, Some(instance.def), Some(inlined_id))
             }
-            None => (ccx.tcx().mk_substs(Substs::empty()), None)
+            None => (ccx.tcx().mk_substs(Substs::empty()), None, None)
         };
 
-        let inlined_did = def_id.and_then(|def_id| inline::get_local_instance(ccx, def_id));
-        let inlined_id = inlined_did.and_then(|id| ccx.tcx().map.as_local_node_id(id));
         let local_id = def_id.and_then(|id| ccx.tcx().map.as_local_node_id(id));
 
         debug!("FunctionContext::new({})",
@@ -1456,7 +1451,7 @@ impl<'blk, 'tcx> FunctionContext<'blk, 'tcx> {
         };
 
         let debug_context = if let (false, Some(definition)) = (no_debug, definition) {
-            let (instance, sig, abi) = definition;
+            let (instance, sig, abi, _) = definition;
             debuginfo::create_function_debug_context(ccx, instance, sig, abi, llfndecl)
         } else {
             debuginfo::empty_function_debug_context(ccx)
@@ -1843,13 +1838,20 @@ pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         attributes::emit_uwtable(llfndecl, true);
     }
 
-    debug!("trans_closure(..., {})", instance);
+    // this is an info! to allow collecting monomorphization statistics
+    // and to allow finding the last function before LLVM aborts from
+    // release builds.
+    info!("trans_closure(..., {})", instance);
 
     let fn_ty = FnType::new(ccx, abi, sig, &[]);
 
     let (arena, fcx): (TypedArena<_>, FunctionContext);
     arena = TypedArena::new();
-    fcx = FunctionContext::new(ccx, llfndecl, fn_ty, Some((instance, sig, abi)), &arena);
+    fcx = FunctionContext::new(ccx,
+                               llfndecl,
+                               fn_ty,
+                               Some((instance, sig, abi, inlined_id)),
+                               &arena);
 
     if fcx.mir.is_some() {
         return mir::trans_mir(&fcx);
@@ -2248,17 +2250,14 @@ pub fn update_linkage(ccx: &CrateContext,
 }
 
 fn set_global_section(ccx: &CrateContext, llval: ValueRef, i: &hir::Item) {
-    match attr::first_attr_value_str_by_name(&i.attrs, "link_section") {
-        Some(sect) => {
-            if contains_null(&sect) {
-                ccx.sess().fatal(&format!("Illegal null byte in link_section value: `{}`", &sect));
-            }
-            unsafe {
-                let buf = CString::new(sect.as_bytes()).unwrap();
-                llvm::LLVMSetSection(llval, buf.as_ptr());
-            }
-        },
-        None => ()
+    if let Some(sect) = attr::first_attr_value_str_by_name(&i.attrs, "link_section") {
+        if contains_null(&sect) {
+            ccx.sess().fatal(&format!("Illegal null byte in link_section value: `{}`", &sect));
+        }
+        unsafe {
+            let buf = CString::new(sect.as_bytes()).unwrap();
+            llvm::LLVMSetSection(llval, buf.as_ptr());
+        }
     }
 }
 
@@ -2345,15 +2344,6 @@ pub fn trans_item(ccx: &CrateContext, item: &hir::Item) {
             set_global_section(ccx, g, item);
             update_linkage(ccx, g, Some(item.id), OriginalTranslation);
         }
-        hir::ItemForeignMod(ref m) => {
-            if m.abi == Abi::RustIntrinsic || m.abi == Abi::PlatformIntrinsic {
-                return;
-            }
-            for fi in &m.items {
-                let lname = imported_name(fi.name, &fi.attrs).to_string();
-                ccx.item_symbols().borrow_mut().insert(fi.id, lname);
-            }
-        }
         _ => {}
     }
 }
@@ -2438,60 +2428,12 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
     }
 }
 
-pub fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                               instance: Instance<'tcx>,
-                               attrs: &[ast::Attribute])
-                               -> String {
-    let id = ccx.tcx().map.as_local_node_id(instance.def).unwrap();
-
-    match ccx.external_srcs().borrow().get(&id) {
-        Some(&did) => {
-            let sym = ccx.sess().cstore.item_symbol(did);
-            debug!("found item {} in other crate...", sym);
-            return sym;
-        }
-        None => {}
-    }
-
-    match attr::find_export_name_attr(ccx.sess().diagnostic(), attrs) {
-        // Use provided name
-        Some(name) => name.to_string(),
-        _ => {
-            if attr::contains_name(attrs, "no_mangle") {
-                // Don't mangle
-                ccx.tcx().map.name(id).as_str().to_string()
-            } else {
-                match weak_lang_items::link_name(attrs) {
-                    Some(name) => name.to_string(),
-                    None => {
-                        // Usual name mangling
-                        symbol_names::exported_name(ccx, &instance)
-                    }
-                }
-            }
-        }
-    }
-}
-
-pub fn imported_name(name: ast::Name, attrs: &[ast::Attribute]) -> InternedString {
-    match attr::first_attr_value_str_by_name(attrs, "link_name") {
-        Some(ln) => ln.clone(),
-        None => match weak_lang_items::link_name(attrs) {
-            Some(name) => name,
-            None => name.as_str(),
-        }
-    }
-}
-
 fn contains_null(s: &str) -> bool {
     s.bytes().any(|b| b == 0)
 }
 
-pub fn write_metadata<'a, 'tcx>(cx: &SharedCrateContext<'a, 'tcx>,
-                                krate: &hir::Crate,
-                                reachable: &NodeSet,
-                                mir_map: &MirMap<'tcx>)
-                                -> Vec<u8> {
+fn write_metadata(cx: &SharedCrateContext,
+                  reachable_ids: &NodeSet) -> Vec<u8> {
     use flate;
 
     let any_library = cx.sess()
@@ -2506,11 +2448,10 @@ pub fn write_metadata<'a, 'tcx>(cx: &SharedCrateContext<'a, 'tcx>,
     let cstore = &cx.tcx().sess.cstore;
     let metadata = cstore.encode_metadata(cx.tcx(),
                                           cx.export_map(),
-                                          cx.item_symbols(),
                                           cx.link_meta(),
-                                          reachable,
-                                          mir_map,
-                                          krate);
+                                          reachable_ids,
+                                          cx.mir_map(),
+                                          cx.tcx().map.krate());
     let mut compressed = cstore.metadata_encoding_version().to_vec();
     compressed.extend_from_slice(&flate::deflate_bytes(&metadata));
 
@@ -2671,10 +2612,7 @@ fn iter_functions(llmod: llvm::ModuleRef) -> ValueIter {
 /// This list is later used by linkers to determine the set of symbols needed to
 /// be exposed from a dynamic library and it's also encoded into the metadata.
 pub fn filter_reachable_ids(scx: &SharedCrateContext) -> NodeSet {
-    scx.reachable().iter().map(|x| *x).filter(|id| {
-        // First, only worry about nodes which have a symbol name
-        scx.item_symbols().borrow().contains_key(id)
-    }).filter(|&id| {
+    scx.reachable().iter().map(|x| *x).filter(|&id| {
         // Next, we want to ignore some FFI functions that are not exposed from
         // this crate. Reachable FFI functions can be lumped into two
         // categories:
@@ -2692,7 +2630,20 @@ pub fn filter_reachable_ids(scx: &SharedCrateContext) -> NodeSet {
             hir_map::NodeForeignItem(..) => {
                 scx.sess().cstore.is_statically_included_foreign_item(id)
             }
-            _ => true,
+
+            // Only consider nodes that actually have exported symbols.
+            hir_map::NodeItem(&hir::Item {
+                node: hir::ItemStatic(..), .. }) |
+            hir_map::NodeItem(&hir::Item {
+                node: hir::ItemFn(..), .. }) |
+            hir_map::NodeImplItem(&hir::ImplItem {
+                node: hir::ImplItemKind::Method(..), .. }) => {
+                let def_id = scx.tcx().map.local_def_id(id);
+                let scheme = scx.tcx().lookup_item_type(def_id);
+                scheme.generics.types.is_empty()
+            }
+
+            _ => false
         }
     }).collect()
 }
@@ -2734,6 +2685,19 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                              check_overflow,
                                              check_dropflag);
 
+    let reachable_symbol_ids = filter_reachable_ids(&shared_ccx);
+
+    // Translate the metadata.
+    let metadata = time(tcx.sess.time_passes(), "write metadata", || {
+        write_metadata(&shared_ccx, &reachable_symbol_ids)
+    });
+
+    let metadata_module = ModuleTranslation {
+        llcx: shared_ccx.metadata_llcx(),
+        llmod: shared_ccx.metadata_llmod(),
+    };
+    let no_builtins = attr::contains_name(&krate.attrs, "no_builtins");
+
     let codegen_units = collect_and_partition_translation_items(&shared_ccx);
     let codegen_unit_count = codegen_units.len();
     assert!(tcx.sess.opts.cg.codegen_units == codegen_unit_count ||
@@ -2741,6 +2705,24 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     let crate_context_list = CrateContextList::new(&shared_ccx, codegen_units);
 
+    let modules = crate_context_list.iter()
+        .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() })
+        .collect();
+
+    // Skip crate items and just output metadata in -Z no-trans mode.
+    if tcx.sess.opts.no_trans {
+        let linker_info = LinkerInfo::new(&shared_ccx, &[]);
+        return CrateTranslation {
+            modules: modules,
+            metadata_module: metadata_module,
+            link: link_meta,
+            metadata: metadata,
+            reachable: vec![],
+            no_builtins: no_builtins,
+            linker_info: linker_info
+        };
+    }
+
     {
         let ccx = crate_context_list.get_ccx(0);
 
@@ -2770,13 +2752,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
     }
 
-    let reachable_symbol_ids = filter_reachable_ids(&shared_ccx);
-
-    // Translate the metadata.
-    let metadata = time(tcx.sess.time_passes(), "write metadata", || {
-        write_metadata(&shared_ccx, krate, &reachable_symbol_ids, mir_map)
-    });
-
     if shared_ccx.sess().trans_stats() {
         let stats = shared_ccx.stats();
         println!("--- trans stats ---");
@@ -2806,13 +2781,10 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
     }
 
-    let modules = crate_context_list.iter()
-        .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() })
-        .collect();
-
     let sess = shared_ccx.sess();
-    let mut reachable_symbols = reachable_symbol_ids.iter().map(|id| {
-        shared_ccx.item_symbols().borrow()[id].to_string()
+    let mut reachable_symbols = reachable_symbol_ids.iter().map(|&id| {
+        let def_id = shared_ccx.tcx().map.local_def_id(id);
+        Instance::mono(&shared_ccx, def_id).symbol_name(&shared_ccx)
     }).collect::<Vec<_>>();
     if sess.entry_fn.borrow().is_some() {
         reachable_symbols.push("main".to_string());
@@ -2835,7 +2807,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         reachable_symbols.extend(syms.into_iter().filter(|did| {
             sess.cstore.is_extern_item(shared_ccx.tcx(), *did)
         }).map(|did| {
-            sess.cstore.item_symbol(did)
+            Instance::mono(&shared_ccx, did).symbol_name(&shared_ccx)
         }));
     }
 
@@ -2849,12 +2821,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         create_imps(&crate_context_list);
     }
 
-    let metadata_module = ModuleTranslation {
-        llcx: shared_ccx.metadata_llcx(),
-        llmod: shared_ccx.metadata_llmod(),
-    };
-    let no_builtins = attr::contains_name(&krate.attrs, "no_builtins");
-
+    let linker_info = LinkerInfo::new(&shared_ccx, &reachable_symbols);
     CrateTranslation {
         modules: modules,
         metadata_module: metadata_module,
@@ -2862,6 +2829,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         metadata: metadata,
         reachable: reachable_symbols,
         no_builtins: no_builtins,
+        linker_info: linker_info
     }
 }
 
index 0185d1587625c378c476add77f94d05d1066da0a..4a7a5736b13a6fd4c49ccd1bc0e1e7d8fb085d27 100644 (file)
@@ -16,7 +16,7 @@ use llvm::{AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect};
 use llvm::{Opcode, IntPredicate, RealPredicate};
 use llvm::{ValueRef, BasicBlockRef};
 use common::*;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use builder::Builder;
 use type_::Type;
index 9f032cdbfe5130fa42b2dff8a9e2b289150179c2..d415698660000c229a0c1c44c6439e5c0e6f7b9d 100644 (file)
@@ -24,7 +24,7 @@ use libc::{c_uint, c_char};
 
 use std::ffi::CString;
 use std::ptr;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 pub struct Builder<'a, 'tcx: 'a> {
     pub llbuilder: BuilderRef,
index 01e0d45348256ff3464bb88b9347f70c35aff7d2..9ea65532b35b61c8c3a1cd35deda4ca89861ad34 100644 (file)
@@ -53,8 +53,8 @@ use Disr;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc::hir;
 
-use syntax::codemap::DUMMY_SP;
-use syntax::errors;
+use syntax_pos::DUMMY_SP;
+use errors;
 use syntax::ptr::P;
 
 #[derive(Debug)]
@@ -325,7 +325,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
     };
 
     let bare_fn_ty_maybe_ref = if is_by_ref {
-        tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), bare_fn_ty)
+        tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), bare_fn_ty)
     } else {
         bare_fn_ty
     };
@@ -378,7 +378,7 @@ pub fn trans_fn_pointer_shim<'a, 'tcx>(
                                                          bare_fn_ty,
                                                          "fn_pointer_shim");
     let llfn = declare::define_internal_fn(ccx, &function_name, tuple_fn_ty);
-
+    attributes::set_frame_pointer_elimination(ccx, llfn);
     //
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
@@ -496,43 +496,20 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         return immediate_rvalue(llfn, fn_ptr_ty);
     }
 
-    let attrs;
     let local_id = ccx.tcx().map.as_local_node_id(def_id);
-    let maybe_node = local_id.and_then(|id| tcx.map.find(id));
-    let (sym, attrs, local_item) = match maybe_node {
+    let local_item = match local_id.and_then(|id| tcx.map.find(id)) {
         Some(hir_map::NodeItem(&hir::Item {
-            ref attrs, id, span, node: hir::ItemFn(..), ..
+            span, node: hir::ItemFn(..), ..
         })) |
         Some(hir_map::NodeTraitItem(&hir::TraitItem {
-            ref attrs, id, span, node: hir::MethodTraitItem(_, Some(_)), ..
+            span, node: hir::MethodTraitItem(_, Some(_)), ..
         })) |
         Some(hir_map::NodeImplItem(&hir::ImplItem {
-            ref attrs, id, span, node: hir::ImplItemKind::Method(..), ..
-        })) => {
-            let sym = exported_name(ccx, instance, attrs);
-
-            if declare::get_defined_value(ccx, &sym).is_some() {
-                ccx.sess().span_fatal(span,
-                    &format!("symbol `{}` is already defined", sym));
-            }
-
-            (sym, &attrs[..], Some(id))
-        }
-
-        Some(hir_map::NodeForeignItem(&hir::ForeignItem {
-            ref attrs, name, node: hir::ForeignItemFn(..), ..
+            span, node: hir::ImplItemKind::Method(..), ..
         })) => {
-            (imported_name(name, attrs).to_string(), &attrs[..], None)
-        }
-
-        None => {
-            attrs = ccx.sess().cstore.item_attrs(def_id);
-            (ccx.sess().cstore.item_symbol(def_id), &attrs[..], None)
-        }
-
-        ref variant => {
-            bug!("get_fn: unexpected variant: {:?}", variant)
+            Some(span)
         }
+        _ => None
     };
 
     // This is subtle and surprising, but sometimes we have to bitcast
@@ -559,8 +536,16 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     // reference. It also occurs when testing libcore and in some
     // other weird situations. Annoying.
 
+    let sym = instance.symbol_name(ccx.shared());
     let llptrty = type_of::type_of(ccx, fn_ptr_ty);
     let llfn = if let Some(llfn) = declare::get_declared_value(ccx, &sym) {
+        if let Some(span) = local_item {
+            if declare::get_defined_value(ccx, &sym).is_some() {
+                ccx.sess().span_fatal(span,
+                    &format!("symbol `{}` is already defined", sym));
+            }
+        }
+
         if common::val_ty(llfn) != llptrty {
             if local_item.is_some() {
                 bug!("symbol `{}` previously declared as {:?}, now wanted as {:?}",
@@ -577,7 +562,8 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         assert_eq!(common::val_ty(llfn), llptrty);
         debug!("get_fn: not casting pointer!");
 
-        attributes::from_fn_attrs(ccx, attrs, llfn);
+        let attrs = ccx.tcx().get_attrs(def_id);
+        attributes::from_fn_attrs(ccx, &attrs, llfn);
         if local_item.is_some() {
             // FIXME(eddyb) Doubt all extern fn should allow unwinding.
             attributes::unwind(llfn, true);
@@ -586,11 +572,6 @@ fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         llfn
     };
 
-    // Always insert into item_symbols, in case this item is exported.
-    if let Some(id) = local_item {
-        ccx.item_symbols().borrow_mut().insert(id, sym);
-    }
-
     ccx.instances().borrow_mut().insert(instance, llfn);
 
     immediate_rvalue(llfn, fn_ptr_ty)
index 1c393f8091eee996eb58e3f5151097b5b5682c6f..9196cfce16feb3c85f2b937605b18c169f1689ea 100644 (file)
@@ -123,10 +123,10 @@ fn get_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                            -> Ty<'tcx> {
     match tcx.closure_kind(closure_id) {
         ty::ClosureKind::Fn => {
-            tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), fn_ty)
+            tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), fn_ty)
         }
         ty::ClosureKind::FnMut => {
-            tcx.mk_mut_ref(tcx.mk_region(ty::ReStatic), fn_ty)
+            tcx.mk_mut_ref(tcx.mk_region(ty::ReErased), fn_ty)
         }
         ty::ClosureKind::FnOnce => fn_ty,
     }
@@ -150,7 +150,7 @@ fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         return llfn;
     }
 
-    let symbol = symbol_names::exported_name(ccx, &instance);
+    let symbol = instance.symbol_name(ccx.shared());
 
     // Compute the rust-call form of the closure call method.
     let sig = &tcx.closure_type(closure_id, substs).sig;
@@ -171,6 +171,7 @@ fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     // set an inline hint for all closures
     attributes::inline(llfn, attributes::InlineAttr::Hint);
+    attributes::set_frame_pointer_elimination(ccx, llfn);
 
     debug!("get_or_create_declaration_if_closure(): inserting new \
             closure {:?}: {:?}",
@@ -343,7 +344,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
     // Find a version of the closure type. Substitute static for the
     // region since it doesn't really matter.
     let closure_ty = tcx.mk_closure_from_closure_substs(closure_def_id, substs);
-    let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), closure_ty);
+    let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), closure_ty);
 
     // Make a version with the type of by-ref closure.
     let ty::ClosureTy { unsafety, abi, mut sig } =
@@ -377,6 +378,7 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>(
     let function_name =
         symbol_names::internal_name_from_type_and_suffix(ccx, llonce_fn_ty, "once_shim");
     let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
+    attributes::set_frame_pointer_elimination(ccx, lloncefn);
 
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
index d278c3c8320fdc243b383b2d36fd43dfd6b96242..eea6aec37260e9e918ed8417a43e850919871494 100644 (file)
@@ -203,8 +203,8 @@ use rustc::mir::visit as mir_visit;
 use rustc::mir::visit::Visitor as MirVisitor;
 
 use syntax::abi::Abi;
-use syntax::codemap::DUMMY_SP;
-use syntax::errors;
+use errors;
+use syntax_pos::DUMMY_SP;
 use base::custom_coerce_unsize_info;
 use context::SharedCrateContext;
 use common::{fulfill_obligation, normalize_and_test_predicates, type_is_sized};
@@ -523,7 +523,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
             let ty = monomorphize::apply_param_substs(self.scx.tcx(),
                                                       self.param_substs,
                                                       &ty);
-            let ty = self.scx.tcx().erase_regions(&ty);
+            assert!(ty.is_normalized_for_trans());
             let ty = glue::get_drop_glue_type(self.scx.tcx(), ty);
             self.output.push(TransItem::DropGlue(DropGlueKind::Ty(ty)));
         }
@@ -859,6 +859,7 @@ fn do_static_trait_method_dispatch<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
                                                        &callee_substs);
 
     let trait_ref = ty::Binder(rcvr_substs.to_trait_ref(tcx, trait_id));
+    let trait_ref = tcx.normalize_associated_type(&trait_ref);
     let vtbl = fulfill_obligation(scx, DUMMY_SP, trait_ref);
 
     // Now that we know which impl is being used, we can dispatch to
@@ -992,11 +993,8 @@ fn create_fn_trans_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let concrete_substs = monomorphize::apply_param_substs(tcx,
                                                            param_substs,
                                                            &fn_substs);
-    let concrete_substs = tcx.erase_regions(&concrete_substs);
-
-    let trans_item =
-        TransItem::Fn(Instance::new(def_id, concrete_substs));
-    return trans_item;
+    assert!(concrete_substs.is_normalized_for_trans());
+    TransItem::Fn(Instance::new(def_id, concrete_substs))
 }
 
 /// Creates a `TransItem` for each method that is referenced by the vtable for
@@ -1034,10 +1032,14 @@ fn create_trans_items_for_vtable_methods<'a, 'tcx>(scx: &SharedCrateContext<'a,
                             } else {
                                 None
                             }
-                        })
-                        .collect::<Vec<_>>();
+                        });
+
+                    output.extend(items);
 
-                    output.extend(items.into_iter());
+                    // Also add the destructor
+                    let dg_type = glue::get_drop_glue_type(scx.tcx(),
+                                                           trait_ref.self_ty());
+                    output.push(TransItem::DropGlue(DropGlueKind::Ty(dg_type)));
                 }
                 _ => { /* */ }
             }
@@ -1234,7 +1236,7 @@ pub enum TransItemState {
 }
 
 pub fn collecting_debug_information(scx: &SharedCrateContext) -> bool {
-    return scx.sess().opts.cg.debug_assertions == Some(true) &&
+    return cfg!(debug_assertions) &&
            scx.sess().opts.debugging_opts.print_trans_items.is_some();
 }
 
index c1685e6a749046b8c8f7ee42ee737b4b648cbdf9..d057f623383d3587b59b0e0b77c813c5b0334fca 100644 (file)
@@ -39,6 +39,7 @@ use monomorphize;
 use type_::Type;
 use value::Value;
 use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::layout::Layout;
 use rustc::traits::{self, SelectionContext, ProjectionMode};
 use rustc::ty::fold::TypeFoldable;
 use rustc::hir;
@@ -51,9 +52,9 @@ use std::ffi::CString;
 use std::cell::{Cell, RefCell};
 
 use syntax::ast;
-use syntax::codemap::{DUMMY_SP, Span};
 use syntax::parse::token::InternedString;
 use syntax::parse::token;
+use syntax_pos::{DUMMY_SP, Span};
 
 pub use context::{CrateContext, SharedCrateContext};
 
@@ -99,6 +100,63 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
     }
 }
 
+/// Returns Some([a, b]) if the type has a pair of fields with types a and b.
+pub fn type_pair_fields<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
+                                  -> Option<[Ty<'tcx>; 2]> {
+    match ty.sty {
+        ty::TyEnum(adt, substs) | ty::TyStruct(adt, substs) => {
+            assert_eq!(adt.variants.len(), 1);
+            let fields = &adt.variants[0].fields;
+            if fields.len() != 2 {
+                return None;
+            }
+            Some([monomorphize::field_ty(ccx.tcx(), substs, &fields[0]),
+                  monomorphize::field_ty(ccx.tcx(), substs, &fields[1])])
+        }
+        ty::TyClosure(_, ty::ClosureSubsts { upvar_tys: tys, .. }) |
+        ty::TyTuple(tys) => {
+            if tys.len() != 2 {
+                return None;
+            }
+            Some([tys[0], tys[1]])
+        }
+        _ => None
+    }
+}
+
+/// Returns true if the type is represented as a pair of immediates.
+pub fn type_is_imm_pair<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
+                                  -> bool {
+    let tcx = ccx.tcx();
+    let layout = tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| {
+        match ty.layout(&infcx) {
+            Ok(layout) => layout,
+            Err(err) => {
+                bug!("type_is_imm_pair: layout for `{:?}` failed: {}",
+                     ty, err);
+            }
+        }
+    });
+
+    match *layout {
+        Layout::FatPointer { .. } => true,
+        Layout::Univariant { ref variant, .. } => {
+            // There must be only 2 fields.
+            if variant.offset_after_field.len() != 2 {
+                return false;
+            }
+
+            match type_pair_fields(ccx, ty) {
+                Some([a, b]) => {
+                    type_is_immediate(ccx, a) && type_is_immediate(ccx, b)
+                }
+                None => false
+            }
+        }
+        _ => false
+    }
+}
+
 /// Identify types which have size zero at runtime.
 pub fn type_is_zero_size<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
     use machine::llsize_of_alloc;
@@ -202,8 +260,7 @@ impl<'a, 'tcx> VariantInfo<'tcx> {
 
     /// Return the variant corresponding to a given node (e.g. expr)
     pub fn of_node(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>, id: ast::NodeId) -> Self {
-        let node_def = tcx.def_map.borrow().get(&id).map(|v| v.full_def());
-        Self::from_ty(tcx, ty, node_def)
+        Self::from_ty(tcx, ty, Some(tcx.expect_def(id)))
     }
 
     pub fn field_index(&self, name: ast::Name) -> usize {
@@ -577,6 +634,15 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> {
         self.lpad.get()
     }
 
+    pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) {
+        // FIXME: use an IVar?
+        self.lpad.set(lpad);
+    }
+
+    pub fn set_lpad(&self, lpad: Option<LandingPad>) {
+        self.set_lpad_ref(lpad.map(|p| &*self.fcx().lpad_arena.alloc(p)))
+    }
+
     pub fn mir(&self) -> CachedMir<'blk, 'tcx> {
         self.fcx.mir()
     }
@@ -589,15 +655,6 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> {
         self.tcx().map.node_to_string(id).to_string()
     }
 
-    pub fn def(&self, nid: ast::NodeId) -> Def {
-        match self.tcx().def_map.borrow().get(&nid) {
-            Some(v) => v.full_def(),
-            None => {
-                bug!("no def associated with node id {}", nid);
-            }
-        }
-    }
-
     pub fn to_str(&self) -> String {
         format!("[block {:p}]", self)
     }
@@ -716,7 +773,16 @@ impl<'blk, 'tcx> BlockAndBuilder<'blk, 'tcx> {
     }
 
     pub fn set_lpad(&self, lpad: Option<LandingPad>) {
-        self.bcx.lpad.set(lpad.map(|p| &*self.fcx().lpad_arena.alloc(p)))
+        self.bcx.set_lpad(lpad)
+    }
+
+    pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) {
+        // FIXME: use an IVar?
+        self.bcx.set_lpad_ref(lpad);
+    }
+
+    pub fn lpad(&self) -> Option<&'blk LandingPad> {
+        self.bcx.lpad()
     }
 }
 
@@ -761,6 +827,10 @@ impl LandingPad {
     pub fn bundle(&self) -> Option<&OperandBundleDef> {
         self.operand.as_ref()
     }
+
+    pub fn cleanuppad(&self) -> Option<ValueRef> {
+        self.cleanuppad
+    }
 }
 
 impl Clone for LandingPad {
@@ -1061,7 +1131,7 @@ pub fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
     let trait_ref = tcx.erase_regions(&trait_ref);
 
     scx.trait_cache().memoize(trait_ref, || {
-        debug!("trans fulfill_obligation: trait_ref={:?} def_id={:?}",
+        debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})",
                trait_ref, trait_ref.def_id());
 
         // Do the initial selection for the obligation. This yields the
@@ -1096,11 +1166,14 @@ pub fn fulfill_obligation<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
                 }
             };
 
+            debug!("fulfill_obligation: selection={:?}", selection);
+
             // Currently, we use a fulfillment context to completely resolve
             // all nested obligations. This is because they can inform the
             // inference of the impl's type parameters.
             let mut fulfill_cx = traits::FulfillmentContext::new();
             let vtable = selection.map(|predicate| {
+                debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate);
                 fulfill_cx.register_predicate_obligation(&infcx, predicate);
             });
             let vtable = infcx.drain_fulfillment_cx_or_panic(span, &mut fulfill_cx, &vtable);
@@ -1140,18 +1213,18 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     })
 }
 
-pub fn langcall(bcx: Block,
+pub fn langcall(tcx: TyCtxt,
                 span: Option<Span>,
                 msg: &str,
                 li: LangItem)
                 -> DefId {
-    match bcx.tcx().lang_items.require(li) {
+    match tcx.lang_items.require(li) {
         Ok(id) => id,
         Err(s) => {
             let msg = format!("{} {}", msg, s);
             match span {
-                Some(span) => bcx.tcx().sess.span_fatal(span, &msg[..]),
-                None => bcx.tcx().sess.fatal(&msg[..]),
+                Some(span) => tcx.sess.span_fatal(span, &msg[..]),
+                None => tcx.sess.fatal(&msg[..]),
             }
         }
     }
index 3e876eb3d7de079fc53d08ddd73d72711ac611b9..5596ab0d819e0a3470f7b6a2e360a0c72de87fa2 100644 (file)
@@ -19,7 +19,7 @@ use rustc::hir::def::Def;
 use rustc::hir::def_id::DefId;
 use rustc::hir::map as hir_map;
 use {abi, adt, closure, debuginfo, expr, machine};
-use base::{self, exported_name, imported_name, push_ctxt};
+use base::{self, push_ctxt};
 use callee::Callee;
 use collector;
 use trans_item::TransItem;
@@ -49,9 +49,9 @@ use std::borrow::Cow;
 use libc::c_uint;
 use syntax::ast::{self, LitKind};
 use syntax::attr::{self, AttrMetaMethods};
-use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 pub type FnArgMap<'a> = Option<&'a NodeMap<ValueRef>>;
 
@@ -138,18 +138,15 @@ pub fn addr_of(ccx: &CrateContext,
                align: machine::llalign,
                kind: &str)
                -> ValueRef {
-    match ccx.const_globals().borrow().get(&cv) {
-        Some(&gv) => {
-            unsafe {
-                // Upgrade the alignment in cases where the same constant is used with different
-                // alignment requirements
-                if align > llvm::LLVMGetAlignment(gv) {
-                    llvm::LLVMSetAlignment(gv, align);
-                }
+    if let Some(&gv) = ccx.const_globals().borrow().get(&cv) {
+        unsafe {
+            // Upgrade the alignment in cases where the same constant is used with different
+            // alignment requirements
+            if align > llvm::LLVMGetAlignment(gv) {
+                llvm::LLVMSetAlignment(gv, align);
             }
-            return gv;
         }
-        None => {}
+        return gv;
     }
     let gv = addr_of_mut(ccx, cv, align, kind);
     unsafe {
@@ -297,8 +294,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         // `def` must be its own statement and cannot be in the `match`
         // otherwise the `def_map` will be borrowed for the entire match instead
         // of just to get the `def` value
-        let def = ccx.tcx().def_map.borrow().get(&expr.id).unwrap().full_def();
-        match def {
+        match ccx.tcx().expect_def(expr.id) {
             Def::Const(def_id) | Def::AssociatedConst(def_id) => {
                 if !ccx.tcx().tables.borrow().adjustments.contains_key(&expr.id) {
                     debug!("get_const_expr_as_global ({:?}): found const {:?}",
@@ -382,7 +378,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     // Don't copy data to do a deref+ref
                     // (i.e., skip the last auto-deref).
                     llconst = addr_of(cx, llconst, type_of::align_of(cx, ty), "autoref");
-                    ty = cx.tcx().mk_imm_ref(cx.tcx().mk_region(ty::ReStatic), ty);
+                    ty = cx.tcx().mk_imm_ref(cx.tcx().mk_region(ty::ReErased), ty);
                 }
             } else if adj.autoderefs > 0 {
                 let (dv, dt) = const_deref(cx, llconst, ty);
@@ -716,7 +712,10 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             if iv >= len {
                 // FIXME #3170: report this earlier on in the const-eval
                 // pass. Reporting here is a bit late.
-                const_err(cx, e.span, Err(ErrKind::IndexOutOfBounds), trueconst)?;
+                const_err(cx, e.span, Err(ErrKind::IndexOutOfBounds {
+                    len: len,
+                    index: iv
+                }), trueconst)?;
                 C_undef(val_ty(arr).element_type())
             } else {
                 const_get_elt(arr, &[iv as c_uint])
@@ -800,8 +799,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     _ => break,
                 }
             }
-            let opt_def = cx.tcx().def_map.borrow().get(&cur.id).map(|d| d.full_def());
-            if let Some(Def::Static(def_id, _)) = opt_def {
+            if let Some(Def::Static(def_id, _)) = cx.tcx().expect_def_or_none(cur.id) {
                 get_static(cx, def_id).val
             } else {
                 // If this isn't the address of a static, then keep going through
@@ -888,8 +886,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             }
         },
         hir::ExprPath(..) => {
-            let def = cx.tcx().def_map.borrow().get(&e.id).unwrap().full_def();
-            match def {
+            match cx.tcx().expect_def(e.id) {
                 Def::Local(_, id) => {
                     if let Some(val) = fn_args.and_then(|args| args.get(&id).cloned()) {
                         val
@@ -934,9 +931,8 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     _ => break,
                 };
             }
-            let def = cx.tcx().def_map.borrow()[&callee.id].full_def();
             let arg_vals = map_list(args)?;
-            match def {
+            match cx.tcx().expect_def(callee.id) {
                 Def::Fn(did) | Def::Method(did) => {
                     const_fn_call(
                         cx,
@@ -1017,34 +1013,31 @@ pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
         return Datum::new(g, ty, Lvalue::new("static"));
     }
 
+    let sym = instance.symbol_name(ccx.shared());
+
     let g = if let Some(id) = ccx.tcx().map.as_local_node_id(def_id) {
         let llty = type_of::type_of(ccx, ty);
         match ccx.tcx().map.get(id) {
             hir_map::NodeItem(&hir::Item {
-                ref attrs, span, node: hir::ItemStatic(..), ..
+                span, node: hir::ItemStatic(..), ..
             }) => {
                 // If this static came from an external crate, then
                 // we need to get the symbol from metadata instead of
                 // using the current crate's name/version
                 // information in the hash of the symbol
-                let sym = exported_name(ccx, instance, attrs);
                 debug!("making {}", sym);
 
                 // Create the global before evaluating the initializer;
                 // this is necessary to allow recursive statics.
-                let g = declare::define_global(ccx, &sym, llty).unwrap_or_else(|| {
+                declare::define_global(ccx, &sym, llty).unwrap_or_else(|| {
                     ccx.sess().span_fatal(span,
                         &format!("symbol `{}` is already defined", sym))
-                });
-
-                ccx.item_symbols().borrow_mut().insert(id, sym);
-                g
+                })
             }
 
             hir_map::NodeForeignItem(&hir::ForeignItem {
-                ref attrs, name, span, node: hir::ForeignItemStatic(..), ..
+                ref attrs, span, node: hir::ForeignItemStatic(..), ..
             }) => {
-                let ident = imported_name(name, attrs);
                 let g = if let Some(name) =
                         attr::first_attr_value_str_by_name(&attrs, "linkage") {
                     // If this is a static with a linkage specified, then we need to handle
@@ -1066,7 +1059,7 @@ pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
                     };
                     unsafe {
                         // Declare a symbol `foo` with the desired linkage.
-                        let g1 = declare::declare_global(ccx, &ident, llty2);
+                        let g1 = declare::declare_global(ccx, &sym, llty2);
                         llvm::SetLinkage(g1, linkage);
 
                         // Declare an internal global `extern_with_linkage_foo` which
@@ -1076,10 +1069,10 @@ pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
                         // `extern_with_linkage_foo` will instead be initialized to
                         // zero.
                         let mut real_name = "_rust_extern_with_linkage_".to_string();
-                        real_name.push_str(&ident);
+                        real_name.push_str(&sym);
                         let g2 = declare::define_global(ccx, &real_name, llty).unwrap_or_else(||{
                             ccx.sess().span_fatal(span,
-                                &format!("symbol `{}` is already defined", ident))
+                                &format!("symbol `{}` is already defined", sym))
                         });
                         llvm::SetLinkage(g2, llvm::InternalLinkage);
                         llvm::LLVMSetInitializer(g2, g1);
@@ -1087,7 +1080,7 @@ pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
                     }
                 } else {
                     // Generate an external declaration.
-                    declare::declare_global(ccx, &ident, llty)
+                    declare::declare_global(ccx, &sym, llty)
                 };
 
                 for attr in attrs {
@@ -1104,8 +1097,7 @@ pub fn get_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_id: DefId)
     } else {
         // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
         // FIXME(nagisa): investigate whether it can be changed into define_global
-        let name = ccx.sess().cstore.item_symbol(def_id);
-        let g = declare::declare_global(ccx, &name, type_of::type_of(ccx, ty));
+        let g = declare::declare_global(ccx, &sym, type_of::type_of(ccx, ty));
         // Thread-local statics in some other crate need to *always* be linked
         // against in a thread-local fashion, so we need to be sure to apply the
         // thread-local attribute locally if it was present remotely. If we
index 4d6c4cdcc6b072f5df36d090664561aa1f82dea8..bfcb1ae33b3019f000314b4e25005f23f1a839aa 100644 (file)
@@ -71,7 +71,6 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> {
 
     export_map: ExportMap,
     reachable: NodeSet,
-    item_symbols: RefCell<NodeMap<String>>,
     link_meta: LinkMeta,
     symbol_hasher: RefCell<Sha256>,
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -395,7 +394,6 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
             metadata_llcx: metadata_llcx,
             export_map: export_map,
             reachable: reachable,
-            item_symbols: RefCell::new(NodeMap()),
             link_meta: link_meta,
             symbol_hasher: RefCell::new(symbol_hasher),
             tcx: tcx,
@@ -439,10 +437,6 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
         &self.reachable
     }
 
-    pub fn item_symbols<'a>(&'a self) -> &'a RefCell<NodeMap<String>> {
-        &self.item_symbols
-    }
-
     pub fn trait_cache(&self) -> &RefCell<DepTrackingMap<TraitSelectionCache<'tcx>>> {
         &self.trait_cache
     }
@@ -501,7 +495,15 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
         assert!(scheme.generics.types.is_empty());
         self.tcx().mk_substs(
             Substs::new(VecPerParamSpace::empty(),
-                        scheme.generics.regions.map(|_| ty::ReStatic)))
+                        scheme.generics.regions.map(|_| ty::ReErased)))
+    }
+
+    pub fn symbol_hasher(&self) -> &RefCell<Sha256> {
+        &self.symbol_hasher
+    }
+
+    pub fn mir_map(&self) -> &MirMap<'tcx> {
+        &self.mir_map
     }
 
     pub fn metadata_symbol_name(&self) -> String {
@@ -716,10 +718,6 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
         &self.shared.reachable
     }
 
-    pub fn item_symbols<'a>(&'a self) -> &'a RefCell<NodeMap<String>> {
-        &self.shared.item_symbols
-    }
-
     pub fn link_meta<'a>(&'a self) -> &'a LinkMeta {
         &self.shared.link_meta
     }
@@ -1099,45 +1097,7 @@ fn declare_intrinsic(ccx: &CrateContext, key: &str) -> Option<ValueRef> {
     ifn!("llvm.localrecover", fn(i8p, i8p, t_i32) -> i8p);
     ifn!("llvm.x86.seh.recoverfp", fn(i8p, i8p) -> i8p);
 
-    // Some intrinsics were introduced in later versions of LLVM, but they have
-    // fallbacks in libc or libm and such.
-    macro_rules! compatible_ifn {
-        ($name:expr, noop($cname:ident ($($arg:expr),*) -> void), $llvm_version:expr) => (
-            if unsafe { llvm::LLVMVersionMinor() >= $llvm_version } {
-                // The `if key == $name` is already in ifn!
-                ifn!($name, fn($($arg),*) -> void);
-            } else if key == $name {
-                let f = declare::declare_cfn(ccx, stringify!($cname),
-                                             Type::func(&[$($arg),*], &void));
-                llvm::SetLinkage(f, llvm::InternalLinkage);
-
-                let bld = ccx.builder();
-                let llbb = unsafe {
-                    llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), f,
-                                                        "entry-block\0".as_ptr() as *const _)
-                };
-
-                bld.position_at_end(llbb);
-                bld.ret_void();
-
-                ccx.intrinsics().borrow_mut().insert($name, f.clone());
-                return Some(f);
-            }
-        );
-        ($name:expr, $cname:ident ($($arg:expr),*) -> $ret:expr, $llvm_version:expr) => (
-            if unsafe { llvm::LLVMVersionMinor() >= $llvm_version } {
-                // The `if key == $name` is already in ifn!
-                ifn!($name, fn($($arg),*) -> $ret);
-            } else if key == $name {
-                let f = declare::declare_cfn(ccx, stringify!($cname),
-                                             Type::func(&[$($arg),*], &$ret));
-                ccx.intrinsics().borrow_mut().insert($name, f.clone());
-                return Some(f);
-            }
-        )
-    }
-
-    compatible_ifn!("llvm.assume", noop(llvmcompat_assume(i1) -> void), 6);
+    ifn!("llvm.assume", fn(i1) -> void);
 
     if ccx.sess().opts.debuginfo != NoDebugInfo {
         ifn!("llvm.dbg.declare", fn(Type::metadata(ccx), Type::metadata(ccx)) -> void);
index f793f0a6d553ba8ec1326a3e3cbdf33348356e35..8845f124218bc437b3da19d2a9048be5c97eba88 100644 (file)
@@ -318,8 +318,8 @@ pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let loop_id = match opt_label {
         None => fcx.top_loop_scope(),
         Some(_) => {
-            match bcx.tcx().def_map.borrow().get(&expr.id).map(|d| d.full_def())  {
-                Some(Def::Label(loop_id)) => loop_id,
+            match bcx.tcx().expect_def(expr.id) {
+                Def::Label(loop_id) => loop_id,
                 r => {
                     bug!("{:?} in def-map for label", r)
                 }
@@ -400,7 +400,7 @@ pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let align = machine::llalign_of_min(ccx, val_ty(expr_file_line_const));
     let expr_file_line = consts::addr_of(ccx, expr_file_line_const, align, "panic_loc");
     let args = vec!(expr_file_line);
-    let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
+    let did = langcall(bcx.tcx(), Some(call_info.span), "", PanicFnLangItem);
     Callee::def(ccx, did, ccx.tcx().mk_substs(Substs::empty()))
         .call(bcx, call_info.debug_loc(), ArgVals(&args), None).bcx
 }
@@ -428,7 +428,7 @@ pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let align = machine::llalign_of_min(ccx, val_ty(file_line_const));
     let file_line = consts::addr_of(ccx, file_line_const, align, "panic_bounds_check_loc");
     let args = vec!(file_line, index, len);
-    let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
+    let did = langcall(bcx.tcx(), Some(call_info.span), "", PanicBoundsCheckFnLangItem);
     Callee::def(ccx, did, ccx.tcx().mk_substs(Substs::empty()))
         .call(bcx, call_info.debug_loc(), ArgVals(&args), None).bcx
 }
index eda3ce1d1062c2f2cc900c6d7567d41362517395..875f88e37c916d623d8e9024be1aa7506a0b7d07 100644 (file)
@@ -106,7 +106,7 @@ use rustc::ty::Ty;
 
 use std::fmt;
 use syntax::ast;
-use syntax::codemap::DUMMY_SP;
+use syntax_pos::DUMMY_SP;
 
 /// A `Datum` encapsulates the result of evaluating an expression.  It
 /// describes where the value is stored, what Rust type the value has,
index ba592382d1a794d1aa8bba8b96356409dbed7ebd..2b079e7dcc8d9cd483c5db3786435f2750a2dae0 100644 (file)
@@ -16,16 +16,17 @@ use llvm;
 use llvm::debuginfo::{DIScope, DISubprogram};
 use common::{CrateContext, FunctionContext};
 use rustc::hir::pat_util;
-use rustc::mir::repr::{Mir, ScopeId};
+use rustc::mir::repr::{Mir, VisibilityScope};
 use rustc::util::nodemap::NodeMap;
 
 use libc::c_uint;
 use std::ptr;
 
-use syntax::codemap::{Span, Pos};
+use syntax_pos::{Span, Pos};
 use syntax::{ast, codemap};
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc::hir::{self, PatKind};
 
 // This procedure builds the *scope map* for a given function, which maps any
@@ -42,18 +43,15 @@ pub fn create_scope_map(cx: &CrateContext,
                         fn_ast_id: ast::NodeId)
                         -> NodeMap<DIScope> {
     let mut scope_map = NodeMap();
-
-    let def_map = &cx.tcx().def_map;
-
     let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata, name: None });
     scope_map.insert(fn_ast_id, fn_metadata);
 
     // Push argument identifiers onto the stack so arguments integrate nicely
     // with variable shadowing.
     for arg in args {
-        pat_util::pat_bindings(def_map, &arg.pat, |_, node_id, _, path1| {
+        pat_util::pat_bindings(&arg.pat, |_, node_id, _, path1| {
             scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata,
-                                               name: Some(path1.node.unhygienize()) });
+                                               name: Some(path1.node) });
             scope_map.insert(node_id, fn_metadata);
         })
     }
@@ -72,9 +70,9 @@ pub fn create_scope_map(cx: &CrateContext,
 
 /// Produce DIScope DIEs for each MIR Scope which has variables defined in it.
 /// If debuginfo is disabled, the returned vector is empty.
-pub fn create_mir_scopes(fcx: &FunctionContext) -> Vec<DIScope> {
+pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec<VisibilityScope, DIScope> {
     let mir = fcx.mir.clone().expect("create_mir_scopes: missing MIR for fn");
-    let mut scopes = vec![ptr::null_mut(); mir.scopes.len()];
+    let mut scopes = IndexVec::from_elem(ptr::null_mut(), &mir.visibility_scopes);
 
     let fn_metadata = match fcx.debug_context {
         FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata,
@@ -85,14 +83,14 @@ pub fn create_mir_scopes(fcx: &FunctionContext) -> Vec<DIScope> {
     };
 
     // Find all the scopes with variables defined in them.
-    let mut has_variables = BitVector::new(mir.scopes.len());
+    let mut has_variables = BitVector::new(mir.visibility_scopes.len());
     for var in &mir.var_decls {
-        has_variables.insert(var.scope.index());
+        has_variables.insert(var.source_info.scope.index());
     }
 
     // Instantiate all scopes.
-    for idx in 0..mir.scopes.len() {
-        let scope = ScopeId::new(idx);
+    for idx in 0..mir.visibility_scopes.len() {
+        let scope = VisibilityScope::new(idx);
         make_mir_scope(fcx.ccx, &mir, &has_variables, fn_metadata, scope, &mut scopes);
     }
 
@@ -103,24 +101,23 @@ fn make_mir_scope(ccx: &CrateContext,
                   mir: &Mir,
                   has_variables: &BitVector,
                   fn_metadata: DISubprogram,
-                  scope: ScopeId,
-                  scopes: &mut [DIScope]) {
-    let idx = scope.index();
-    if !scopes[idx].is_null() {
+                  scope: VisibilityScope,
+                  scopes: &mut IndexVec<VisibilityScope, DIScope>) {
+    if !scopes[scope].is_null() {
         return;
     }
 
-    let scope_data = &mir.scopes[scope];
+    let scope_data = &mir.visibility_scopes[scope];
     let parent_scope = if let Some(parent) = scope_data.parent_scope {
         make_mir_scope(ccx, mir, has_variables, fn_metadata, parent, scopes);
-        scopes[parent.index()]
+        scopes[parent]
     } else {
         // The root is the function itself.
-        scopes[idx] = fn_metadata;
+        scopes[scope] = fn_metadata;
         return;
     };
 
-    if !has_variables.contains(idx) {
+    if !has_variables.contains(scope.index()) {
         // Do not create a DIScope if there are no variables
         // defined in this MIR Scope, to avoid debuginfo bloat.
 
@@ -128,14 +125,14 @@ fn make_mir_scope(ccx: &CrateContext,
         // our parent is the root, because we might want to
         // put arguments in the root and not have shadowing.
         if parent_scope != fn_metadata {
-            scopes[idx] = parent_scope;
+            scopes[scope] = parent_scope;
             return;
         }
     }
 
     let loc = span_start(ccx, scope_data.span);
-    let file_metadata = file_metadata(ccx, &loc.file.name);
-    scopes[idx] = unsafe {
+    scopes[scope] = unsafe {
+    let file_metadata = file_metadata(ccx, &loc.file.name, &loc.file.abs_path);
         llvm::LLVMDIBuilderCreateLexicalBlock(
             DIB(ccx),
             parent_scope,
@@ -155,7 +152,7 @@ fn with_new_scope<F>(cx: &CrateContext,
 {
     // Create a new lexical scope and push it onto the stack
     let loc = span_start(cx, scope_span);
-    let file_metadata = file_metadata(cx, &loc.file.name);
+    let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path);
     let parent_scope = scope_stack.last().unwrap().scope_metadata;
 
     let scope_metadata = unsafe {
@@ -235,76 +232,66 @@ fn walk_pattern(cx: &CrateContext,
                 pat: &hir::Pat,
                 scope_stack: &mut Vec<ScopeStackEntry> ,
                 scope_map: &mut NodeMap<DIScope>) {
-
-    let def_map = &cx.tcx().def_map;
-
     // Unfortunately, we cannot just use pat_util::pat_bindings() or
     // ast_util::walk_pat() here because we have to visit *all* nodes in
     // order to put them into the scope map. The above functions don't do that.
     match pat.node {
-        PatKind::Ident(_, ref path1, ref sub_pat_opt) => {
-
-            // Check if this is a binding. If so we need to put it on the
-            // scope stack and maybe introduce an artificial scope
-            if pat_util::pat_is_binding(&def_map.borrow(), &pat) {
-
-                let name = path1.node.unhygienize();
-
-                // LLVM does not properly generate 'DW_AT_start_scope' fields
-                // for variable DIEs. For this reason we have to introduce
-                // an artificial scope at bindings whenever a variable with
-                // the same name is declared in *any* parent scope.
-                //
-                // Otherwise the following error occurs:
-                //
-                // let x = 10;
-                //
-                // do_something(); // 'gdb print x' correctly prints 10
-                //
-                // {
-                //     do_something(); // 'gdb print x' prints 0, because it
-                //                     // already reads the uninitialized 'x'
-                //                     // from the next line...
-                //     let x = 100;
-                //     do_something(); // 'gdb print x' correctly prints 100
-                // }
-
-                // Is there already a binding with that name?
-                // N.B.: this comparison must be UNhygienic... because
-                // gdb knows nothing about the context, so any two
-                // variables with the same name will cause the problem.
-                let need_new_scope = scope_stack
-                    .iter()
-                    .any(|entry| entry.name == Some(name));
-
-                if need_new_scope {
-                    // Create a new lexical scope and push it onto the stack
-                    let loc = span_start(cx, pat.span);
-                    let file_metadata = file_metadata(cx, &loc.file.name);
-                    let parent_scope = scope_stack.last().unwrap().scope_metadata;
-
-                    let scope_metadata = unsafe {
-                        llvm::LLVMDIBuilderCreateLexicalBlock(
-                            DIB(cx),
-                            parent_scope,
-                            file_metadata,
-                            loc.line as c_uint,
-                            loc.col.to_usize() as c_uint)
-                    };
-
-                    scope_stack.push(ScopeStackEntry {
-                        scope_metadata: scope_metadata,
-                        name: Some(name)
-                    });
-
-                } else {
-                    // Push a new entry anyway so the name can be found
-                    let prev_metadata = scope_stack.last().unwrap().scope_metadata;
-                    scope_stack.push(ScopeStackEntry {
-                        scope_metadata: prev_metadata,
-                        name: Some(name)
-                    });
-                }
+        PatKind::Binding(_, ref path1, ref sub_pat_opt) => {
+            // LLVM does not properly generate 'DW_AT_start_scope' fields
+            // for variable DIEs. For this reason we have to introduce
+            // an artificial scope at bindings whenever a variable with
+            // the same name is declared in *any* parent scope.
+            //
+            // Otherwise the following error occurs:
+            //
+            // let x = 10;
+            //
+            // do_something(); // 'gdb print x' correctly prints 10
+            //
+            // {
+            //     do_something(); // 'gdb print x' prints 0, because it
+            //                     // already reads the uninitialized 'x'
+            //                     // from the next line...
+            //     let x = 100;
+            //     do_something(); // 'gdb print x' correctly prints 100
+            // }
+
+            // Is there already a binding with that name?
+            // N.B.: this comparison must be UNhygienic... because
+            // gdb knows nothing about the context, so any two
+            // variables with the same name will cause the problem.
+            let name = path1.node;
+            let need_new_scope = scope_stack
+                .iter()
+                .any(|entry| entry.name == Some(name));
+
+            if need_new_scope {
+                // Create a new lexical scope and push it onto the stack
+                let loc = span_start(cx, pat.span);
+                let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path);
+                let parent_scope = scope_stack.last().unwrap().scope_metadata;
+
+                let scope_metadata = unsafe {
+                    llvm::LLVMDIBuilderCreateLexicalBlock(
+                        DIB(cx),
+                        parent_scope,
+                        file_metadata,
+                        loc.line as c_uint,
+                        loc.col.to_usize() as c_uint)
+                };
+
+                scope_stack.push(ScopeStackEntry {
+                    scope_metadata: scope_metadata,
+                    name: Some(name)
+                });
+
+            } else {
+                // Push a new entry anyway so the name can be found
+                let prev_metadata = scope_stack.last().unwrap().scope_metadata;
+                scope_stack.push(ScopeStackEntry {
+                    scope_metadata: prev_metadata,
+                    name: Some(name)
+                });
             }
 
             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
@@ -318,13 +305,11 @@ fn walk_pattern(cx: &CrateContext,
             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
         }
 
-        PatKind::TupleStruct(_, ref sub_pats_opt) => {
+        PatKind::TupleStruct(_, ref sub_pats, _) => {
             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
 
-            if let Some(ref sub_pats) = *sub_pats_opt {
-                for p in sub_pats {
-                    walk_pattern(cx, &p, scope_stack, scope_map);
-                }
+            for p in sub_pats {
+                walk_pattern(cx, &p, scope_stack, scope_map);
             }
         }
 
@@ -343,7 +328,7 @@ fn walk_pattern(cx: &CrateContext,
             }
         }
 
-        PatKind::Tup(ref sub_pats) => {
+        PatKind::Tuple(ref sub_pats, _) => {
             scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
 
             for sub_pat in sub_pats {
index ccb01789aa64c8f4160b5699f1b8b1959b0731a4..34dedeede98e0c6ef480df93c2374cad40164e05 100644 (file)
@@ -44,9 +44,9 @@ use std::ptr;
 use std::rc::Rc;
 use syntax;
 use syntax::util::interner::Interner;
-use syntax::codemap::Span;
-use syntax::{ast, codemap};
+use syntax::ast;
 use syntax::parse::token;
+use syntax_pos::{self, Span};
 
 
 // From DWARF 5.
@@ -563,7 +563,7 @@ fn vec_slice_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     assert!(member_descriptions.len() == member_llvm_types.len());
 
     let loc = span_start(cx, span);
-    let file_metadata = file_metadata(cx, &loc.file.name);
+    let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path);
 
     let metadata = composite_type_metadata(cx,
                                            slice_llvm_type,
@@ -660,7 +660,7 @@ fn trait_pointer_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                             &[],
                             containing_scope,
                             NO_FILE_METADATA,
-                            codemap::DUMMY_SP)
+                            syntax_pos::DUMMY_SP)
 }
 
 pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
@@ -853,17 +853,19 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     metadata
 }
 
-pub fn file_metadata(cx: &CrateContext, full_path: &str) -> DIFile {
+pub fn file_metadata(cx: &CrateContext, path: &str, full_path: &Option<String>) -> DIFile {
     // FIXME (#9639): This needs to handle non-utf8 paths
     let work_dir = cx.sess().working_dir.to_str().unwrap();
     let file_name =
-        if full_path.starts_with(work_dir) {
-            &full_path[work_dir.len() + 1..full_path.len()]
-        } else {
-            full_path
-        };
+        full_path.as_ref().map(|p| p.as_str()).unwrap_or_else(|| {
+            if path.starts_with(work_dir) {
+                &path[work_dir.len() + 1..path.len()]
+            } else {
+                path
+            }
+        });
 
-    file_metadata_(cx, full_path, file_name, &work_dir)
+    file_metadata_(cx, path, file_name, &work_dir)
 }
 
 pub fn unknown_file_metadata(cx: &CrateContext) -> DIFile {
@@ -874,9 +876,8 @@ pub fn unknown_file_metadata(cx: &CrateContext) -> DIFile {
 }
 
 fn file_metadata_(cx: &CrateContext, key: &str, file_name: &str, work_dir: &str) -> DIFile {
-    match debug_context(cx).created_files.borrow().get(key) {
-        Some(file_metadata) => return *file_metadata,
-        None => ()
+    if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(key) {
+        return *file_metadata;
     }
 
     debug!("file_metadata: file_name: {}, work_dir: {}", file_name, work_dir);
@@ -1384,7 +1385,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                                             &[sole_struct_member_description],
                                             self.containing_scope,
                                             self.file_metadata,
-                                            codemap::DUMMY_SP);
+                                            syntax_pos::DUMMY_SP);
 
                 // Encode the information about the null variant in the union
                 // member's name.
@@ -1614,7 +1615,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 let discriminant_base_type_metadata =
                     type_metadata(cx,
                                   adt::ty_of_inttype(cx.tcx(), inttype),
-                                  codemap::DUMMY_SP);
+                                  syntax_pos::DUMMY_SP);
                 let discriminant_name = get_enum_discriminant_name(cx, enum_def_id);
 
                 let name = CString::new(discriminant_name.as_bytes()).unwrap();
@@ -1848,9 +1849,9 @@ pub fn create_global_var_metadata(cx: &CrateContext,
     let node_def_id = cx.tcx().map.local_def_id(node_id);
     let (var_scope, span) = get_namespace_and_span_for_item(cx, node_def_id);
 
-    let (file_metadata, line_number) = if span != codemap::DUMMY_SP {
+    let (file_metadata, line_number) = if span != syntax_pos::DUMMY_SP {
         let loc = span_start(cx, span);
-        (file_metadata(cx, &loc.file.name), loc.line as c_uint)
+        (file_metadata(cx, &loc.file.name, &loc.file.abs_path), loc.line as c_uint)
     } else {
         (NO_FILE_METADATA, UNKNOWN_LINE_NUMBER)
     };
@@ -1889,11 +1890,8 @@ pub fn create_local_var_metadata(bcx: Block, local: &hir::Local) {
         return;
     }
 
-    let cx = bcx.ccx();
-    let def_map = &cx.tcx().def_map;
     let locals = bcx.fcx.lllocals.borrow();
-
-    pat_util::pat_bindings(def_map, &local.pat, |_, node_id, span, var_name| {
+    pat_util::pat_bindings(&local.pat, |_, node_id, span, var_name| {
         let datum = match locals.get(&node_id) {
             Some(datum) => datum,
             None => {
@@ -1945,7 +1943,7 @@ pub fn create_captured_var_metadata<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         }
         Some(hir_map::NodeLocal(pat)) => {
             match pat.node {
-                PatKind::Ident(_, ref path1, _) => {
+                PatKind::Binding(_, ref path1, _) => {
                     path1.node
                 }
                 _ => {
@@ -2062,7 +2060,6 @@ pub fn create_argument_metadata(bcx: Block, arg: &hir::Arg) {
         return;
     }
 
-    let def_map = &bcx.tcx().def_map;
     let scope_metadata = bcx
                          .fcx
                          .debug_context
@@ -2070,7 +2067,7 @@ pub fn create_argument_metadata(bcx: Block, arg: &hir::Arg) {
                          .fn_metadata;
     let locals = bcx.fcx.lllocals.borrow();
 
-    pat_util::pat_bindings(def_map, &arg.pat, |_, node_id, span, var_name| {
+    pat_util::pat_bindings(&arg.pat, |_, node_id, span, var_name| {
         let datum = match locals.get(&node_id) {
             Some(v) => v,
             None => {
index 6c1bd715f13a50911e3b909461a4e6bcf0279b38..8c5b3ed54c2f58e04acf399a12b0171084154842 100644 (file)
@@ -42,8 +42,8 @@ use std::cell::{Cell, RefCell};
 use std::ffi::CString;
 use std::ptr;
 
-use syntax::codemap::{Span, Pos};
-use syntax::{ast, codemap};
+use syntax_pos::{self, Span, Pos};
+use syntax::ast;
 use syntax::attr::IntType;
 
 pub mod gdb;
@@ -242,12 +242,12 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     let (containing_scope, span) = get_containing_scope_and_span(cx, instance);
 
     // This can be the case for functions inlined from another crate
-    if span == codemap::DUMMY_SP {
+    if span == syntax_pos::DUMMY_SP {
         return FunctionDebugContext::FunctionWithoutDebugInfo;
     }
 
     let loc = span_start(cx, span);
-    let file_metadata = file_metadata(cx, &loc.file.name);
+    let file_metadata = file_metadata(cx, &loc.file.name, &loc.file.abs_path);
 
     let function_type_metadata = unsafe {
         let fn_signature = get_function_signature(cx, sig, abi);
@@ -327,7 +327,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         signature.push(match sig.output {
             ty::FnConverging(ret_ty) => match ret_ty.sty {
                 ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(),
-                _ => type_metadata(cx, ret_ty, codemap::DUMMY_SP)
+                _ => type_metadata(cx, ret_ty, syntax_pos::DUMMY_SP)
             },
             ty::FnDiverging => diverging_type_metadata(cx)
         });
@@ -340,13 +340,13 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
         // Arguments types
         for &argument_type in inputs {
-            signature.push(type_metadata(cx, argument_type, codemap::DUMMY_SP));
+            signature.push(type_metadata(cx, argument_type, syntax_pos::DUMMY_SP));
         }
 
         if abi == Abi::RustCall && !sig.inputs.is_empty() {
             if let ty::TyTuple(args) = sig.inputs[sig.inputs.len() - 1].sty {
                 for &argument_type in args {
-                    signature.push(type_metadata(cx, argument_type, codemap::DUMMY_SP));
+                    signature.push(type_metadata(cx, argument_type, syntax_pos::DUMMY_SP));
                 }
             }
         }
@@ -386,7 +386,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         let template_params: Vec<_> = if cx.sess().opts.debuginfo == FullDebugInfo {
             generics.types.as_slice().iter().enumerate().map(|(i, param)| {
                 let actual_type = cx.tcx().normalize_associated_type(&actual_types[i]);
-                let actual_type_metadata = type_metadata(cx, actual_type, codemap::DUMMY_SP);
+                let actual_type_metadata = type_metadata(cx, actual_type, syntax_pos::DUMMY_SP);
                 let name = CString::new(param.name.as_str().as_bytes()).unwrap();
                 unsafe {
                     llvm::LLVMDIBuilderCreateTemplateTypeParameter(
@@ -420,7 +420,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 let impl_self_ty = monomorphize::apply_param_substs(cx.tcx(),
                                                                     instance.substs,
                                                                     &impl_self_ty);
-                Some(type_metadata(cx, impl_self_ty, codemap::DUMMY_SP))
+                Some(type_metadata(cx, impl_self_ty, syntax_pos::DUMMY_SP))
             } else {
                 // For trait method impls we still use the "parallel namespace"
                 // strategy
@@ -441,7 +441,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         // Try to get some span information, if we have an inlined item.
         let definition_span = match cx.external().borrow().get(&instance.def) {
             Some(&Some(node_id)) => cx.tcx().map.span(node_id),
-            _ => cx.tcx().map.def_id_span(instance.def, codemap::DUMMY_SP)
+            _ => cx.tcx().map.def_id_span(instance.def, syntax_pos::DUMMY_SP)
         };
 
         (containing_scope, definition_span)
@@ -476,8 +476,9 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                  span: Span) {
     let cx: &CrateContext = bcx.ccx();
 
-    let filename = span_start(cx, span).file.name.clone();
-    let file_metadata = file_metadata(cx, &filename[..]);
+    let file = span_start(cx, span).file;
+    let filename = file.name.clone();
+    let file_metadata = file_metadata(cx, &filename[..], &file.abs_path);
 
     let loc = span_start(cx, span);
     let type_metadata = type_metadata(cx, variable_type, span);
index fc31eaa4e74a7961507f2ea92f42623887401819..167229ddfd9856eb24b5f9c42165ec7fd00e888a 100644 (file)
@@ -22,7 +22,7 @@ use common::CrateContext;
 use libc::c_uint;
 use std::ffi::CString;
 use std::ptr;
-use syntax::codemap::DUMMY_SP;
+use syntax_pos::DUMMY_SP;
 
 pub fn mangled_name_of_item(ccx: &CrateContext, def_id: DefId, extra: &str) -> String {
     fn fill_nested(ccx: &CrateContext, def_id: DefId, extra: &str, output: &mut String) {
@@ -72,7 +72,7 @@ pub fn item_namespace(ccx: &CrateContext, def_id: DefId) -> DIScope {
     let span = ccx.tcx().map.def_id_span(def_id, DUMMY_SP);
     let (file, line) = if span != DUMMY_SP {
         let loc = span_start(ccx, span);
-        (file_metadata(ccx, &loc.file.name), loc.line as c_uint)
+        (file_metadata(ccx, &loc.file.name, &loc.file.abs_path), loc.line as c_uint)
     } else {
         (NO_FILE_METADATA, UNKNOWN_LINE_NUMBER)
     };
index 6b00c1bb1a85582b39484ffdab05b8121c3ae0a6..9726001b4d42b71828a51298eee9127a817eff9d 100644 (file)
@@ -21,8 +21,8 @@ use common::{NodeIdAndSpan, CrateContext, FunctionContext};
 
 use libc::c_uint;
 use std::ptr;
-use syntax::codemap::{Span, Pos};
-use syntax::{ast, codemap};
+use syntax_pos::{self, Span, Pos};
+use syntax::ast;
 
 pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                                     node_id: ast::NodeId,
@@ -70,7 +70,7 @@ pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
             if !bytes.is_empty() && &bytes[bytes.len()-1..] == b"}" {
                 cleanup_span = Span {
-                    lo: node_span.hi - codemap::BytePos(1),
+                    lo: node_span.hi - syntax_pos::BytePos(1),
                     hi: node_span.hi,
                     expn_id: node_span.expn_id
                 };
index 3fd979371843434db7542198803e8dc028639143..1e0afa4534b15f5f71803582e62031bf4cfede69 100644 (file)
@@ -21,8 +21,8 @@ use machine;
 use common::{CrateContext, FunctionContext};
 use type_::Type;
 
-use syntax::codemap::Span;
-use syntax::{ast, codemap};
+use syntax_pos::{self, Span};
+use syntax::ast;
 
 pub fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool
 {
@@ -44,8 +44,8 @@ pub fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray {
     };
 }
 
-/// Return codemap::Loc corresponding to the beginning of the span
-pub fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc {
+/// Return syntax_pos::Loc corresponding to the beginning of the span
+pub fn span_start(cx: &CrateContext, span: Span) -> syntax_pos::Loc {
     cx.sess().codemap().lookup_char_pos(span.lo)
 }
 
@@ -88,7 +88,7 @@ pub fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: DefId)
     // Try to get some span information, if we have an inlined item.
     let definition_span = match cx.external().borrow().get(&def_id) {
         Some(&Some(node_id)) => cx.tcx().map.span(node_id),
-        _ => cx.tcx().map.def_id_span(def_id, codemap::DUMMY_SP)
+        _ => cx.tcx().map.def_id_span(def_id, syntax_pos::DUMMY_SP)
     };
 
     (containing_scope, definition_span)
index d9de673db27caba2d9dbc5d46e29b2b827095863..d36878b03322a30fbe24c4c98c66bdaed168a242 100644 (file)
@@ -15,7 +15,7 @@ register_long_diagnostics! {
 E0510: r##"
 `return_address` was used in an invalid context. Erroneous code example:
 
-```compile_fail
+```ignore
 #![feature(intrinsics)]
 
 extern "rust-intrinsic" {
@@ -54,7 +54,7 @@ E0511: r##"
 Invalid monomorphization of an intrinsic function was used. Erroneous code
 example:
 
-```compile_fail
+```ignore
 #![feature(platform_intrinsics)]
 
 extern "platform-intrinsic" {
index 36a593a546b9c045172c9fa94894e99d228c9aa8..71c6cba9cc22a6866aa221368c44cf21950a94ff 100644 (file)
@@ -81,8 +81,9 @@ use type_::Type;
 
 use rustc::hir;
 
-use syntax::{ast, codemap};
+use syntax::ast;
 use syntax::parse::token::InternedString;
+use syntax_pos;
 use std::fmt;
 use std::mem;
 
@@ -153,7 +154,7 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             // have side effects. This seems to be reached through tuple struct constructors being
             // passed zero-size constants.
             if let hir::ExprPath(..) = expr.node {
-                match bcx.def(expr.id) {
+                match bcx.tcx().expect_def(expr.id) {
                     Def::Const(_) | Def::AssociatedConst(_) => {
                         assert!(type_is_zero_size(bcx.ccx(), bcx.tcx().node_id_to_type(expr.id)));
                         return bcx;
@@ -172,7 +173,7 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             // `[x; N]` somewhere within.
             match expr.node {
                 hir::ExprPath(..) => {
-                    match bcx.def(expr.id) {
+                    match bcx.tcx().expect_def(expr.id) {
                         Def::Const(did) | Def::AssociatedConst(did) => {
                             let empty_substs = bcx.tcx().mk_substs(Substs::empty());
                             let const_expr = consts::get_const_expr(bcx.ccx(), did, expr,
@@ -454,7 +455,7 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 }
 
 fn coerce_unsized<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                              span: codemap::Span,
+                              span: syntax_pos::Span,
                               source: Datum<'tcx, Rvalue>,
                               target: Datum<'tcx, Rvalue>)
                               -> Block<'blk, 'tcx> {
@@ -651,7 +652,7 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             trans(bcx, &e)
         }
         hir::ExprPath(..) => {
-            let var = trans_var(bcx, bcx.def(expr.id));
+            let var = trans_var(bcx, bcx.tcx().expect_def(expr.id));
             DatumBlock::new(bcx, var.to_expr_datum())
         }
         hir::ExprField(ref base, name) => {
@@ -1073,7 +1074,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             trans_into(bcx, &e, dest)
         }
         hir::ExprPath(..) => {
-            trans_def_dps_unadjusted(bcx, expr, bcx.def(expr.id), dest)
+            trans_def_dps_unadjusted(bcx, expr, bcx.tcx().expect_def(expr.id), dest)
         }
         hir::ExprIf(ref cond, ref thn, ref els) => {
             controlflow::trans_if(bcx, expr.id, &cond, &thn, els.as_ref().map(|e| &**e), dest)
@@ -1265,7 +1266,7 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                             fields: &[hir::Field],
                             base: Option<&hir::Expr>,
-                            expr_span: codemap::Span,
+                            expr_span: syntax_pos::Span,
                             expr_id: ast::NodeId,
                             ty: Ty<'tcx>,
                             dest: Dest) -> Block<'blk, 'tcx> {
@@ -1978,7 +1979,7 @@ fn auto_ref<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     // Compute final type. Note that we are loose with the region and
     // mutability, since those things don't matter in trans.
     let referent_ty = lv_datum.ty;
-    let ptr_ty = bcx.tcx().mk_imm_ref(bcx.tcx().mk_region(ty::ReStatic), referent_ty);
+    let ptr_ty = bcx.tcx().mk_imm_ref(bcx.tcx().mk_region(ty::ReErased), referent_ty);
 
     // Construct the resulting datum. The right datum to return here would be an Lvalue datum,
     // because there is cleanup scheduled and the datum doesn't own the data, but for thin pointers
@@ -2155,11 +2156,13 @@ impl OverflowOpViaIntrinsic {
 
         let new_sty = match ty.sty {
             TyInt(Is) => match &tcx.sess.target.target.target_pointer_width[..] {
+                "16" => TyInt(I16),
                 "32" => TyInt(I32),
                 "64" => TyInt(I64),
                 _ => bug!("unsupported target word size")
             },
             TyUint(Us) => match &tcx.sess.target.target.target_pointer_width[..] {
+                "16" => TyUint(U16),
                 "32" => TyUint(U32),
                 "64" => TyUint(U64),
                 _ => bug!("unsupported target word size")
@@ -2218,6 +2221,8 @@ impl OverflowOpViaIntrinsic {
                                         rhs: ValueRef,
                                         binop_debug_loc: DebugLoc)
                                         -> (Block<'blk, 'tcx>, ValueRef) {
+        use rustc_const_math::{ConstMathErr, Op};
+
         let llfn = self.to_intrinsic(bcx, lhs_t);
 
         let val = Call(bcx, llfn, &[lhs, rhs], binop_debug_loc);
@@ -2228,13 +2233,19 @@ impl OverflowOpViaIntrinsic {
                         binop_debug_loc);
 
         let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
-        Call(bcx, expect, &[cond, C_integral(Type::i1(bcx.ccx()), 0, false)],
-             binop_debug_loc);
+        let expected = Call(bcx, expect, &[cond, C_bool(bcx.ccx(), false)],
+                            binop_debug_loc);
+
+        let op = match *self {
+            OverflowOpViaIntrinsic::Add => Op::Add,
+            OverflowOpViaIntrinsic::Sub => Op::Sub,
+            OverflowOpViaIntrinsic::Mul => Op::Mul
+        };
 
         let bcx =
-            base::with_cond(bcx, cond, |bcx|
+            base::with_cond(bcx, expected, |bcx|
                 controlflow::trans_fail(bcx, info,
-                    InternedString::new("arithmetic operation overflowed")));
+                    InternedString::new(ConstMathErr::Overflow(op).description())));
 
         (bcx, result)
     }
@@ -2250,6 +2261,8 @@ impl OverflowOpViaInputCheck {
                                           binop_debug_loc: DebugLoc)
                                           -> (Block<'blk, 'tcx>, ValueRef)
     {
+        use rustc_const_math::{ConstMathErr, Op};
+
         let lhs_llty = val_ty(lhs);
         let rhs_llty = val_ty(rhs);
 
@@ -2264,16 +2277,16 @@ impl OverflowOpViaInputCheck {
 
         let outer_bits = And(bcx, rhs, invert_mask, binop_debug_loc);
         let cond = build_nonzero_check(bcx, outer_bits, binop_debug_loc);
-        let result = match *self {
+        let (result, op) = match *self {
             OverflowOpViaInputCheck::Shl =>
-                build_unchecked_lshift(bcx, lhs, rhs, binop_debug_loc),
+                (build_unchecked_lshift(bcx, lhs, rhs, binop_debug_loc), Op::Shl),
             OverflowOpViaInputCheck::Shr =>
-                build_unchecked_rshift(bcx, lhs_t, lhs, rhs, binop_debug_loc),
+                (build_unchecked_rshift(bcx, lhs_t, lhs, rhs, binop_debug_loc), Op::Shr)
         };
         let bcx =
             base::with_cond(bcx, cond, |bcx|
                 controlflow::trans_fail(bcx, info,
-                    InternedString::new("shift operation overflowed")));
+                    InternedString::new(ConstMathErr::Overflow(op).description())));
 
         (bcx, result)
     }
@@ -2361,7 +2374,7 @@ fn expr_kind<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, expr: &hir::Expr) -> ExprKin
 
     match expr.node {
         hir::ExprPath(..) => {
-            match tcx.resolve_expr(expr) {
+            match tcx.expect_def(expr.id) {
                 // Put functions and ctors with the ADTs, as they
                 // are zero-sized, so DPS is the cheapest option.
                 Def::Struct(..) | Def::Variant(..) |
index 10e33195305f616bfbf29975417b028ae413d9d4..ac23d713d2727936f91b8cdc042b656363920960 100644 (file)
 
 use std;
 
+use attributes;
 use back::symbol_names;
 use llvm;
 use llvm::{ValueRef, get_param};
 use middle::lang_items::ExchangeFreeFnLangItem;
 use rustc::ty::subst::{Substs};
 use rustc::traits;
-use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use abi::{Abi, FnType};
 use adt;
 use adt::GetDtorType; // for tcx.dtor_type()
@@ -42,7 +43,7 @@ use type_::Type;
 use value::Value;
 
 use arena::TypedArena;
-use syntax::codemap::DUMMY_SP;
+use syntax_pos::DUMMY_SP;
 
 pub fn trans_exchange_free_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                            v: ValueRef,
@@ -52,7 +53,7 @@ pub fn trans_exchange_free_dyn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                            -> Block<'blk, 'tcx> {
     let _icx = push_ctxt("trans_exchange_free");
 
-    let def_id = langcall(bcx, None, "", ExchangeFreeFnLangItem);
+    let def_id = langcall(bcx.tcx(), None, "", ExchangeFreeFnLangItem);
     let args = [PointerCast(bcx, v, Type::i8p(bcx.ccx())), size, align];
     Callee::def(bcx.ccx(), def_id, bcx.tcx().mk_substs(Substs::empty()))
         .call(bcx, debug_loc, ArgVals(&args), None).bcx
@@ -96,10 +97,12 @@ pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
 pub fn get_drop_glue_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                     t: Ty<'tcx>) -> Ty<'tcx> {
+    assert!(t.is_normalized_for_trans());
+
     // Even if there is no dtor for t, there might be one deeper down and we
     // might need to pass in the vtable ptr.
     if !type_is_sized(tcx, t) {
-        return tcx.erase_regions(&t);
+        return t;
     }
 
     // FIXME (#22815): note that type_needs_drop conservatively
@@ -123,11 +126,11 @@ pub fn get_drop_glue_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                     // `Box<ZeroSizeType>` does not allocate.
                     tcx.types.i8
                 } else {
-                    tcx.erase_regions(&t)
+                    t
                 }
             })
         }
-        _ => tcx.erase_regions(&t)
+        _ => t
     }
 }
 
@@ -270,6 +273,7 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let fn_nm = symbol_names::internal_name_from_type_and_suffix(ccx, t, suffix);
     assert!(declare::get_defined_value(ccx, &fn_nm).is_none());
     let llfn = declare::declare_cfn(ccx, &fn_nm, llfnty);
+    attributes::set_frame_pointer_elimination(ccx, llfn);
     ccx.available_drop_glues().borrow_mut().insert(g, fn_nm);
     ccx.drop_glues().borrow_mut().insert(g, llfn);
 
index 640ac25a5e31cc666556f6982c71dd20073a178a..bd24647edf00b4b0f349c7e60f90bbab479a2dbe 100644 (file)
@@ -44,7 +44,7 @@ use syntax::ptr::P;
 use syntax::parse::token;
 
 use rustc::session::Session;
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
 
 use std::cmp::Ordering;
 
@@ -1577,6 +1577,7 @@ fn int_type_width_signed<'tcx>(sty: &ty::TypeVariants<'tcx>, ccx: &CrateContext)
         TyInt(t) => Some((match t {
             ast::IntTy::Is => {
                 match &ccx.tcx().sess.target.target.target_pointer_width[..] {
+                    "16" => 16,
                     "32" => 32,
                     "64" => 64,
                     tws => bug!("Unsupported target word size for isize: {}", tws),
@@ -1590,6 +1591,7 @@ fn int_type_width_signed<'tcx>(sty: &ty::TypeVariants<'tcx>, ccx: &CrateContext)
         TyUint(t) => Some((match t {
             ast::UintTy::Us => {
                 match &ccx.tcx().sess.target.target.target_pointer_width[..] {
+                    "16" => 16,
                     "32" => 32,
                     "64" => 64,
                     tws => bug!("Unsupported target word size for usize: {}", tws),
index bccb5aa050b511c60ffe7356efd6d3fd47b4d6c3..9cb5d8b6ad62a2e43781bb64b3b7806f5fabe425 100644 (file)
@@ -28,7 +28,6 @@
 #![feature(const_fn)]
 #![feature(custom_attribute)]
 #![allow(unused_attributes)]
-#![feature(iter_arith)]
 #![feature(libc)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
@@ -48,7 +47,6 @@ extern crate rustc_back;
 extern crate rustc_data_structures;
 extern crate rustc_incremental;
 pub extern crate rustc_llvm as llvm;
-extern crate rustc_mir;
 extern crate rustc_platform_intrinsics as intrinsics;
 extern crate serialize;
 extern crate rustc_const_math;
@@ -56,6 +54,8 @@ extern crate rustc_const_eval;
 
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
+extern crate syntax_pos;
+extern crate rustc_errors as errors;
 
 pub use rustc::session;
 pub use rustc::middle;
@@ -145,6 +145,7 @@ pub struct CrateTranslation {
     pub metadata: Vec<u8>,
     pub reachable: Vec<String>,
     pub no_builtins: bool,
+    pub linker_info: back::linker::LinkerInfo
 }
 
 __build_diagnostic_array! { librustc_trans, DIAGNOSTICS }
index 64ee18fccef37f32a1b63be1a47d4b945624fea2..ac6af8d66e19ff55b24c84ce9e099147071c4506 100644 (file)
@@ -10,6 +10,7 @@
 
 use std::rc::Rc;
 
+use attributes;
 use arena::TypedArena;
 use back::symbol_names;
 use llvm::{ValueRef, get_params};
@@ -35,7 +36,7 @@ use value::Value;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 
 use syntax::ast::Name;
-use syntax::codemap::DUMMY_SP;
+use syntax_pos::DUMMY_SP;
 
 // drop_glue pointer, size, align.
 const VTABLE_OFFSET: usize = 3;
@@ -91,6 +92,7 @@ pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
     let function_name =
         symbol_names::internal_name_from_type_and_suffix(ccx, method_ty, "object_shim");
     let llfn = declare::define_internal_fn(ccx, &function_name, method_ty);
+    attributes::set_frame_pointer_elimination(ccx, llfn);
 
     let (block_arena, fcx): (TypedArena<_>, FunctionContext);
     block_arena = TypedArena::new();
@@ -269,7 +271,7 @@ pub fn get_vtable_methods<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             // the method may have some early-bound lifetimes, add
             // regions for those
             let num_dummy_regions = trait_method_type.generics.regions.len(FnSpace);
-            let dummy_regions = vec![ty::ReStatic; num_dummy_regions];
+            let dummy_regions = vec![ty::ReErased; num_dummy_regions];
             let method_substs = substs.clone()
                                       .with_method(vec![], dummy_regions);
             let method_substs = tcx.mk_substs(method_substs);
index 0b88ba554da678fa0d1aa79978fe46b926b3ea5a..dac7afab6e38b4d85de41d18f1c2fae4b837c9d4 100644 (file)
@@ -8,25 +8,33 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! An analysis to determine which temporaries require allocas and
+//! An analysis to determine which locals require allocas and
 //! which do not.
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc::mir::repr as mir;
+use rustc::mir::repr::TerminatorKind;
 use rustc::mir::visit::{Visitor, LvalueContext};
+use rustc::mir::traversal;
 use common::{self, Block, BlockAndBuilder};
+use glue;
 use super::rvalue;
 
-pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
-                               mir: &mir::Mir<'tcx>) -> BitVector {
+pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>,
+                                 mir: &mir::Mir<'tcx>) -> BitVector {
     let bcx = bcx.build();
-    let mut analyzer = TempAnalyzer::new(mir, &bcx, mir.temp_decls.len());
+    let mut analyzer = LocalAnalyzer::new(mir, &bcx);
 
     analyzer.visit_mir(mir);
 
-    for (index, temp_decl) in mir.temp_decls.iter().enumerate() {
-        let ty = bcx.monomorphize(&temp_decl.ty);
-        debug!("temp {:?} has type {:?}", index, ty);
+    let local_types = mir.arg_decls.iter().map(|a| a.ty)
+               .chain(mir.var_decls.iter().map(|v| v.ty))
+               .chain(mir.temp_decls.iter().map(|t| t.ty))
+               .chain(mir.return_ty.maybe_converging());
+    for (index, ty) in local_types.enumerate() {
+        let ty = bcx.monomorphize(&ty);
+        debug!("local {} has type {:?}", index, ty);
         if ty.is_scalar() ||
             ty.is_unique() ||
             ty.is_region_ptr() ||
@@ -37,6 +45,8 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
             // in an ValueRef without an alloca.
             assert!(common::type_is_immediate(bcx.ccx(), ty) ||
                     common::type_is_fat_ptr(bcx.tcx(), ty));
+        } else if common::type_is_imm_pair(bcx.ccx(), ty) {
+            // We allow pairs and uses of any of their 2 fields.
         } else {
             // These sorts of types require an alloca. Note that
             // type_is_immediate() may *still* be true, particularly
@@ -44,93 +54,242 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
             // (e.g. structs) into an alloca unconditionally, just so
             // that we don't have to deal with having two pathways
             // (gep vs extractvalue etc).
-            analyzer.mark_as_lvalue(index);
+            analyzer.mark_as_lvalue(mir::Local::new(index));
         }
     }
 
-    analyzer.lvalue_temps
+    analyzer.lvalue_locals
 }
 
-struct TempAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> {
+struct LocalAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> {
     mir: &'mir mir::Mir<'tcx>,
     bcx: &'mir BlockAndBuilder<'bcx, 'tcx>,
-    lvalue_temps: BitVector,
+    lvalue_locals: BitVector,
     seen_assigned: BitVector
 }
 
-impl<'mir, 'bcx, 'tcx> TempAnalyzer<'mir, 'bcx, 'tcx> {
+impl<'mir, 'bcx, 'tcx> LocalAnalyzer<'mir, 'bcx, 'tcx> {
     fn new(mir: &'mir mir::Mir<'tcx>,
-           bcx: &'mir BlockAndBuilder<'bcx, 'tcx>,
-           temp_count: usize) -> TempAnalyzer<'mir, 'bcx, 'tcx> {
-        TempAnalyzer {
+           bcx: &'mir BlockAndBuilder<'bcx, 'tcx>)
+           -> LocalAnalyzer<'mir, 'bcx, 'tcx> {
+        let local_count = mir.count_locals();
+        LocalAnalyzer {
             mir: mir,
             bcx: bcx,
-            lvalue_temps: BitVector::new(temp_count),
-            seen_assigned: BitVector::new(temp_count)
+            lvalue_locals: BitVector::new(local_count),
+            seen_assigned: BitVector::new(local_count)
         }
     }
 
-    fn mark_as_lvalue(&mut self, temp: usize) {
-        debug!("marking temp {} as lvalue", temp);
-        self.lvalue_temps.insert(temp);
+    fn mark_as_lvalue(&mut self, local: mir::Local) {
+        debug!("marking {:?} as lvalue", local);
+        self.lvalue_locals.insert(local.index());
     }
 
-    fn mark_assigned(&mut self, temp: usize) {
-        if !self.seen_assigned.insert(temp) {
-            self.mark_as_lvalue(temp);
+    fn mark_assigned(&mut self, local: mir::Local) {
+        if !self.seen_assigned.insert(local.index()) {
+            self.mark_as_lvalue(local);
         }
     }
 }
 
-impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
+impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
     fn visit_assign(&mut self,
                     block: mir::BasicBlock,
                     lvalue: &mir::Lvalue<'tcx>,
                     rvalue: &mir::Rvalue<'tcx>) {
         debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue);
 
-        match *lvalue {
-            mir::Lvalue::Temp(index) => {
-                self.mark_assigned(index as usize);
-                if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) {
-                    self.mark_as_lvalue(index as usize);
-                }
-            }
-            _ => {
-                self.visit_lvalue(lvalue, LvalueContext::Store);
+        if let Some(index) = self.mir.local_index(lvalue) {
+            self.mark_assigned(index);
+            if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) {
+                self.mark_as_lvalue(index);
             }
+        } else {
+            self.visit_lvalue(lvalue, LvalueContext::Store);
         }
 
         self.visit_rvalue(rvalue);
     }
 
+    fn visit_terminator_kind(&mut self,
+                             block: mir::BasicBlock,
+                             kind: &mir::TerminatorKind<'tcx>) {
+        match *kind {
+            mir::TerminatorKind::Call {
+                func: mir::Operand::Constant(mir::Constant {
+                    literal: mir::Literal::Item { def_id, .. }, ..
+                }),
+                ref args, ..
+            } if Some(def_id) == self.bcx.tcx().lang_items.box_free_fn() => {
+                // box_free(x) shares with `drop x` the property that it
+                // is not guaranteed to be statically dominated by the
+                // definition of x, so x must always be in an alloca.
+                if let mir::Operand::Consume(ref lvalue) = args[0] {
+                    self.visit_lvalue(lvalue, LvalueContext::Drop);
+                }
+            }
+            _ => {}
+        }
+
+        self.super_terminator_kind(block, kind);
+    }
+
     fn visit_lvalue(&mut self,
                     lvalue: &mir::Lvalue<'tcx>,
                     context: LvalueContext) {
         debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context);
 
-        match *lvalue {
-            mir::Lvalue::Temp(index) => {
-                match context {
-                    LvalueContext::Call => {
-                        self.mark_assigned(index as usize);
+        // Allow uses of projections of immediate pair fields.
+        if let mir::Lvalue::Projection(ref proj) = *lvalue {
+            if self.mir.local_index(&proj.base).is_some() {
+                let ty = self.mir.lvalue_ty(self.bcx.tcx(), &proj.base);
+                let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx()));
+                if common::type_is_imm_pair(self.bcx.ccx(), ty) {
+                    if let mir::ProjectionElem::Field(..) = proj.elem {
+                        if let LvalueContext::Consume = context {
+                            return;
+                        }
                     }
-                    LvalueContext::Consume => {
-                    }
-                    LvalueContext::Store |
-                    LvalueContext::Drop |
-                    LvalueContext::Inspect |
-                    LvalueContext::Borrow { .. } |
-                    LvalueContext::Slice { .. } |
-                    LvalueContext::Projection => {
-                        self.mark_as_lvalue(index as usize);
+                }
+            }
+        }
+
+        if let Some(index) = self.mir.local_index(lvalue) {
+            match context {
+                LvalueContext::Call => {
+                    self.mark_assigned(index);
+                }
+                LvalueContext::Consume => {
+                }
+                LvalueContext::Store |
+                LvalueContext::Inspect |
+                LvalueContext::Borrow { .. } |
+                LvalueContext::Slice { .. } |
+                LvalueContext::Projection => {
+                    self.mark_as_lvalue(index);
+                }
+                LvalueContext::Drop => {
+                    let ty = self.mir.lvalue_ty(self.bcx.tcx(), lvalue);
+                    let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx()));
+
+                    // Only need the lvalue if we're actually dropping it.
+                    if glue::type_needs_drop(self.bcx.tcx(), ty) {
+                        self.mark_as_lvalue(index);
                     }
                 }
             }
-            _ => {
+        }
+
+        // A deref projection only reads the pointer, never needs the lvalue.
+        if let mir::Lvalue::Projection(ref proj) = *lvalue {
+            if let mir::ProjectionElem::Deref = proj.elem {
+                return self.visit_lvalue(&proj.base, LvalueContext::Consume);
             }
         }
 
         self.super_lvalue(lvalue, context);
     }
 }
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum CleanupKind {
+    NotCleanup,
+    Funclet,
+    Internal { funclet: mir::BasicBlock }
+}
+
+pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
+                                mir: &mir::Mir<'tcx>)
+                                -> IndexVec<mir::BasicBlock, CleanupKind>
+{
+    fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
+                              mir: &mir::Mir<'tcx>) {
+        for (bb, data) in mir.basic_blocks().iter_enumerated() {
+            match data.terminator().kind {
+                TerminatorKind::Goto { .. } |
+                TerminatorKind::Resume |
+                TerminatorKind::Return |
+                TerminatorKind::Unreachable |
+                TerminatorKind::If { .. } |
+                TerminatorKind::Switch { .. } |
+                TerminatorKind::SwitchInt { .. } => {
+                    /* nothing to do */
+                }
+                TerminatorKind::Call { cleanup: unwind, .. } |
+                TerminatorKind::Assert { cleanup: unwind, .. } |
+                TerminatorKind::DropAndReplace { unwind, .. } |
+                TerminatorKind::Drop { unwind, .. } => {
+                    if let Some(unwind) = unwind {
+                        debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
+                               bb, data, unwind);
+                        result[unwind] = CleanupKind::Funclet;
+                    }
+                }
+            }
+        }
+    }
+
+    fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
+                       mir: &mir::Mir<'tcx>) {
+        let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());
+
+        let mut set_successor = |funclet: mir::BasicBlock, succ| {
+            match funclet_succs[funclet] {
+                ref mut s @ None => {
+                    debug!("set_successor: updating successor of {:?} to {:?}",
+                           funclet, succ);
+                    *s = Some(succ);
+                },
+                Some(s) => if s != succ {
+                    span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
+                              funclet, s, succ);
+                }
+            }
+        };
+
+        for (bb, data) in traversal::reverse_postorder(mir) {
+            let funclet = match result[bb] {
+                CleanupKind::NotCleanup => continue,
+                CleanupKind::Funclet => bb,
+                CleanupKind::Internal { funclet } => funclet,
+            };
+
+            debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
+                   bb, data, result[bb], funclet);
+
+            for &succ in data.terminator().successors().iter() {
+                let kind = result[succ];
+                debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
+                       funclet, succ, kind);
+                match kind {
+                    CleanupKind::NotCleanup => {
+                        result[succ] = CleanupKind::Internal { funclet: funclet };
+                    }
+                    CleanupKind::Funclet => {
+                        set_successor(funclet, succ);
+                    }
+                    CleanupKind::Internal { funclet: succ_funclet } => {
+                        if funclet != succ_funclet {
+                            // `succ` has 2 different funclet going into it, so it must
+                            // be a funclet by itself.
+
+                            debug!("promoting {:?} to a funclet and updating {:?}", succ,
+                                   succ_funclet);
+                            result[succ] = CleanupKind::Funclet;
+                            set_successor(succ_funclet, succ);
+                            set_successor(funclet, succ);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());
+
+    discover_masters(&mut result, mir);
+    propagate(&mut result, mir);
+    debug!("cleanup_kinds: result={:?}", result);
+    result
+}
index 4e3386bc736775440d22bb3f1fd6d015dbe9c392..7a7f1901736c5939af7d3787154d1b7a0ff22fbf 100644 (file)
@@ -8,7 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use llvm::{self, BasicBlockRef, ValueRef, OperandBundleDef};
+use llvm::{self, ValueRef};
+use rustc_const_eval::ErrKind;
+use rustc::middle::lang_items;
 use rustc::ty;
 use rustc::mir::repr as mir;
 use abi::{Abi, FnType, ArgType};
@@ -16,7 +18,9 @@ use adt;
 use base;
 use build;
 use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual};
-use common::{self, type_is_fat_ptr, Block, BlockAndBuilder, C_undef};
+use common::{self, Block, BlockAndBuilder, LandingPad};
+use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef};
+use consts;
 use debuginfo::DebugLoc;
 use Disr;
 use machine::{llalign_of_min, llbitsize_of_real};
@@ -24,32 +28,75 @@ use meth;
 use type_of;
 use glue;
 use type_::Type;
+
 use rustc_data_structures::fnv::FnvHashMap;
+use syntax::parse::token;
 
-use super::{MirContext, TempRef, drop};
+use super::{MirContext, LocalRef};
+use super::analyze::CleanupKind;
 use super::constant::Const;
 use super::lvalue::{LvalueRef, load_fat_ptr};
 use super::operand::OperandRef;
-use super::operand::OperandValue::{self, FatPtr, Immediate, Ref};
+use super::operand::OperandValue::*;
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_block(&mut self, bb: mir::BasicBlock) {
-        debug!("trans_block({:?})", bb);
-
         let mut bcx = self.bcx(bb);
         let mir = self.mir.clone();
-        let data = mir.basic_block_data(bb);
-
-        // MSVC SEH bits
-        let (cleanup_pad, cleanup_bundle) = if let Some((cp, cb)) = self.make_cleanup_pad(bb) {
-            (Some(cp), Some(cb))
-        } else {
-            (None, None)
+        let data = &mir[bb];
+
+        debug!("trans_block({:?}={:?})", bb, data);
+
+        // Create the cleanup bundle, if needed.
+        let cleanup_pad = bcx.lpad().and_then(|lp| lp.cleanuppad());
+        let cleanup_bundle = bcx.lpad().and_then(|l| l.bundle());
+
+        let funclet_br = |this: &Self, bcx: BlockAndBuilder, bb: mir::BasicBlock| {
+            let lltarget = this.blocks[bb].llbb;
+            if let Some(cp) = cleanup_pad {
+                match this.cleanup_kinds[bb] {
+                    CleanupKind::Funclet => {
+                        // micro-optimization: generate a `ret` rather than a jump
+                        // to a return block
+                        bcx.cleanup_ret(cp, Some(lltarget));
+                    }
+                    CleanupKind::Internal { .. } => bcx.br(lltarget),
+                    CleanupKind::NotCleanup => bug!("jump from cleanup bb to bb {:?}", bb)
+                }
+            } else {
+                bcx.br(lltarget);
+            }
         };
-        let funclet_br = |bcx: BlockAndBuilder, llbb: BasicBlockRef| if let Some(cp) = cleanup_pad {
-            bcx.cleanup_ret(cp, Some(llbb));
-        } else {
-            bcx.br(llbb);
+
+        let llblock = |this: &mut Self, target: mir::BasicBlock| {
+            let lltarget = this.blocks[target].llbb;
+
+            if let Some(cp) = cleanup_pad {
+                match this.cleanup_kinds[target] {
+                    CleanupKind::Funclet => {
+                        // MSVC cross-funclet jump - need a trampoline
+
+                        debug!("llblock: creating cleanup trampoline for {:?}", target);
+                        let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
+                        let trampoline = this.fcx.new_block(name, None).build();
+                        trampoline.set_personality_fn(this.fcx.eh_personality());
+                        trampoline.cleanup_ret(cp, Some(lltarget));
+                        trampoline.llbb()
+                    }
+                    CleanupKind::Internal { .. } => lltarget,
+                    CleanupKind::NotCleanup =>
+                        bug!("jump from cleanup bb {:?} to bb {:?}", bb, target)
+                }
+            } else {
+                if let (CleanupKind::NotCleanup, CleanupKind::Funclet) =
+                    (this.cleanup_kinds[bb], this.cleanup_kinds[target])
+                {
+                    // jump *into* cleanup - need a landing pad if GNU
+                    this.landing_pad_to(target).llbb
+                } else {
+                    lltarget
+                }
+            }
         };
 
         for statement in &data.statements {
@@ -59,8 +106,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         let terminator = data.terminator();
         debug!("trans_block: terminator: {:?}", terminator);
 
-        let debug_loc = DebugLoc::ScopeAt(self.scopes[terminator.scope.index()],
-                                          terminator.span);
+        let span = terminator.source_info.span;
+        let debug_loc = self.debug_loc(terminator.source_info);
         debug_loc.apply_to_bcx(&bcx);
         debug_loc.apply(bcx.fcx());
         match terminator.kind {
@@ -78,13 +125,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             }
 
             mir::TerminatorKind::Goto { target } => {
-                funclet_br(bcx, self.llblock(target));
+                funclet_br(self, bcx, target);
             }
 
             mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => {
                 let cond = self.trans_operand(&bcx, cond);
-                let lltrue = self.llblock(true_bb);
-                let llfalse = self.llblock(false_bb);
+
+                let lltrue = llblock(self, true_bb);
+                let llfalse = llblock(self, false_bb);
                 bcx.cond_br(cond.immediate(), lltrue, llfalse);
             }
 
@@ -106,18 +154,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     // code. This is especially helpful in cases like an if-let on a huge enum.
                     // Note: This optimization is only valid for exhaustive matches.
                     Some((&&bb, &c)) if c > targets.len() / 2 => {
-                        (Some(bb), self.blocks[bb.index()])
+                        (Some(bb), llblock(self, bb))
                     }
                     // We're generating an exhaustive switch, so the else branch
                     // can't be hit.  Branching to an unreachable instruction
                     // lets LLVM know this
-                    _ => (None, self.unreachable_block())
+                    _ => (None, self.unreachable_block().llbb)
                 };
-                let switch = bcx.switch(discr, default_blk.llbb, targets.len());
+                let switch = bcx.switch(discr, default_blk, targets.len());
                 assert_eq!(adt_def.variants.len(), targets.len());
                 for (adt_variant, &target) in adt_def.variants.iter().zip(targets) {
                     if default_bb != Some(target) {
-                        let llbb = self.llblock(target);
+                        let llbb = llblock(self, target);
                         let llval = bcx.with_block(|bcx| adt::trans_case(
                                 bcx, &repr, Disr::from(adt_variant.disr_val)));
                         build::AddCase(switch, llval, llbb)
@@ -129,28 +177,69 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let (otherwise, targets) = targets.split_last().unwrap();
                 let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval);
                 let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty));
-                let switch = bcx.switch(discr, self.llblock(*otherwise), values.len());
+                let switch = bcx.switch(discr, llblock(self, *otherwise), values.len());
                 for (value, target) in values.iter().zip(targets) {
                     let val = Const::from_constval(bcx.ccx(), value.clone(), switch_ty);
-                    let llbb = self.llblock(*target);
+                    let llbb = llblock(self, *target);
                     build::AddCase(switch, val.llval, llbb)
                 }
             }
 
             mir::TerminatorKind::Return => {
-                bcx.with_block(|bcx| {
-                    self.fcx.build_return_block(bcx, debug_loc);
-                })
+                let ret = bcx.fcx().fn_ty.ret;
+                if ret.is_ignore() || ret.is_indirect() {
+                    bcx.ret_void();
+                    return;
+                }
+
+                let llval = if let Some(cast_ty) = ret.cast {
+                    let index = mir.local_index(&mir::Lvalue::ReturnPointer).unwrap();
+                    let op = match self.locals[index] {
+                        LocalRef::Operand(Some(op)) => op,
+                        LocalRef::Operand(None) => bug!("use of return before def"),
+                        LocalRef::Lvalue(tr_lvalue) => {
+                            OperandRef {
+                                val: Ref(tr_lvalue.llval),
+                                ty: tr_lvalue.ty.to_ty(bcx.tcx())
+                            }
+                        }
+                    };
+                    let llslot = match op.val {
+                        Immediate(_) | Pair(..) => {
+                            let llscratch = build::AllocaFcx(bcx.fcx(), ret.original_ty, "ret");
+                            self.store_operand(&bcx, llscratch, op);
+                            llscratch
+                        }
+                        Ref(llval) => llval
+                    };
+                    let load = bcx.load(bcx.pointercast(llslot, cast_ty.ptr_to()));
+                    let llalign = llalign_of_min(bcx.ccx(), ret.ty);
+                    unsafe {
+                        llvm::LLVMSetAlignment(load, llalign);
+                    }
+                    load
+                } else {
+                    let op = self.trans_consume(&bcx, &mir::Lvalue::ReturnPointer);
+                    op.pack_if_pair(&bcx).immediate()
+                };
+                bcx.ret(llval);
+            }
+
+            mir::TerminatorKind::Unreachable => {
+                bcx.unreachable();
             }
 
-            mir::TerminatorKind::Drop { ref value, target, unwind } => {
-                let lvalue = self.trans_lvalue(&bcx, value);
-                let ty = lvalue.ty.to_ty(bcx.tcx());
+            mir::TerminatorKind::Drop { ref location, target, unwind } => {
+                let ty = mir.lvalue_ty(bcx.tcx(), location).to_ty(bcx.tcx());
+                let ty = bcx.monomorphize(&ty);
+
                 // Double check for necessity to drop
                 if !glue::type_needs_drop(bcx.tcx(), ty) {
-                    funclet_br(bcx, self.llblock(target));
+                    funclet_br(self, bcx, target);
                     return;
                 }
+
+                let lvalue = self.trans_lvalue(&bcx, location);
                 let drop_fn = glue::get_drop_glue(bcx.ccx(), ty);
                 let drop_ty = glue::get_drop_glue_type(bcx.tcx(), ty);
                 let llvalue = if drop_ty != ty {
@@ -159,24 +248,124 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     lvalue.llval
                 };
                 if let Some(unwind) = unwind {
-                    let uwbcx = self.bcx(unwind);
-                    let unwind = self.make_landing_pad(uwbcx);
                     bcx.invoke(drop_fn,
                                &[llvalue],
-                               self.llblock(target),
-                               unwind.llbb(),
-                               cleanup_bundle.as_ref());
-                    self.bcx(target).at_start(|bcx| {
-                        debug_loc.apply_to_bcx(bcx);
-                        drop::drop_fill(bcx, lvalue.llval, ty)
-                    });
+                               self.blocks[target].llbb,
+                               llblock(self, unwind),
+                               cleanup_bundle);
+                } else {
+                    bcx.call(drop_fn, &[llvalue], cleanup_bundle);
+                    funclet_br(self, bcx, target);
+                }
+            }
+
+            mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
+                let cond = self.trans_operand(&bcx, cond).immediate();
+                let const_cond = common::const_to_opt_uint(cond).map(|c| c == 1);
+
+                // Don't translate the panic block if success if known.
+                if const_cond == Some(expected) {
+                    funclet_br(self, bcx, target);
+                    return;
+                }
+
+                // Pass the condition through llvm.expect for branch hinting.
+                let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
+                let cond = bcx.call(expect, &[cond, C_bool(bcx.ccx(), expected)], None);
+
+                // Create the failure block and the conditional branch to it.
+                let lltarget = llblock(self, target);
+                let panic_block = self.fcx.new_block("panic", None);
+                if expected {
+                    bcx.cond_br(cond, lltarget, panic_block.llbb);
+                } else {
+                    bcx.cond_br(cond, panic_block.llbb, lltarget);
+                }
+
+                // After this point, bcx is the block for the call to panic.
+                bcx = panic_block.build();
+
+                // Get the location information.
+                let loc = bcx.sess().codemap().lookup_char_pos(span.lo);
+                let filename = token::intern_and_get_ident(&loc.file.name);
+                let filename = C_str_slice(bcx.ccx(), filename);
+                let line = C_u32(bcx.ccx(), loc.line as u32);
+
+                // Put together the arguments to the panic entry point.
+                let (lang_item, args, const_err) = match *msg {
+                    mir::AssertMessage::BoundsCheck { ref len, ref index } => {
+                        let len = self.trans_operand(&mut bcx, len).immediate();
+                        let index = self.trans_operand(&mut bcx, index).immediate();
+
+                        let const_err = common::const_to_opt_uint(len).and_then(|len| {
+                            common::const_to_opt_uint(index).map(|index| {
+                                ErrKind::IndexOutOfBounds {
+                                    len: len,
+                                    index: index
+                                }
+                            })
+                        });
+
+                        let file_line = C_struct(bcx.ccx(), &[filename, line], false);
+                        let align = llalign_of_min(bcx.ccx(), common::val_ty(file_line));
+                        let file_line = consts::addr_of(bcx.ccx(),
+                                                        file_line,
+                                                        align,
+                                                        "panic_bounds_check_loc");
+                        (lang_items::PanicBoundsCheckFnLangItem,
+                         vec![file_line, index, len],
+                         const_err)
+                    }
+                    mir::AssertMessage::Math(ref err) => {
+                        let msg_str = token::intern_and_get_ident(err.description());
+                        let msg_str = C_str_slice(bcx.ccx(), msg_str);
+                        let msg_file_line = C_struct(bcx.ccx(),
+                                                     &[msg_str, filename, line],
+                                                     false);
+                        let align = llalign_of_min(bcx.ccx(), common::val_ty(msg_file_line));
+                        let msg_file_line = consts::addr_of(bcx.ccx(),
+                                                            msg_file_line,
+                                                            align,
+                                                            "panic_loc");
+                        (lang_items::PanicFnLangItem,
+                         vec![msg_file_line],
+                         Some(ErrKind::Math(err.clone())))
+                    }
+                };
+
+                // If we know we always panic, and the error message
+                // is also constant, then we can produce a warning.
+                if const_cond == Some(!expected) {
+                    if let Some(err) = const_err {
+                        let _ = consts::const_err(bcx.ccx(), span,
+                                                  Err::<(), _>(err),
+                                                  consts::TrueConst::No);
+                    }
+                }
+
+                // Obtain the panic entry point.
+                let def_id = common::langcall(bcx.tcx(), Some(span), "", lang_item);
+                let callee = Callee::def(bcx.ccx(), def_id,
+                    bcx.ccx().empty_substs_for_def_id(def_id));
+                let llfn = callee.reify(bcx.ccx()).val;
+
+                // Translate the actual panic invoke/call.
+                if let Some(unwind) = cleanup {
+                    bcx.invoke(llfn,
+                               &args,
+                               self.unreachable_block().llbb,
+                               llblock(self, unwind),
+                               cleanup_bundle);
                 } else {
-                    bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref());
-                    drop::drop_fill(&bcx, lvalue.llval, ty);
-                    funclet_br(bcx, self.llblock(target));
+                    bcx.call(llfn, &args, cleanup_bundle);
+                    bcx.unreachable();
                 }
             }
 
+            mir::TerminatorKind::DropAndReplace { .. } => {
+                bug!("undesugared DropAndReplace in trans: {:?}", data);
+            }
+
             mir::TerminatorKind::Call { ref func, ref args, ref destination, ref cleanup } => {
                 // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
                 let callee = self.trans_operand(&bcx, func);
@@ -211,8 +400,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     let llptr = self.trans_operand(&bcx, &args[0]).immediate();
                     let val = self.trans_operand(&bcx, &args[1]);
                     self.store_operand(&bcx, llptr, val);
-                    self.set_operand_dropped(&bcx, &args[1]);
-                    funclet_br(bcx, self.llblock(target));
+                    funclet_br(self, bcx, target);
                     return;
                 }
 
@@ -222,8 +410,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         this.trans_transmute(&bcx, &args[0], dest);
                     });
 
-                    self.set_operand_dropped(&bcx, &args[0]);
-                    funclet_br(bcx, self.llblock(target));
+                    funclet_br(self, bcx, target);
                     return;
                 }
 
@@ -270,8 +457,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     if is_shuffle && idx == 2 {
                         match *arg {
                             mir::Operand::Consume(_) => {
-                                span_bug!(terminator.span,
-                                          "shuffle indices must be constant");
+                                span_bug!(span, "shuffle indices must be constant");
                             }
                             mir::Operand::Constant(ref constant) => {
                                 let val = self.trans_constant(&bcx, constant);
@@ -282,8 +468,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         }
                     }
 
-                    let val = self.trans_operand(&bcx, arg).val;
-                    self.trans_argument(&bcx, val, &mut llargs, &fn_ty,
+                    let op = self.trans_operand(&bcx, arg);
+                    self.trans_argument(&bcx, op, &mut llargs, &fn_ty,
                                         &mut idx, &mut callee.data);
                 }
                 if let Some(tup) = untuple {
@@ -321,17 +507,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
                             // Make a fake operand for store_return
                             let op = OperandRef {
-                                val: OperandValue::Ref(dst),
+                                val: Ref(dst),
                                 ty: sig.output.unwrap()
                             };
                             self.store_return(&bcx, ret_dest, fn_ty.ret, op);
                         }
 
                         if let Some((_, target)) = *destination {
-                            for op in args {
-                                self.set_operand_dropped(&bcx, op);
-                            }
-                            funclet_br(bcx, self.llblock(target));
+                            funclet_br(self, bcx, target);
                         } else {
                             // trans_intrinsic_call already used Unreachable.
                             // bcx.unreachable();
@@ -344,57 +527,41 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 };
 
                 // Many different ways to call a function handled here
-                if let Some(cleanup) = cleanup.map(|bb| self.bcx(bb)) {
+                if let &Some(cleanup) = cleanup {
                     let ret_bcx = if let Some((_, target)) = *destination {
-                        self.blocks[target.index()]
+                        self.blocks[target]
                     } else {
                         self.unreachable_block()
                     };
-                    let landingpad = self.make_landing_pad(cleanup);
-
                     let invokeret = bcx.invoke(fn_ptr,
                                                &llargs,
                                                ret_bcx.llbb,
-                                               landingpad.llbb(),
-                                               cleanup_bundle.as_ref());
+                                               llblock(self, cleanup),
+                                               cleanup_bundle);
                     fn_ty.apply_attrs_callsite(invokeret);
 
-                    landingpad.at_start(|bcx| {
-                        debug_loc.apply_to_bcx(bcx);
-                        for op in args {
-                            self.set_operand_dropped(bcx, op);
-                        }
-                    });
-
                     if destination.is_some() {
                         let ret_bcx = ret_bcx.build();
                         ret_bcx.at_start(|ret_bcx| {
                             debug_loc.apply_to_bcx(ret_bcx);
                             let op = OperandRef {
-                                val: OperandValue::Immediate(invokeret),
+                                val: Immediate(invokeret),
                                 ty: sig.output.unwrap()
                             };
                             self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op);
-                            for op in args {
-                                self.set_operand_dropped(&ret_bcx, op);
-                            }
                         });
                     }
                 } else {
-                    let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref());
+                    let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle);
                     fn_ty.apply_attrs_callsite(llret);
                     if let Some((_, target)) = *destination {
                         let op = OperandRef {
-                            val: OperandValue::Immediate(llret),
+                            val: Immediate(llret),
                             ty: sig.output.unwrap()
                         };
                         self.store_return(&bcx, ret_dest, fn_ty.ret, op);
-                        for op in args {
-                            self.set_operand_dropped(&bcx, op);
-                        }
-                        funclet_br(bcx, self.llblock(target));
+                        funclet_br(self, bcx, target);
                     } else {
-                        // no need to drop args, because the call never returns
                         bcx.unreachable();
                     }
                 }
@@ -404,25 +571,34 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
     fn trans_argument(&mut self,
                       bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                      val: OperandValue,
+                      op: OperandRef<'tcx>,
                       llargs: &mut Vec<ValueRef>,
                       fn_ty: &FnType,
                       next_idx: &mut usize,
                       callee: &mut CalleeData) {
-        // Treat the values in a fat pointer separately.
-        if let FatPtr(ptr, meta) = val {
-            if *next_idx == 0 {
-                if let Virtual(idx) = *callee {
-                    let llfn = bcx.with_block(|bcx| {
-                        meth::get_virtual_method(bcx, meta, idx)
-                    });
-                    let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to();
-                    *callee = Fn(bcx.pointercast(llfn, llty));
+        if let Pair(a, b) = op.val {
+            // Treat the values in a fat pointer separately.
+            if common::type_is_fat_ptr(bcx.tcx(), op.ty) {
+                let (ptr, meta) = (a, b);
+                if *next_idx == 0 {
+                    if let Virtual(idx) = *callee {
+                        let llfn = bcx.with_block(|bcx| {
+                            meth::get_virtual_method(bcx, meta, idx)
+                        });
+                        let llty = fn_ty.llvm_type(bcx.ccx()).ptr_to();
+                        *callee = Fn(bcx.pointercast(llfn, llty));
+                    }
                 }
+
+                let imm_op = |x| OperandRef {
+                    val: Immediate(x),
+                    // We won't be checking the type again.
+                    ty: bcx.tcx().types.err
+                };
+                self.trans_argument(bcx, imm_op(ptr), llargs, fn_ty, next_idx, callee);
+                self.trans_argument(bcx, imm_op(meta), llargs, fn_ty, next_idx, callee);
+                return;
             }
-            self.trans_argument(bcx, Immediate(ptr), llargs, fn_ty, next_idx, callee);
-            self.trans_argument(bcx, Immediate(meta), llargs, fn_ty, next_idx, callee);
-            return;
         }
 
         let arg = &fn_ty.args[*next_idx];
@@ -438,15 +614,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         }
 
         // Force by-ref if we have to load through a cast pointer.
-        let (mut llval, by_ref) = match val {
-            Immediate(llval) if arg.is_indirect() || arg.cast.is_some() => {
-                let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg");
-                bcx.store(llval, llscratch);
-                (llscratch, true)
+        let (mut llval, by_ref) = match op.val {
+            Immediate(_) | Pair(..) => {
+                if arg.is_indirect() || arg.cast.is_some() {
+                    let llscratch = build::AllocaFcx(bcx.fcx(), arg.original_ty, "arg");
+                    self.store_operand(bcx, llscratch, op);
+                    (llscratch, true)
+                } else {
+                    (op.pack_if_pair(bcx).immediate(), false)
+                }
             }
-            Immediate(llval) => (llval, false),
-            Ref(llval) => (llval, true),
-            FatPtr(_, _) => bug!("fat pointers handled above")
+            Ref(llval) => (llval, true)
         };
 
         if by_ref && !arg.is_indirect() {
@@ -493,12 +671,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     let ptr = adt::trans_field_ptr_builder(bcx, &base_repr, base, Disr(0), n);
                     let val = if common::type_is_fat_ptr(bcx.tcx(), ty) {
                         let (lldata, llextra) = load_fat_ptr(bcx, ptr);
-                        FatPtr(lldata, llextra)
+                        Pair(lldata, llextra)
                     } else {
                         // trans_argument will load this if it needs to
                         Ref(ptr)
                     };
-                    self.trans_argument(bcx, val, llargs, fn_ty, next_idx, callee);
+                    let op = OperandRef {
+                        val: val,
+                        ty: ty
+                    };
+                    self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee);
                 }
 
             }
@@ -510,11 +692,29 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         elem = bcx.trunc(elem, Type::i1(bcx.ccx()));
                     }
                     // If the tuple is immediate, the elements are as well
-                    let val = Immediate(elem);
-                    self.trans_argument(bcx, val, llargs, fn_ty, next_idx, callee);
+                    let op = OperandRef {
+                        val: Immediate(elem),
+                        ty: ty
+                    };
+                    self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee);
+                }
+            }
+            Pair(a, b) => {
+                let elems = [a, b];
+                for (n, &ty) in arg_types.iter().enumerate() {
+                    let mut elem = elems[n];
+                    // Truncate bools to i1, if needed
+                    if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx()) {
+                        elem = bcx.trunc(elem, Type::i1(bcx.ccx()));
+                    }
+                    // Pair is always made up of immediates
+                    let op = OperandRef {
+                        val: Immediate(elem),
+                        ty: ty
+                    };
+                    self.trans_argument(bcx, op, llargs, fn_ty, next_idx, callee);
                 }
             }
-            FatPtr(_, _) => bug!("tuple is a fat pointer?!")
         }
 
     }
@@ -534,17 +734,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         }
     }
 
-    /// Create a landingpad wrapper around the given Block.
+    /// Return the landingpad wrapper around the given basic block
     ///
     /// No-op in MSVC SEH scheme.
-    fn make_landing_pad(&mut self,
-                        cleanup: BlockAndBuilder<'bcx, 'tcx>)
-                        -> BlockAndBuilder<'bcx, 'tcx>
+    fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> Block<'bcx, 'tcx>
     {
-        if base::wants_msvc_seh(cleanup.sess()) {
-            return cleanup;
+        if let Some(block) = self.landing_pads[target_bb] {
+            return block;
+        }
+
+        if base::wants_msvc_seh(self.fcx.ccx.sess()) {
+            return self.blocks[target_bb];
         }
-        let bcx = self.fcx.new_block("cleanup", None).build();
+
+        let target = self.bcx(target_bb);
+
+        let block = self.fcx.new_block("cleanup", None);
+        self.landing_pads[target_bb] = Some(block);
+
+        let bcx = block.build();
         let ccx = bcx.ccx();
         let llpersonality = self.fcx.eh_personality();
         let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false);
@@ -552,36 +760,34 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         bcx.set_cleanup(llretval);
         let slot = self.get_personality_slot(&bcx);
         bcx.store(llretval, slot);
-        bcx.br(cleanup.llbb());
-        bcx
+        bcx.br(target.llbb());
+        block
     }
 
-    /// Create prologue cleanuppad instruction under MSVC SEH handling scheme.
-    ///
-    /// Also handles setting some state for the original trans and creating an operand bundle for
-    /// function calls.
-    fn make_cleanup_pad(&mut self, bb: mir::BasicBlock) -> Option<(ValueRef, OperandBundleDef)> {
+    pub fn init_cpad(&mut self, bb: mir::BasicBlock) {
         let bcx = self.bcx(bb);
-        let data = self.mir.basic_block_data(bb);
-        let use_funclets = base::wants_msvc_seh(bcx.sess()) && data.is_cleanup;
-        let cleanup_pad = if use_funclets {
-            bcx.set_personality_fn(self.fcx.eh_personality());
-            bcx.at_start(|bcx| {
-                DebugLoc::None.apply_to_bcx(bcx);
-                Some(bcx.cleanup_pad(None, &[]))
-            })
-        } else {
-            None
+        let data = &self.mir[bb];
+        debug!("init_cpad({:?})", data);
+
+        match self.cleanup_kinds[bb] {
+            CleanupKind::NotCleanup => {
+                bcx.set_lpad(None)
+            }
+            _ if !base::wants_msvc_seh(bcx.sess()) => {
+                bcx.set_lpad(Some(LandingPad::gnu()))
+            }
+            CleanupKind::Internal { funclet } => {
+                // FIXME: is this needed?
+                bcx.set_personality_fn(self.fcx.eh_personality());
+                bcx.set_lpad_ref(self.bcx(funclet).lpad());
+            }
+            CleanupKind::Funclet => {
+                bcx.set_personality_fn(self.fcx.eh_personality());
+                DebugLoc::None.apply_to_bcx(&bcx);
+                let cleanup_pad = bcx.cleanup_pad(None, &[]);
+                bcx.set_lpad(Some(LandingPad::msvc(cleanup_pad)));
+            }
         };
-        // Set the landingpad global-state for old translator, so it knows about the SEH used.
-        bcx.set_lpad(if let Some(cleanup_pad) = cleanup_pad {
-            Some(common::LandingPad::msvc(cleanup_pad))
-        } else if data.is_cleanup {
-            Some(common::LandingPad::gnu())
-        } else {
-            None
-        });
-        cleanup_pad.map(|f| (f, OperandBundleDef::new("funclet", &[f])))
     }
 
     fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> {
@@ -594,11 +800,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     }
 
     fn bcx(&self, bb: mir::BasicBlock) -> BlockAndBuilder<'bcx, 'tcx> {
-        self.blocks[bb.index()].build()
-    }
-
-    pub fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
-        self.blocks[bb.index()].llbb
+        self.blocks[bb].build()
     }
 
     fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
@@ -608,42 +810,39 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         if fn_ret_ty.is_ignore() {
             return ReturnDest::Nothing;
         }
-        let dest = match *dest {
-            mir::Lvalue::Temp(idx) => {
-                let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), dest);
-                let lvalue_ty = bcx.monomorphize(&lvalue_ty);
-                let ret_ty = lvalue_ty.to_ty(bcx.tcx());
-                match self.temps[idx as usize] {
-                    TempRef::Lvalue(dest) => dest,
-                    TempRef::Operand(None) => {
-                        // Handle temporary lvalues, specifically Operand ones, as
-                        // they don't have allocas
-                        return if fn_ret_ty.is_indirect() {
-                            // Odd, but possible, case, we have an operand temporary,
-                            // but the calling convention has an indirect return.
-                            let tmp = bcx.with_block(|bcx| {
-                                base::alloc_ty(bcx, ret_ty, "tmp_ret")
-                            });
-                            llargs.push(tmp);
-                            ReturnDest::IndirectOperand(tmp, idx)
-                        } else if is_intrinsic {
-                            // Currently, intrinsics always need a location to store
-                            // the result. so we create a temporary alloca for the
-                            // result
-                            let tmp = bcx.with_block(|bcx| {
-                                base::alloc_ty(bcx, ret_ty, "tmp_ret")
-                            });
-                            ReturnDest::IndirectOperand(tmp, idx)
-                        } else {
-                            ReturnDest::DirectOperand(idx)
-                        };
-                    }
-                    TempRef::Operand(Some(_)) => {
-                        bug!("lvalue temp already assigned to");
-                    }
+        let dest = if let Some(index) = self.mir.local_index(dest) {
+            let ret_ty = self.lvalue_ty(dest);
+            match self.locals[index] {
+                LocalRef::Lvalue(dest) => dest,
+                LocalRef::Operand(None) => {
+                    // Handle temporary lvalues, specifically Operand ones, as
+                    // they don't have allocas
+                    return if fn_ret_ty.is_indirect() {
+                        // Odd, but possible, case, we have an operand temporary,
+                        // but the calling convention has an indirect return.
+                        let tmp = bcx.with_block(|bcx| {
+                            base::alloc_ty(bcx, ret_ty, "tmp_ret")
+                        });
+                        llargs.push(tmp);
+                        ReturnDest::IndirectOperand(tmp, index)
+                    } else if is_intrinsic {
+                        // Currently, intrinsics always need a location to store
+                        // the result. so we create a temporary alloca for the
+                        // result
+                        let tmp = bcx.with_block(|bcx| {
+                            base::alloc_ty(bcx, ret_ty, "tmp_ret")
+                        });
+                        ReturnDest::IndirectOperand(tmp, index)
+                    } else {
+                        ReturnDest::DirectOperand(index)
+                    };
+                }
+                LocalRef::Operand(Some(_)) => {
+                    bug!("lvalue local already assigned to");
                 }
             }
-            _ => self.trans_lvalue(bcx, dest)
+        } else {
+            self.trans_lvalue(bcx, dest)
         };
         if fn_ret_ty.is_indirect() {
             llargs.push(dest.llval);
@@ -664,7 +863,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let f = Callee::def(bcx.ccx(), def_id, substs);
                 let datum = f.reify(bcx.ccx());
                 val = OperandRef {
-                    val: OperandValue::Immediate(datum.val),
+                    val: Immediate(datum.val),
                     ty: datum.ty
                 };
             }
@@ -675,6 +874,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         self.store_operand(bcx, cast_ptr, val);
     }
 
+
     // Stores the return value of a function call into it's final location.
     fn store_return(&mut self,
                     bcx: &BlockAndBuilder<'bcx, 'tcx>,
@@ -686,24 +886,22 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         match dest {
             Nothing => (),
             Store(dst) => ret_ty.store(bcx, op.immediate(), dst),
-            IndirectOperand(tmp, idx) => {
+            IndirectOperand(tmp, index) => {
                 let op = self.trans_load(bcx, tmp, op.ty);
-                self.temps[idx as usize] = TempRef::Operand(Some(op));
+                self.locals[index] = LocalRef::Operand(Some(op));
             }
-            DirectOperand(idx) => {
-                let op = if type_is_fat_ptr(bcx.tcx(), op.ty) {
-                    let llval = op.immediate();
-                    let ptr = bcx.extract_value(llval, 0);
-                    let meta = bcx.extract_value(llval, 1);
-
-                    OperandRef {
-                        val: OperandValue::FatPtr(ptr, meta),
-                        ty: op.ty
-                    }
+            DirectOperand(index) => {
+                // If there is a cast, we have to store and reload.
+                let op = if ret_ty.cast.is_some() {
+                    let tmp = bcx.with_block(|bcx| {
+                        base::alloc_ty(bcx, op.ty, "tmp_ret")
+                    });
+                    ret_ty.store(bcx, op.immediate(), tmp);
+                    self.trans_load(bcx, tmp, op.ty)
                 } else {
-                    op
+                    op.unpack_if_pair(bcx)
                 };
-                self.temps[idx as usize] = TempRef::Operand(Some(op));
+                self.locals[index] = LocalRef::Operand(Some(op));
             }
         }
     }
@@ -714,8 +912,8 @@ enum ReturnDest {
     Nothing,
     // Store the return value to the pointer
     Store(ValueRef),
-    // Stores an indirect return value to an operand temporary lvalue
-    IndirectOperand(ValueRef, u32),
-    // Stores a direct return value to an operand temporary lvalue
-    DirectOperand(u32)
+    // Stores an indirect return value to an operand local lvalue
+    IndirectOperand(ValueRef, mir::Local),
+    // Stores a direct return value to an operand local lvalue
+    DirectOperand(mir::Local)
 }
index 0403c7b1f757b89fb166b9744c869af6fa67627b..da72793abf6dab869a558e9993e7362c342ef916 100644 (file)
@@ -12,14 +12,17 @@ use llvm::{self, ValueRef};
 use rustc::middle::const_val::ConstVal;
 use rustc_const_eval::ErrKind;
 use rustc_const_math::ConstInt::*;
+use rustc_const_math::ConstFloat::*;
+use rustc_const_math::ConstMathErr;
 use rustc::hir::def_id::DefId;
 use rustc::infer::TransNormalize;
 use rustc::mir::repr as mir;
 use rustc::mir::tcx::LvalueTy;
 use rustc::traits;
-use rustc::ty::{self, Ty, TypeFoldable};
+use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc::ty::cast::{CastTy, IntTy};
 use rustc::ty::subst::Substs;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use {abi, adt, base, Disr};
 use callee::Callee;
 use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty};
@@ -31,7 +34,7 @@ use type_of;
 use type_::Type;
 use value::Value;
 
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
 
 use std::ptr;
 
@@ -62,7 +65,9 @@ impl<'tcx> Const<'tcx> {
                              -> Const<'tcx> {
         let llty = type_of::type_of(ccx, ty);
         let val = match cv {
-            ConstVal::Float(v) => C_floating_f64(v, llty),
+            ConstVal::Float(F32(v)) => C_floating_f64(v as f64, llty),
+            ConstVal::Float(F64(v)) => C_floating_f64(v, llty),
+            ConstVal::Float(FInfer {..}) => bug!("MIR must not use `{:?}`", cv),
             ConstVal::Bool(v) => C_bool(ccx, v),
             ConstVal::Integral(I8(v)) => C_integral(Type::i8(ccx), v as u64, true),
             ConstVal::Integral(I16(v)) => C_integral(Type::i16(ccx), v as u64, true),
@@ -80,14 +85,14 @@ impl<'tcx> Const<'tcx> {
                 let u = v.as_u64(ccx.tcx().sess.target.uint_type);
                 C_integral(Type::int(ccx), u, false)
             },
-            ConstVal::Integral(Infer(v)) => C_integral(llty, v as u64, false),
-            ConstVal::Integral(InferSigned(v)) => C_integral(llty, v as u64, true),
+            ConstVal::Integral(Infer(_)) |
+            ConstVal::Integral(InferSigned(_)) => bug!("MIR must not use `{:?}`", cv),
             ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
             ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
             ConstVal::Struct(_) | ConstVal::Tuple(_) |
             ConstVal::Array(..) | ConstVal::Repeat(..) |
             ConstVal::Function(_) => {
-                bug!("MIR must not use {:?} (which refers to a local ID)", cv)
+                bug!("MIR must not use `{:?}` (which refers to a local ID)", cv)
             }
             ConstVal::Char(c) => C_integral(Type::char(ccx), c as u64, false),
             ConstVal::Dummy => bug!(),
@@ -98,9 +103,15 @@ impl<'tcx> Const<'tcx> {
         Const::new(val, ty)
     }
 
+    fn get_pair(&self) -> (ValueRef, ValueRef) {
+        (const_get_elt(self.llval, &[0]),
+         const_get_elt(self.llval, &[1]))
+    }
+
     fn get_fat_ptr(&self) -> (ValueRef, ValueRef) {
-        (const_get_elt(self.llval, &[abi::FAT_PTR_ADDR as u32]),
-         const_get_elt(self.llval, &[abi::FAT_PTR_EXTRA as u32]))
+        assert_eq!(abi::FAT_PTR_ADDR, 0);
+        assert_eq!(abi::FAT_PTR_EXTRA, 1);
+        self.get_pair()
     }
 
     fn as_lvalue(&self) -> ConstLvalue<'tcx> {
@@ -115,10 +126,10 @@ impl<'tcx> Const<'tcx> {
         let llty = type_of::immediate_type_of(ccx, self.ty);
         let llvalty = val_ty(self.llval);
 
-        let val = if common::type_is_fat_ptr(ccx.tcx(), self.ty) {
-            let (data, extra) = self.get_fat_ptr();
-            OperandValue::FatPtr(data, extra)
-        } else if common::type_is_immediate(ccx, self.ty) && llty == llvalty {
+        let val = if llty == llvalty && common::type_is_imm_pair(ccx, self.ty) {
+            let (a, b) = self.get_pair();
+            OperandValue::Pair(a, b)
+        } else if llty == llvalty && common::type_is_immediate(ccx, self.ty) {
             // If the types match, we can use the value directly.
             OperandValue::Immediate(self.llval)
         } else {
@@ -192,17 +203,8 @@ struct MirConstContext<'a, 'tcx: 'a> {
     /// Type parameters for const fn and associated constants.
     substs: &'tcx Substs<'tcx>,
 
-    /// Arguments passed to a const fn.
-    args: Vec<Const<'tcx>>,
-
-    /// Variable values - specifically, argument bindings of a const fn.
-    vars: Vec<Option<Const<'tcx>>>,
-
-    /// Temp values.
-    temps: Vec<Option<Const<'tcx>>>,
-
-    /// Value assigned to Return, which is the resulting constant.
-    return_value: Option<Const<'tcx>>
+    /// Values of locals in a constant or const fn.
+    locals: IndexVec<mir::Local, Option<Const<'tcx>>>
 }
 
 
@@ -210,22 +212,24 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
     fn new(ccx: &'a CrateContext<'a, 'tcx>,
            mir: &'a mir::Mir<'tcx>,
            substs: &'tcx Substs<'tcx>,
-           args: Vec<Const<'tcx>>)
+           args: IndexVec<mir::Arg, Const<'tcx>>)
            -> MirConstContext<'a, 'tcx> {
-        MirConstContext {
+        let mut context = MirConstContext {
             ccx: ccx,
             mir: mir,
             substs: substs,
-            args: args,
-            vars: vec![None; mir.var_decls.len()],
-            temps: vec![None; mir.temp_decls.len()],
-            return_value: None
+            locals: (0..mir.count_locals()).map(|_| None).collect(),
+        };
+        for (i, arg) in args.into_iter().enumerate() {
+            let index = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(i))).unwrap();
+            context.locals[index] = Some(arg);
         }
+        context
     }
 
     fn trans_def(ccx: &'a CrateContext<'a, 'tcx>,
                  mut instance: Instance<'tcx>,
-                 args: Vec<Const<'tcx>>)
+                 args: IndexVec<mir::Arg, Const<'tcx>>)
                  -> Result<Const<'tcx>, ConstEvalFailure> {
         // Try to resolve associated constants.
         if instance.substs.self_ty().is_some() {
@@ -263,38 +267,63 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
     fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalFailure> {
         let tcx = self.ccx.tcx();
         let mut bb = mir::START_BLOCK;
+
+        // Make sure to evaluate all statemenets to
+        // report as many errors as we possibly can.
+        let mut failure = Ok(());
+
         loop {
-            let data = self.mir.basic_block_data(bb);
+            let data = &self.mir[bb];
             for statement in &data.statements {
+                let span = statement.source_info.span;
                 match statement.kind {
                     mir::StatementKind::Assign(ref dest, ref rvalue) => {
                         let ty = self.mir.lvalue_ty(tcx, dest);
                         let ty = self.monomorphize(&ty).to_ty(tcx);
-                        let value = self.const_rvalue(rvalue, ty, statement.span)?;
-                        self.store(dest, value, statement.span);
+                        match self.const_rvalue(rvalue, ty, span) {
+                            Ok(value) => self.store(dest, value, span),
+                            Err(err) => if failure.is_ok() { failure = Err(err); }
+                        }
                     }
                 }
             }
 
             let terminator = data.terminator();
-            let span = terminator.span;
+            let span = terminator.source_info.span;
             bb = match terminator.kind {
                 mir::TerminatorKind::Drop { target, .. } | // No dropping.
                 mir::TerminatorKind::Goto { target } => target,
                 mir::TerminatorKind::Return => {
-                    return Ok(self.return_value.unwrap_or_else(|| {
+                    failure?;
+                    let index = self.mir.local_index(&mir::Lvalue::ReturnPointer).unwrap();
+                    return Ok(self.locals[index].unwrap_or_else(|| {
                         span_bug!(span, "no returned value in constant");
-                    }))
+                    }));
                 }
 
-                // This is only supported to make bounds checking work.
-                mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => {
+                mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, .. } => {
                     let cond = self.const_operand(cond, span)?;
-                    if common::const_to_uint(cond.llval) != 0 {
-                        true_bb
-                    } else {
-                        false_bb
+                    let cond_bool = common::const_to_uint(cond.llval) != 0;
+                    if cond_bool != expected {
+                        let err = match *msg {
+                            mir::AssertMessage::BoundsCheck { ref len, ref index } => {
+                                let len = self.const_operand(len, span)?;
+                                let index = self.const_operand(index, span)?;
+                                ErrKind::IndexOutOfBounds {
+                                    len: common::const_to_uint(len.llval),
+                                    index: common::const_to_uint(index.llval)
+                                }
+                            }
+                            mir::AssertMessage::Math(ref err) => {
+                                ErrKind::Math(err.clone())
+                            }
+                        };
+                        match consts::const_err(self.ccx, span, Err(err), TrueConst::Yes) {
+                            Ok(()) => {}
+                            Err(err) => if failure.is_ok() { failure = Err(err); }
+                        }
                     }
+                    target
                 }
 
                 mir::TerminatorKind::Call { ref func, ref args, ref destination, .. } => {
@@ -308,22 +337,21 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                                        func, fn_ty)
                     };
 
-                    // Indexing OOB doesn't call a const fn, handle it.
-                    if Some(instance.def) == tcx.lang_items.panic_bounds_check_fn() {
-                        consts::const_err(self.ccx, span,
-                                          Err(ErrKind::IndexOutOfBounds),
-                                          TrueConst::Yes)?;
+                    let mut const_args = IndexVec::with_capacity(args.len());
+                    for arg in args {
+                        match self.const_operand(arg, span) {
+                            Ok(arg) => { const_args.push(arg); },
+                            Err(err) => if failure.is_ok() { failure = Err(err); }
+                        }
                     }
-
-                    let args = args.iter().map(|arg| {
-                        self.const_operand(arg, span)
-                    }).collect::<Result<Vec<_>, _>>()?;
-                    let value = MirConstContext::trans_def(self.ccx, instance, args)?;
                     if let Some((ref dest, target)) = *destination {
-                        self.store(dest, value, span);
+                        match MirConstContext::trans_def(self.ccx, instance, const_args) {
+                            Ok(value) => self.store(dest, value, span),
+                            Err(err) => if failure.is_ok() { failure = Err(err); }
+                        }
                         target
                     } else {
-                        span_bug!(span, "diverging {:?} in constant", terminator.kind)
+                        span_bug!(span, "diverging {:?} in constant", terminator.kind);
                     }
                 }
                 _ => span_bug!(span, "{:?} in constant", terminator.kind)
@@ -332,30 +360,28 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
     }
 
     fn store(&mut self, dest: &mir::Lvalue<'tcx>, value: Const<'tcx>, span: Span) {
-        let dest = match *dest {
-            mir::Lvalue::Var(index) => &mut self.vars[index as usize],
-            mir::Lvalue::Temp(index) => &mut self.temps[index as usize],
-            mir::Lvalue::ReturnPointer => &mut self.return_value,
-            _ => span_bug!(span, "assignment to {:?} in constant", dest)
-        };
-        *dest = Some(value);
+        if let Some(index) = self.mir.local_index(dest) {
+            self.locals[index] = Some(value);
+        } else {
+            span_bug!(span, "assignment to {:?} in constant", dest);
+        }
     }
 
     fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span)
                     -> Result<ConstLvalue<'tcx>, ConstEvalFailure> {
         let tcx = self.ccx.tcx();
+
+        if let Some(index) = self.mir.local_index(lvalue) {
+            return Ok(self.locals[index].unwrap_or_else(|| {
+                span_bug!(span, "{:?} not initialized", lvalue)
+            }).as_lvalue());
+        }
+
         let lvalue = match *lvalue {
-            mir::Lvalue::Var(index) => {
-                self.vars[index as usize].unwrap_or_else(|| {
-                    span_bug!(span, "var{} not initialized", index)
-                }).as_lvalue()
-            }
-            mir::Lvalue::Temp(index) => {
-                self.temps[index as usize].unwrap_or_else(|| {
-                    span_bug!(span, "tmp{} not initialized", index)
-                }).as_lvalue()
-            }
-            mir::Lvalue::Arg(index) => self.args[index as usize].as_lvalue(),
+            mir::Lvalue::Var(_) |
+            mir::Lvalue::Temp(_) |
+            mir::Lvalue::Arg(_) |
+            mir::Lvalue::ReturnPointer => bug!(), // handled above
             mir::Lvalue::Static(def_id) => {
                 ConstLvalue {
                     base: Base::Static(consts::get_static(self.ccx, def_id).val),
@@ -363,9 +389,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                     ty: self.mir.lvalue_ty(tcx, lvalue).to_ty(tcx)
                 }
             }
-            mir::Lvalue::ReturnPointer => {
-                span_bug!(span, "accessing Lvalue::ReturnPointer in constant")
-            }
             mir::Lvalue::Projection(ref projection) => {
                 let tr_base = self.const_lvalue(&projection.base, span)?;
                 let projected_ty = LvalueTy::Ty { ty: tr_base.ty }
@@ -413,8 +436,16 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                         } else {
                             span_bug!(span, "index is not an integer-constant expression")
                         };
-                        (Base::Value(const_get_elt(base.llval, &[iv as u32])),
-                         ptr::null_mut())
+
+                        // Produce an undef instead of a LLVM assertion on OOB.
+                        let len = common::const_to_uint(tr_base.len(self.ccx));
+                        let llelem = if iv < len {
+                            const_get_elt(base.llval, &[iv as u32])
+                        } else {
+                            C_undef(type_of::type_of(self.ccx, projected_ty))
+                        };
+
+                        (Base::Value(llelem), ptr::null_mut())
                     }
                     _ => span_bug!(span, "{:?} in constant", projection.elem)
                 };
@@ -448,11 +479,11 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
 
                         let substs = self.monomorphize(&substs);
                         let instance = Instance::new(def_id, substs);
-                        MirConstContext::trans_def(self.ccx, instance, vec![])
+                        MirConstContext::trans_def(self.ccx, instance, IndexVec::new())
                     }
                     mir::Literal::Promoted { index } => {
                         let mir = &self.mir.promoted[index];
-                        MirConstContext::new(self.ccx, mir, self.substs, vec![]).trans()
+                        MirConstContext::new(self.ccx, mir, self.substs, IndexVec::new()).trans()
                     }
                     mir::Literal::Value { value } => {
                         Ok(Const::from_constval(self.ccx, value, ty))
@@ -485,9 +516,17 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
             }
 
             mir::Rvalue::Aggregate(ref kind, ref operands) => {
-                let fields = operands.iter().map(|operand| {
-                    Ok(self.const_operand(operand, span)?.llval)
-                }).collect::<Result<Vec<_>, _>>()?;
+                // Make sure to evaluate all operands to
+                // report as many errors as we possibly can.
+                let mut fields = Vec::with_capacity(operands.len());
+                let mut failure = Ok(());
+                for operand in operands {
+                    match self.const_operand(operand, span) {
+                        Ok(val) => fields.push(val.llval),
+                        Err(err) => if failure.is_ok() { failure = Err(err); }
+                    }
+                }
+                failure?;
 
                 // FIXME Shouldn't need to manually trigger closure instantiations.
                 if let mir::AggregateKind::Closure(def_id, substs) = *kind {
@@ -656,7 +695,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                                 consts::ptrcast(data_ptr, ll_cast_ty)
                             }
                         } else {
-                            bug!("Unexpected non-FatPtr operand")
+                            bug!("Unexpected non-fat-pointer operand")
                         }
                     }
                 };
@@ -667,7 +706,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                 let tr_lvalue = self.const_lvalue(lvalue, span)?;
 
                 let ty = tr_lvalue.ty;
-                let ref_ty = tcx.mk_ref(tcx.mk_region(ty::ReStatic),
+                let ref_ty = tcx.mk_ref(tcx.mk_region(ty::ReErased),
                     ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() });
 
                 let base = match tr_lvalue.base {
@@ -702,73 +741,28 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                 let ty = lhs.ty;
                 let binop_ty = self.mir.binop_ty(tcx, op, lhs.ty, rhs.ty);
                 let (lhs, rhs) = (lhs.llval, rhs.llval);
-                assert!(!ty.is_simd());
-                let is_float = ty.is_fp();
-                let signed = ty.is_signed();
-
-                if let (Some(lhs), Some(rhs)) = (to_const_int(lhs, ty, tcx),
-                                                 to_const_int(rhs, ty, tcx)) {
-                    let result = match op {
-                        mir::BinOp::Add => lhs + rhs,
-                        mir::BinOp::Sub => lhs - rhs,
-                        mir::BinOp::Mul => lhs * rhs,
-                        mir::BinOp::Div => lhs / rhs,
-                        mir::BinOp::Rem => lhs % rhs,
-                        mir::BinOp::Shl => lhs << rhs,
-                        mir::BinOp::Shr => lhs >> rhs,
-                        _ => Ok(lhs)
-                    };
-                    consts::const_err(self.ccx, span,
-                                      result.map_err(ErrKind::Math),
-                                      TrueConst::Yes)?;
-                }
-
-                let llval = unsafe {
-                    match op {
-                        mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs),
-                        mir::BinOp::Add             => llvm::LLVMConstAdd(lhs, rhs),
-
-                        mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs),
-                        mir::BinOp::Sub             => llvm::LLVMConstSub(lhs, rhs),
-
-                        mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs),
-                        mir::BinOp::Mul             => llvm::LLVMConstMul(lhs, rhs),
-
-                        mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs),
-                        mir::BinOp::Div if signed   => llvm::LLVMConstSDiv(lhs, rhs),
-                        mir::BinOp::Div             => llvm::LLVMConstUDiv(lhs, rhs),
+                Const::new(const_scalar_binop(op, lhs, rhs, ty), binop_ty)
+            }
 
-                        mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs),
-                        mir::BinOp::Rem if signed   => llvm::LLVMConstSRem(lhs, rhs),
-                        mir::BinOp::Rem             => llvm::LLVMConstURem(lhs, rhs),
+            mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
+                let lhs = self.const_operand(lhs, span)?;
+                let rhs = self.const_operand(rhs, span)?;
+                let ty = lhs.ty;
+                let val_ty = self.mir.binop_ty(tcx, op, lhs.ty, rhs.ty);
+                let binop_ty = tcx.mk_tup(vec![val_ty, tcx.types.bool]);
+                let (lhs, rhs) = (lhs.llval, rhs.llval);
+                assert!(!ty.is_fp());
 
-                        mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs),
-                        mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs),
-                        mir::BinOp::BitOr  => llvm::LLVMConstOr(lhs, rhs),
-                        mir::BinOp::Shl    => {
-                            let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
-                            llvm::LLVMConstShl(lhs, rhs)
-                        }
-                        mir::BinOp::Shr    => {
-                            let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
-                            if signed { llvm::LLVMConstAShr(lhs, rhs) }
-                            else      { llvm::LLVMConstLShr(lhs, rhs) }
-                        }
-                        mir::BinOp::Eq | mir::BinOp::Ne |
-                        mir::BinOp::Lt | mir::BinOp::Le |
-                        mir::BinOp::Gt | mir::BinOp::Ge => {
-                            if is_float {
-                                let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop());
-                                llvm::ConstFCmp(cmp, lhs, rhs)
-                            } else {
-                                let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(),
-                                                                         signed);
-                                llvm::ConstICmp(cmp, lhs, rhs)
-                            }
-                        }
+                match const_scalar_checked_binop(tcx, op, lhs, rhs, ty) {
+                    Some((llval, of)) => {
+                        let llof = C_bool(self.ccx, of);
+                        Const::new(C_struct(self.ccx, &[llval, llof], false), binop_ty)
                     }
-                };
-                Const::new(llval, binop_ty)
+                    None => {
+                        span_bug!(span, "{:?} got non-integer operands: {:?} and {:?}",
+                                  rvalue, Value(lhs), Value(rhs));
+                    }
+                }
             }
 
             mir::Rvalue::UnaryOp(op, ref operand) => {
@@ -781,11 +775,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                         }
                     }
                     mir::UnOp::Neg => {
-                        if let Some(cval) = to_const_int(lloperand, operand.ty, tcx) {
-                            consts::const_err(self.ccx, span,
-                                              (-cval).map_err(ErrKind::Math),
-                                              TrueConst::Yes)?;
-                        }
                         let is_float = operand.ty.is_fp();
                         unsafe {
                             if is_float {
@@ -804,6 +793,97 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
 
         Ok(val)
     }
+
+}
+
+pub fn const_scalar_binop(op: mir::BinOp,
+                          lhs: ValueRef,
+                          rhs: ValueRef,
+                          input_ty: Ty) -> ValueRef {
+    assert!(!input_ty.is_simd());
+    let is_float = input_ty.is_fp();
+    let signed = input_ty.is_signed();
+
+    unsafe {
+        match op {
+            mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs),
+            mir::BinOp::Add             => llvm::LLVMConstAdd(lhs, rhs),
+
+            mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs),
+            mir::BinOp::Sub             => llvm::LLVMConstSub(lhs, rhs),
+
+            mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs),
+            mir::BinOp::Mul             => llvm::LLVMConstMul(lhs, rhs),
+
+            mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs),
+            mir::BinOp::Div if signed   => llvm::LLVMConstSDiv(lhs, rhs),
+            mir::BinOp::Div             => llvm::LLVMConstUDiv(lhs, rhs),
+
+            mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs),
+            mir::BinOp::Rem if signed   => llvm::LLVMConstSRem(lhs, rhs),
+            mir::BinOp::Rem             => llvm::LLVMConstURem(lhs, rhs),
+
+            mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs),
+            mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs),
+            mir::BinOp::BitOr  => llvm::LLVMConstOr(lhs, rhs),
+            mir::BinOp::Shl    => {
+                let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
+                llvm::LLVMConstShl(lhs, rhs)
+            }
+            mir::BinOp::Shr    => {
+                let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
+                if signed { llvm::LLVMConstAShr(lhs, rhs) }
+                else      { llvm::LLVMConstLShr(lhs, rhs) }
+            }
+            mir::BinOp::Eq | mir::BinOp::Ne |
+            mir::BinOp::Lt | mir::BinOp::Le |
+            mir::BinOp::Gt | mir::BinOp::Ge => {
+                if is_float {
+                    let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop());
+                    llvm::ConstFCmp(cmp, lhs, rhs)
+                } else {
+                    let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(),
+                                                                signed);
+                    llvm::ConstICmp(cmp, lhs, rhs)
+                }
+            }
+        }
+    }
+}
+
+pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                            op: mir::BinOp,
+                                            lllhs: ValueRef,
+                                            llrhs: ValueRef,
+                                            input_ty: Ty<'tcx>)
+                                            -> Option<(ValueRef, bool)> {
+    if let (Some(lhs), Some(rhs)) = (to_const_int(lllhs, input_ty, tcx),
+                                     to_const_int(llrhs, input_ty, tcx)) {
+        let result = match op {
+            mir::BinOp::Add => lhs + rhs,
+            mir::BinOp::Sub => lhs - rhs,
+            mir::BinOp::Mul => lhs * rhs,
+            mir::BinOp::Shl => lhs << rhs,
+            mir::BinOp::Shr => lhs >> rhs,
+            _ => {
+                bug!("Operator `{:?}` is not a checkable operator", op)
+            }
+        };
+
+        let of = match result {
+            Ok(_) => false,
+            Err(ConstMathErr::Overflow(_)) |
+            Err(ConstMathErr::ShiftNegative) => true,
+            Err(err) => {
+                bug!("Operator `{:?}` on `{:?}` and `{:?}` errored: {}",
+                     op, lhs, rhs, err.description());
+            }
+        };
+
+        Some((const_scalar_binop(op, lllhs, llrhs, input_ty), of))
+    } else {
+        None
+    }
 }
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
@@ -824,11 +904,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
                 let substs = bcx.monomorphize(&substs);
                 let instance = Instance::new(def_id, substs);
-                MirConstContext::trans_def(bcx.ccx(), instance, vec![])
+                MirConstContext::trans_def(bcx.ccx(), instance, IndexVec::new())
             }
             mir::Literal::Promoted { index } => {
                 let mir = &self.mir.promoted[index];
-                MirConstContext::new(bcx.ccx(), mir, bcx.fcx().param_substs, vec![]).trans()
+                MirConstContext::new(bcx.ccx(), mir, bcx.fcx().param_substs,
+                                     IndexVec::new()).trans()
             }
             mir::Literal::Value { value } => {
                 Ok(Const::from_constval(bcx.ccx(), value, ty))
@@ -855,5 +936,5 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 pub fn trans_static_initializer(ccx: &CrateContext, def_id: DefId)
                                 -> Result<ValueRef, ConstEvalFailure> {
     let instance = Instance::mono(ccx.shared(), def_id);
-    MirConstContext::trans_def(ccx, instance, vec![]).map(|c| c.llval)
+    MirConstContext::trans_def(ccx, instance, IndexVec::new()).map(|c| c.llval)
 }
diff --git a/src/librustc_trans/mir/drop.rs b/src/librustc_trans/mir/drop.rs
deleted file mode 100644 (file)
index 623cd5a..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use llvm::ValueRef;
-use rustc::ty::Ty;
-use adt;
-use base;
-use common::{self, BlockAndBuilder};
-use machine;
-use type_of;
-use type_::Type;
-
-pub fn drop_fill<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, value: ValueRef, ty: Ty<'tcx>) {
-    let llty = type_of::type_of(bcx.ccx(), ty);
-    let llptr = bcx.pointercast(value, Type::i8(bcx.ccx()).ptr_to());
-    let filling = common::C_u8(bcx.ccx(), adt::DTOR_DONE);
-    let size = machine::llsize_of(bcx.ccx(), llty);
-    let align = common::C_u32(bcx.ccx(), machine::llalign_of_min(bcx.ccx(), llty));
-    base::call_memset(&bcx, llptr, filling, size, align, false);
-}
index b39a6ac1ce38178ea3da711a16b0e31a362804e2..ceaba2a40ca55f77c7773194d179c0a838e53550 100644 (file)
@@ -12,6 +12,7 @@ use llvm::ValueRef;
 use rustc::ty::{self, Ty, TypeFoldable};
 use rustc::mir::repr as mir;
 use rustc::mir::tcx::LvalueTy;
+use rustc_data_structures::indexed_vec::Idx;
 use abi;
 use adt;
 use base;
@@ -20,14 +21,15 @@ use common::{self, BlockAndBuilder, CrateContext, C_uint, C_undef};
 use consts;
 use machine;
 use type_of::type_of;
-use mir::drop;
+use type_of;
 use Disr;
 
 use std::ptr;
 
-use super::{MirContext, TempRef};
+use super::{MirContext, LocalRef};
+use super::operand::OperandValue;
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 pub struct LvalueRef<'tcx> {
     /// Pointer to the contents of the lvalue
     pub llval: ValueRef,
@@ -51,9 +53,6 @@ impl<'tcx> LvalueRef<'tcx> {
     {
         assert!(!ty.has_erasable_regions());
         let lltemp = bcx.with_block(|bcx| base::alloc_ty(bcx, ty, name));
-        if bcx.fcx().type_needs_drop(ty) {
-            drop::drop_fill(bcx, lltemp, ty);
-        }
         LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty))
     }
 
@@ -89,39 +88,50 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         -> LvalueRef<'tcx> {
         debug!("trans_lvalue(lvalue={:?})", lvalue);
 
-        let fcx = bcx.fcx();
         let ccx = bcx.ccx();
         let tcx = bcx.tcx();
-        match *lvalue {
-            mir::Lvalue::Var(index) => self.vars[index as usize],
-            mir::Lvalue::Temp(index) => match self.temps[index as usize] {
-                TempRef::Lvalue(lvalue) =>
-                    lvalue,
-                TempRef::Operand(..) =>
-                    bug!("using operand temp {:?} as lvalue", lvalue),
-            },
-            mir::Lvalue::Arg(index) => self.args[index as usize],
+
+        if let Some(index) = self.mir.local_index(lvalue) {
+            match self.locals[index] {
+                LocalRef::Lvalue(lvalue) => {
+                    return lvalue;
+                }
+                LocalRef::Operand(..) => {
+                    bug!("using operand local {:?} as lvalue", lvalue);
+                }
+            }
+        }
+
+        let result = match *lvalue {
+            mir::Lvalue::Var(_) |
+            mir::Lvalue::Temp(_) |
+            mir::Lvalue::Arg(_) |
+            mir::Lvalue::ReturnPointer => bug!(), // handled above
             mir::Lvalue::Static(def_id) => {
-                let const_ty = self.mir.lvalue_ty(tcx, lvalue);
-                LvalueRef::new_sized(consts::get_static(ccx, def_id).val, const_ty)
+                let const_ty = self.lvalue_ty(lvalue);
+                LvalueRef::new_sized(consts::get_static(ccx, def_id).val,
+                                     LvalueTy::from_ty(const_ty))
             },
-            mir::Lvalue::ReturnPointer => {
-                let llval = if !fcx.fn_ty.ret.is_ignore() {
-                    bcx.with_block(|bcx| {
-                        fcx.get_ret_slot(bcx, "")
-                    })
-                } else {
-                    // This is a void return; that is, there’s no place to store the value and
-                    // there cannot really be one (or storing into it doesn’t make sense, anyway).
-                    // Ergo, we return an undef ValueRef, so we do not have to special-case every
-                    // place using lvalues, and could use it the same way you use a regular
-                    // ReturnPointer LValue (i.e. store into it, load from it etc).
-                    C_undef(fcx.fn_ty.ret.original_ty.ptr_to())
+            mir::Lvalue::Projection(box mir::Projection {
+                ref base,
+                elem: mir::ProjectionElem::Deref
+            }) => {
+                // Load the pointer from its location.
+                let ptr = self.trans_consume(bcx, base);
+                let projected_ty = LvalueTy::from_ty(ptr.ty)
+                    .projection_ty(tcx, &mir::ProjectionElem::Deref);
+                let projected_ty = bcx.monomorphize(&projected_ty);
+                let (llptr, llextra) = match ptr.val {
+                    OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()),
+                    OperandValue::Pair(llptr, llextra) => (llptr, llextra),
+                    OperandValue::Ref(_) => bug!("Deref of by-Ref type {:?}", ptr.ty)
                 };
-                let fn_return_ty = bcx.monomorphize(&self.mir.return_ty);
-                let return_ty = fn_return_ty.unwrap();
-                LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty))
-            },
+                LvalueRef {
+                    llval: llptr,
+                    llextra: llextra,
+                    ty: projected_ty,
+                }
+            }
             mir::Lvalue::Projection(ref projection) => {
                 let tr_base = self.trans_lvalue(bcx, &projection.base);
                 let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem);
@@ -135,19 +145,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         let zero = common::C_uint(bcx.ccx(), 0u64);
                         bcx.inbounds_gep(tr_base.llval, &[zero, llindex])
                     };
-                    (element, ptr::null_mut())
+                    element
                 };
 
                 let (llprojected, llextra) = match projection.elem {
-                    mir::ProjectionElem::Deref => {
-                        let base_ty = tr_base.ty.to_ty(tcx);
-                        if common::type_is_sized(tcx, projected_ty.to_ty(tcx)) {
-                            (base::load_ty_builder(bcx, tr_base.llval, base_ty),
-                             ptr::null_mut())
-                        } else {
-                            load_fat_ptr(bcx, tr_base.llval)
-                        }
-                    }
+                    mir::ProjectionElem::Deref => bug!(),
                     mir::ProjectionElem::Field(ref field, _) => {
                         let base_ty = tr_base.ty.to_ty(tcx);
                         let base_repr = adt::represent_type(ccx, base_ty);
@@ -173,13 +175,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     }
                     mir::ProjectionElem::Index(ref index) => {
                         let index = self.trans_operand(bcx, index);
-                        project_index(self.prepare_index(bcx, index.immediate()))
+                        (project_index(self.prepare_index(bcx, index.immediate())), ptr::null_mut())
                     }
                     mir::ProjectionElem::ConstantIndex { offset,
                                                          from_end: false,
                                                          min_length: _ } => {
                         let lloffset = C_uint(bcx.ccx(), offset);
-                        project_index(self.prepare_index(bcx, lloffset))
+                        (project_index(lloffset), ptr::null_mut())
                     }
                     mir::ProjectionElem::ConstantIndex { offset,
                                                          from_end: true,
@@ -187,7 +189,30 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         let lloffset = C_uint(bcx.ccx(), offset);
                         let lllen = tr_base.len(bcx.ccx());
                         let llindex = bcx.sub(lllen, lloffset);
-                        project_index(self.prepare_index(bcx, llindex))
+                        (project_index(llindex), ptr::null_mut())
+                    }
+                    mir::ProjectionElem::Subslice { from, to } => {
+                        let llindex = C_uint(bcx.ccx(), from);
+                        let llbase = project_index(llindex);
+
+                        let base_ty = tr_base.ty.to_ty(bcx.tcx());
+                        match base_ty.sty {
+                            ty::TyArray(..) => {
+                                // must cast the lvalue pointer type to the new
+                                // array type (*[%_; new_len]).
+                                let base_ty = self.lvalue_ty(lvalue);
+                                let llbasety = type_of::type_of(bcx.ccx(), base_ty).ptr_to();
+                                let llbase = bcx.pointercast(llbase, llbasety);
+                                (llbase, ptr::null_mut())
+                            }
+                            ty::TySlice(..) => {
+                                assert!(tr_base.llextra != ptr::null_mut());
+                                let lllen = bcx.sub(tr_base.llextra,
+                                                    C_uint(bcx.ccx(), from+to));
+                                (llbase, lllen)
+                            }
+                            _ => bug!("unexpected type {:?} in Subslice", base_ty)
+                        }
                     }
                     mir::ProjectionElem::Downcast(..) => {
                         (tr_base.llval, tr_base.llextra)
@@ -199,52 +224,47 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     ty: projected_ty,
                 }
             }
-        }
+        };
+        debug!("trans_lvalue(lvalue={:?}) => {:?}", lvalue, result);
+        result
     }
 
     // Perform an action using the given Lvalue.
-    // If the Lvalue is an empty TempRef::Operand, then a temporary stack slot
+    // If the Lvalue is an empty LocalRef::Operand, then a temporary stack slot
     // is created first, then used as an operand to update the Lvalue.
     pub fn with_lvalue_ref<F, U>(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
                                  lvalue: &mir::Lvalue<'tcx>, f: F) -> U
     where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U
     {
-        match *lvalue {
-            mir::Lvalue::Temp(idx) => {
-                match self.temps[idx as usize] {
-                    TempRef::Lvalue(lvalue) => f(self, lvalue),
-                    TempRef::Operand(None) => {
-                        let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), lvalue);
-                        let lvalue_ty = bcx.monomorphize(&lvalue_ty);
-                        let lvalue = LvalueRef::alloca(bcx,
-                                                       lvalue_ty.to_ty(bcx.tcx()),
-                                                       "lvalue_temp");
-                        let ret = f(self, lvalue);
-                        let op = self.trans_load(bcx, lvalue.llval, lvalue_ty.to_ty(bcx.tcx()));
-                        self.temps[idx as usize] = TempRef::Operand(Some(op));
-                        ret
-                    }
-                    TempRef::Operand(Some(_)) => {
-                        let lvalue_ty = self.mir.lvalue_ty(bcx.tcx(), lvalue);
-                        let lvalue_ty = bcx.monomorphize(&lvalue_ty);
-
-                        // See comments in TempRef::new_operand as to why
-                        // we always have Some in a ZST TempRef::Operand.
-                        let ty = lvalue_ty.to_ty(bcx.tcx());
-                        if common::type_is_zero_size(bcx.ccx(), ty) {
-                            // Pass an undef pointer as no stores can actually occur.
-                            let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to());
-                            f(self, LvalueRef::new_sized(llptr, lvalue_ty))
-                        } else {
-                            bug!("Lvalue temp already set");
-                        }
+        if let Some(index) = self.mir.local_index(lvalue) {
+            match self.locals[index] {
+                LocalRef::Lvalue(lvalue) => f(self, lvalue),
+                LocalRef::Operand(None) => {
+                    let lvalue_ty = self.lvalue_ty(lvalue);
+                    let lvalue = LvalueRef::alloca(bcx,
+                                                   lvalue_ty,
+                                                   "lvalue_temp");
+                    let ret = f(self, lvalue);
+                    let op = self.trans_load(bcx, lvalue.llval, lvalue_ty);
+                    self.locals[index] = LocalRef::Operand(Some(op));
+                    ret
+                }
+                LocalRef::Operand(Some(_)) => {
+                    // See comments in LocalRef::new_operand as to why
+                    // we always have Some in a ZST LocalRef::Operand.
+                    let ty = self.lvalue_ty(lvalue);
+                    if common::type_is_zero_size(bcx.ccx(), ty) {
+                        // Pass an undef pointer as no stores can actually occur.
+                        let llptr = C_undef(type_of(bcx.ccx(), ty).ptr_to());
+                        f(self, LvalueRef::new_sized(llptr, LvalueTy::from_ty(ty)))
+                    } else {
+                        bug!("Lvalue local already set");
                     }
                 }
             }
-            _ => {
-                let lvalue = self.trans_lvalue(bcx, lvalue);
-                f(self, lvalue)
-            }
+        } else {
+            let lvalue = self.trans_lvalue(bcx, lvalue);
+            f(self, lvalue)
         }
     }
 
@@ -268,4 +288,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             llindex
         }
     }
+
+    pub fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
+        let tcx = self.fcx.ccx.tcx();
+        let lvalue_ty = self.mir.lvalue_ty(tcx, lvalue);
+        self.fcx.monomorphize(&lvalue_ty.to_ty(tcx))
+    }
 }
index b98e04e51c007d41e2a4f37c8b52de176438e1f9..0221232a77df5275a89754b15c3e39e5104ce411 100644 (file)
@@ -16,12 +16,12 @@ use rustc::mir::repr as mir;
 use rustc::mir::tcx::LvalueTy;
 use session::config::FullDebugInfo;
 use base;
-use common::{self, Block, BlockAndBuilder, CrateContext, FunctionContext};
+use common::{self, Block, BlockAndBuilder, CrateContext, FunctionContext, C_null};
 use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind};
 use machine;
 use type_of;
 
-use syntax::codemap::DUMMY_SP;
+use syntax_pos::DUMMY_SP;
 use syntax::parse::token::keywords;
 
 use std::ops::Deref;
@@ -30,11 +30,12 @@ use std::rc::Rc;
 use basic_block::BasicBlock;
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 
 pub use self::constant::trans_static_initializer;
 
 use self::lvalue::{LvalueRef, get_dataptr, get_meta};
-use rustc_mir::traversal;
+use rustc::mir::traversal;
 
 use self::operand::{OperandRef, OperandValue};
 
@@ -71,21 +72,25 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
     llpersonalityslot: Option<ValueRef>,
 
     /// A `Block` for each MIR `BasicBlock`
-    blocks: Vec<Block<'bcx, 'tcx>>,
+    blocks: IndexVec<mir::BasicBlock, Block<'bcx, 'tcx>>,
+
+    /// The funclet status of each basic block
+    cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>,
+
+    /// This stores the landing-pad block for a given BB, computed lazily on GNU
+    /// and eagerly on MSVC.
+    landing_pads: IndexVec<mir::BasicBlock, Option<Block<'bcx, 'tcx>>>,
 
     /// Cached unreachable block
     unreachable_block: Option<Block<'bcx, 'tcx>>,
 
-    /// An LLVM alloca for each MIR `VarDecl`
-    vars: Vec<LvalueRef<'tcx>>,
-
-    /// The location where each MIR `TempDecl` is stored. This is
+    /// The location where each MIR arg/var/tmp/ret is stored. This is
     /// usually an `LvalueRef` representing an alloca, but not always:
     /// sometimes we can skip the alloca and just store the value
     /// directly using an `OperandRef`, which makes for tighter LLVM
     /// IR. The conditions for using an `OperandRef` are as follows:
     ///
-    /// - the type of the temporary must be judged "immediate" by `type_is_immediate`
+    /// - the type of the local must be judged "immediate" by `type_is_immediate`
     /// - the operand must never be referenced indirectly
     ///     - we should not take its address using the `&` operator
     ///     - nor should it appear in an lvalue path like `tmp.a`
@@ -94,37 +99,44 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
     ///
     /// Avoiding allocs can also be important for certain intrinsics,
     /// notably `expect`.
-    temps: Vec<TempRef<'tcx>>,
-
-    /// The arguments to the function; as args are lvalues, these are
-    /// always indirect, though we try to avoid creating an alloca
-    /// when we can (and just reuse the pointer the caller provided).
-    args: Vec<LvalueRef<'tcx>>,
+    locals: IndexVec<mir::Local, LocalRef<'tcx>>,
 
     /// Debug information for MIR scopes.
-    scopes: Vec<DIScope>
+    scopes: IndexVec<mir::VisibilityScope, DIScope>
+}
+
+impl<'blk, 'tcx> MirContext<'blk, 'tcx> {
+    pub fn debug_loc(&self, source_info: mir::SourceInfo) -> DebugLoc {
+        DebugLoc::ScopeAt(self.scopes[source_info.scope], source_info.span)
+    }
 }
 
-enum TempRef<'tcx> {
+enum LocalRef<'tcx> {
     Lvalue(LvalueRef<'tcx>),
     Operand(Option<OperandRef<'tcx>>),
 }
 
-impl<'tcx> TempRef<'tcx> {
+impl<'tcx> LocalRef<'tcx> {
     fn new_operand<'bcx>(ccx: &CrateContext<'bcx, 'tcx>,
-                         ty: ty::Ty<'tcx>) -> TempRef<'tcx> {
+                         ty: ty::Ty<'tcx>) -> LocalRef<'tcx> {
         if common::type_is_zero_size(ccx, ty) {
             // Zero-size temporaries aren't always initialized, which
             // doesn't matter because they don't contain data, but
             // we need something in the operand.
-            let val = OperandValue::Immediate(common::C_nil(ccx));
+            let llty = type_of::type_of(ccx, ty);
+            let val = if common::type_is_imm_pair(ccx, ty) {
+                let fields = llty.field_types();
+                OperandValue::Pair(C_null(fields[0]), C_null(fields[1]))
+            } else {
+                OperandValue::Immediate(C_null(llty))
+            };
             let op = OperandRef {
                 val: val,
                 ty: ty
             };
-            TempRef::Operand(Some(op))
+            LocalRef::Operand(Some(op))
         } else {
-            TempRef::Operand(None)
+            LocalRef::Operand(None)
         }
     }
 }
@@ -135,64 +147,73 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
     let bcx = fcx.init(false, None).build();
     let mir = bcx.mir();
 
-    let mir_blocks = mir.all_basic_blocks();
-
     // Analyze the temps to determine which must be lvalues
     // FIXME
-    let lvalue_temps = bcx.with_block(|bcx| {
-      analyze::lvalue_temps(bcx, &mir)
+    let (lvalue_locals, cleanup_kinds) = bcx.with_block(|bcx| {
+        (analyze::lvalue_locals(bcx, &mir),
+         analyze::cleanup_kinds(bcx, &mir))
     });
 
     // Compute debuginfo scopes from MIR scopes.
     let scopes = debuginfo::create_mir_scopes(fcx);
 
     // Allocate variable and temp allocas
-    let args = arg_value_refs(&bcx, &mir, &scopes);
-    let vars = mir.var_decls.iter()
-                            .map(|decl| (bcx.monomorphize(&decl.ty), decl))
-                            .map(|(mty, decl)| {
-        let lvalue = LvalueRef::alloca(&bcx, mty, &decl.name.as_str());
-
-        let scope = scopes[decl.scope.index()];
-        if !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo {
-            bcx.with_block(|bcx| {
-                declare_local(bcx, decl.name, mty, scope,
-                              VariableAccess::DirectVariable { alloca: lvalue.llval },
-                              VariableKind::LocalVariable, decl.span);
-            });
-        }
+    let locals = {
+        let args = arg_local_refs(&bcx, &mir, &scopes, &lvalue_locals);
+        let vars = mir.var_decls.iter().enumerate().map(|(i, decl)| {
+            let ty = bcx.monomorphize(&decl.ty);
+            let scope = scopes[decl.source_info.scope];
+            let dbg = !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo;
+
+            let local = mir.local_index(&mir::Lvalue::Var(mir::Var::new(i))).unwrap();
+            if !lvalue_locals.contains(local.index()) && !dbg {
+                return LocalRef::new_operand(bcx.ccx(), ty);
+            }
 
-        lvalue
-    }).collect();
-    let temps = mir.temp_decls.iter()
-                              .map(|decl| bcx.monomorphize(&decl.ty))
-                              .enumerate()
-                              .map(|(i, mty)| if lvalue_temps.contains(i) {
-                                  TempRef::Lvalue(LvalueRef::alloca(&bcx,
-                                                                    mty,
-                                                                    &format!("temp{:?}", i)))
-                              } else {
-                                  // If this is an immediate temp, we do not create an
-                                  // alloca in advance. Instead we wait until we see the
-                                  // definition and update the operand there.
-                                  TempRef::new_operand(bcx.ccx(), mty)
-                              })
-                              .collect();
+            let lvalue = LvalueRef::alloca(&bcx, ty, &decl.name.as_str());
+            if dbg {
+                bcx.with_block(|bcx| {
+                    declare_local(bcx, decl.name, ty, scope,
+                                VariableAccess::DirectVariable { alloca: lvalue.llval },
+                                VariableKind::LocalVariable, decl.source_info.span);
+                });
+            }
+            LocalRef::Lvalue(lvalue)
+        });
+
+        let locals = mir.temp_decls.iter().enumerate().map(|(i, decl)| {
+            (mir::Lvalue::Temp(mir::Temp::new(i)), decl.ty)
+        }).chain(mir.return_ty.maybe_converging().map(|ty| (mir::Lvalue::ReturnPointer, ty)));
+
+        args.into_iter().chain(vars).chain(locals.map(|(lvalue, ty)| {
+            let ty = bcx.monomorphize(&ty);
+            let local = mir.local_index(&lvalue).unwrap();
+            if lvalue == mir::Lvalue::ReturnPointer && fcx.fn_ty.ret.is_indirect() {
+                let llretptr = llvm::get_param(fcx.llfn, 0);
+                LocalRef::Lvalue(LvalueRef::new_sized(llretptr, LvalueTy::from_ty(ty)))
+            } else if lvalue_locals.contains(local.index()) {
+                LocalRef::Lvalue(LvalueRef::alloca(&bcx, ty, &format!("{:?}", lvalue)))
+            } else {
+                // If this is an immediate local, we do not create an
+                // alloca in advance. Instead we wait until we see the
+                // definition and update the operand there.
+                LocalRef::new_operand(bcx.ccx(), ty)
+            }
+        })).collect()
+    };
 
     // Allocate a `Block` for every basic block
-    let block_bcxs: Vec<Block<'blk,'tcx>> =
-        mir_blocks.iter()
-                  .map(|&bb|{
-                      if bb == mir::START_BLOCK {
-                          fcx.new_block("start", None)
-                      } else {
-                          fcx.new_block(&format!("{:?}", bb), None)
-                      }
-                  })
-                  .collect();
+    let block_bcxs: IndexVec<mir::BasicBlock, Block<'blk,'tcx>> =
+        mir.basic_blocks().indices().map(|bb| {
+            if bb == mir::START_BLOCK {
+                fcx.new_block("start", None)
+            } else {
+                fcx.new_block(&format!("{:?}", bb), None)
+            }
+        }).collect();
 
     // Branch to the START block
-    let start_bcx = block_bcxs[mir::START_BLOCK.index()];
+    let start_bcx = block_bcxs[mir::START_BLOCK];
     bcx.br(start_bcx.llbb);
 
     // Up until here, IR instructions for this function have explicitly not been annotated with
@@ -206,15 +227,22 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
         llpersonalityslot: None,
         blocks: block_bcxs,
         unreachable_block: None,
-        vars: vars,
-        temps: temps,
-        args: args,
+        cleanup_kinds: cleanup_kinds,
+        landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
+        locals: locals,
         scopes: scopes
     };
 
-    let mut visited = BitVector::new(mir_blocks.len());
+    let mut visited = BitVector::new(mir.basic_blocks().len());
+
+    let mut rpo = traversal::reverse_postorder(&mir);
+
+    // Prepare each block for translation.
+    for (bb, _) in rpo.by_ref() {
+        mircx.init_cpad(bb);
+    }
+    rpo.reset();
 
-    let rpo = traversal::reverse_postorder(&mir);
     // Translate the body of each block using reverse postorder
     for (bb, _) in rpo {
         visited.insert(bb.index());
@@ -223,13 +251,12 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
 
     // Remove blocks that haven't been visited, or have no
     // predecessors.
-    for &bb in &mir_blocks {
-        let block = mircx.blocks[bb.index()];
+    for bb in mir.basic_blocks().indices() {
+        let block = mircx.blocks[bb];
         let block = BasicBlock(block.llbb);
         // Unreachable block
         if !visited.contains(bb.index()) {
-            block.delete();
-        } else if block.pred_iter().count() == 0 {
+            debug!("trans_mir: block {:?} was not visited", bb);
             block.delete();
         }
     }
@@ -241,28 +268,27 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
 /// Produce, for each argument, a `ValueRef` pointing at the
 /// argument's value. As arguments are lvalues, these are always
 /// indirect.
-fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
+fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                               mir: &mir::Mir<'tcx>,
-                              scopes: &[DIScope])
-                              -> Vec<LvalueRef<'tcx>> {
+                              scopes: &IndexVec<mir::VisibilityScope, DIScope>,
+                              lvalue_locals: &BitVector)
+                              -> Vec<LocalRef<'tcx>> {
     let fcx = bcx.fcx();
     let tcx = bcx.tcx();
     let mut idx = 0;
     let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize;
 
-    // Get the argument scope assuming ScopeId(0) has no parent.
-    let arg_scope = mir.scopes.get(0).and_then(|data| {
-        let scope = scopes[0];
-        if data.parent_scope.is_none() && !scope.is_null() &&
-           bcx.sess().opts.debuginfo == FullDebugInfo {
-            Some(scope)
-        } else {
-            None
-        }
-    });
+    // Get the argument scope, if it exists and if we need it.
+    let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE];
+    let arg_scope = if !arg_scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo {
+        Some(arg_scope)
+    } else {
+        None
+    };
 
     mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| {
         let arg_ty = bcx.monomorphize(&arg_decl.ty);
+        let local = mir.local_index(&mir::Lvalue::Arg(mir::Arg::new(arg_index))).unwrap();
         if arg_decl.spread {
             // This argument (e.g. the last argument in the "rust-call" ABI)
             // is a tuple that was spread at the ABI level and now we have
@@ -283,8 +309,8 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                 let arg = &fcx.fn_ty.args[idx];
                 idx += 1;
                 if common::type_is_fat_ptr(tcx, tupled_arg_ty) {
-                        // We pass fat pointers as two words, but inside the tuple
-                        // they are the two sub-fields of a single aggregate field.
+                    // We pass fat pointers as two words, but inside the tuple
+                    // they are the two sub-fields of a single aggregate field.
                     let meta = &fcx.fn_ty.args[idx];
                     idx += 1;
                     arg.store_fn_arg(bcx, &mut llarg_idx, get_dataptr(bcx, dst));
@@ -313,7 +339,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                                   bcx.fcx().span.unwrap_or(DUMMY_SP));
                 }));
             }
-            return LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty));
+            return LocalRef::Lvalue(LvalueRef::new_sized(lltemp, LvalueTy::from_ty(arg_ty)));
         }
 
         let arg = &fcx.fn_ty.args[idx];
@@ -323,9 +349,42 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
             // already put it in a temporary alloca and gave it up, unless
             // we emit extra-debug-info, which requires local allocas :(.
             // FIXME: lifetimes
+            if arg.pad.is_some() {
+                llarg_idx += 1;
+            }
             let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
             llarg_idx += 1;
             llarg
+        } else if !lvalue_locals.contains(local.index()) &&
+                  !arg.is_indirect() && arg.cast.is_none() &&
+                  arg_scope.is_none() {
+            if arg.is_ignore() {
+                return LocalRef::new_operand(bcx.ccx(), arg_ty);
+            }
+
+            // We don't have to cast or keep the argument in the alloca.
+            // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
+            // of putting everything in allocas just so we can use llvm.dbg.declare.
+            if arg.pad.is_some() {
+                llarg_idx += 1;
+            }
+            let llarg = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
+            llarg_idx += 1;
+            let val = if common::type_is_fat_ptr(tcx, arg_ty) {
+                let meta = &fcx.fn_ty.args[idx];
+                idx += 1;
+                assert_eq!((meta.cast, meta.pad), (None, None));
+                let llmeta = llvm::get_param(fcx.llfn, llarg_idx as c_uint);
+                llarg_idx += 1;
+                OperandValue::Pair(llarg, llmeta)
+            } else {
+                OperandValue::Immediate(llarg)
+            };
+            let operand = OperandRef {
+                val: val,
+                ty: arg_ty
+            };
+            return LocalRef::Operand(Some(operand.unpack_if_pair(bcx)));
         } else {
             let lltemp = bcx.with_block(|bcx| {
                 base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index))
@@ -419,14 +478,13 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                               bcx.fcx().span.unwrap_or(DUMMY_SP));
             }
         }));
-        LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))
+        LocalRef::Lvalue(LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)))
     }).collect()
 }
 
 mod analyze;
 mod block;
 mod constant;
-mod drop;
 mod lvalue;
 mod operand;
 mod rvalue;
index fc726a3474f7e6d996b506b08e1cf6306338ee3f..446ac91b1f58086d990aea8eeb3154542a0341de 100644 (file)
 use llvm::ValueRef;
 use rustc::ty::Ty;
 use rustc::mir::repr as mir;
+use rustc_data_structures::indexed_vec::Idx;
+
 use base;
 use common::{self, Block, BlockAndBuilder};
-use datum;
 use value::Value;
-use glue;
+use type_of;
+use type_::Type;
 
 use std::fmt;
 
-use super::lvalue::load_fat_ptr;
-use super::{MirContext, TempRef, drop};
+use super::{MirContext, LocalRef};
 
 /// The representation of a Rust value. The enum variant is in fact
 /// uniquely determined by the value's type, but is kept as a
@@ -32,9 +33,8 @@ pub enum OperandValue {
     Ref(ValueRef),
     /// A single LLVM value.
     Immediate(ValueRef),
-    /// A fat pointer. The first ValueRef is the data and the second
-    /// is the extra.
-    FatPtr(ValueRef, ValueRef)
+    /// A pair of immediate LLVM values. Used by fat pointers too.
+    Pair(ValueRef, ValueRef)
 }
 
 /// An `OperandRef` is an "SSA" reference to a Rust value, along with
@@ -65,15 +65,15 @@ impl<'tcx> fmt::Debug for OperandRef<'tcx> {
                 write!(f, "OperandRef(Immediate({:?}) @ {:?})",
                        Value(i), self.ty)
             }
-            OperandValue::FatPtr(a, d) => {
-                write!(f, "OperandRef(FatPtr({:?}, {:?}) @ {:?})",
-                       Value(a), Value(d), self.ty)
+            OperandValue::Pair(a, b) => {
+                write!(f, "OperandRef(Pair({:?}, {:?}) @ {:?})",
+                       Value(a), Value(b), self.ty)
             }
         }
     }
 }
 
-impl<'tcx> OperandRef<'tcx> {
+impl<'bcx, 'tcx> OperandRef<'tcx> {
     /// Asserts that this operand refers to a scalar and returns
     /// a reference to its value.
     pub fn immediate(self) -> ValueRef {
@@ -82,6 +82,56 @@ impl<'tcx> OperandRef<'tcx> {
             _ => bug!()
         }
     }
+
+    /// If this operand is a Pair, we return an
+    /// Immediate aggregate with the two values.
+    pub fn pack_if_pair(mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>)
+                        -> OperandRef<'tcx> {
+        if let OperandValue::Pair(a, b) = self.val {
+            // Reconstruct the immediate aggregate.
+            let llty = type_of::type_of(bcx.ccx(), self.ty);
+            let mut llpair = common::C_undef(llty);
+            let elems = [a, b];
+            for i in 0..2 {
+                let mut elem = elems[i];
+                // Extend boolean i1's to i8.
+                if common::val_ty(elem) == Type::i1(bcx.ccx()) {
+                    elem = bcx.zext(elem, Type::i8(bcx.ccx()));
+                }
+                llpair = bcx.insert_value(llpair, elem, i);
+            }
+            self.val = OperandValue::Immediate(llpair);
+        }
+        self
+    }
+
+    /// If this operand is a pair in an Immediate,
+    /// we return a Pair with the two halves.
+    pub fn unpack_if_pair(mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>)
+                          -> OperandRef<'tcx> {
+        if let OperandValue::Immediate(llval) = self.val {
+            // Deconstruct the immediate aggregate.
+            if common::type_is_imm_pair(bcx.ccx(), self.ty) {
+                debug!("Operand::unpack_if_pair: unpacking {:?}", self);
+
+                let mut a = bcx.extract_value(llval, 0);
+                let mut b = bcx.extract_value(llval, 1);
+
+                let pair_fields = common::type_pair_fields(bcx.ccx(), self.ty);
+                if let Some([a_ty, b_ty]) = pair_fields {
+                    if a_ty.is_bool() {
+                        a = bcx.trunc(a, Type::i1(bcx.ccx()));
+                    }
+                    if b_ty.is_bool() {
+                        b = bcx.trunc(b, Type::i1(bcx.ccx()));
+                    }
+                }
+
+                self.val = OperandValue::Pair(a, b);
+            }
+        }
+        self
+    }
 }
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
@@ -93,20 +143,78 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     {
         debug!("trans_load: {:?} @ {:?}", Value(llval), ty);
 
-        let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
-            datum::ByValue => {
-                OperandValue::Immediate(base::load_ty_builder(bcx, llval, ty))
-            }
-            datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => {
-                let (lldata, llextra) = load_fat_ptr(bcx, llval);
-                OperandValue::FatPtr(lldata, llextra)
-            }
-            datum::ByRef => OperandValue::Ref(llval)
+        let val = if common::type_is_imm_pair(bcx.ccx(), ty) {
+            let a_ptr = bcx.struct_gep(llval, 0);
+            let b_ptr = bcx.struct_gep(llval, 1);
+
+            // This is None only for fat pointers, which don't
+            // need any special load-time behavior anyway.
+            let pair_fields = common::type_pair_fields(bcx.ccx(), ty);
+            let (a, b) = if let Some([a_ty, b_ty]) = pair_fields {
+                (base::load_ty_builder(bcx, a_ptr, a_ty),
+                 base::load_ty_builder(bcx, b_ptr, b_ty))
+            } else {
+                (bcx.load(a_ptr), bcx.load(b_ptr))
+            };
+            OperandValue::Pair(a, b)
+        } else if common::type_is_immediate(bcx.ccx(), ty) {
+            OperandValue::Immediate(base::load_ty_builder(bcx, llval, ty))
+        } else {
+            OperandValue::Ref(llval)
         };
 
         OperandRef { val: val, ty: ty }
     }
 
+    pub fn trans_consume(&mut self,
+                         bcx: &BlockAndBuilder<'bcx, 'tcx>,
+                         lvalue: &mir::Lvalue<'tcx>)
+                         -> OperandRef<'tcx>
+    {
+        debug!("trans_consume(lvalue={:?})", lvalue);
+
+        // watch out for locals that do not have an
+        // alloca; they are handled somewhat differently
+        if let Some(index) = self.mir.local_index(lvalue) {
+            match self.locals[index] {
+                LocalRef::Operand(Some(o)) => {
+                    return o;
+                }
+                LocalRef::Operand(None) => {
+                    bug!("use of {:?} before def", lvalue);
+                }
+                LocalRef::Lvalue(..) => {
+                    // use path below
+                }
+            }
+        }
+
+        // Moves out of pair fields are trivial.
+        if let &mir::Lvalue::Projection(ref proj) = lvalue {
+            if let Some(index) = self.mir.local_index(&proj.base) {
+                if let LocalRef::Operand(Some(o)) = self.locals[index] {
+                    match (o.val, &proj.elem) {
+                        (OperandValue::Pair(a, b),
+                         &mir::ProjectionElem::Field(ref f, ty)) => {
+                            let llval = [a, b][f.index()];
+                            return OperandRef {
+                                val: OperandValue::Immediate(llval),
+                                ty: bcx.monomorphize(&ty)
+                            };
+                        }
+                        _ => {}
+                    }
+                }
+            }
+        }
+
+        // for most lvalues, to consume them we just load them
+        // out from their home
+        let tr_lvalue = self.trans_lvalue(bcx, lvalue);
+        let ty = tr_lvalue.ty.to_ty(bcx.tcx());
+        self.trans_load(bcx, tr_lvalue.llval, ty)
+    }
+
     pub fn trans_operand(&mut self,
                          bcx: &BlockAndBuilder<'bcx, 'tcx>,
                          operand: &mir::Operand<'tcx>)
@@ -116,27 +224,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
         match *operand {
             mir::Operand::Consume(ref lvalue) => {
-                // watch out for temporaries that do not have an
-                // alloca; they are handled somewhat differently
-                if let &mir::Lvalue::Temp(index) = lvalue {
-                    match self.temps[index as usize] {
-                        TempRef::Operand(Some(o)) => {
-                            return o;
-                        }
-                        TempRef::Operand(None) => {
-                            bug!("use of {:?} before def", lvalue);
-                        }
-                        TempRef::Lvalue(..) => {
-                            // use path below
-                        }
-                    }
-                }
-
-                // for most lvalues, to consume them we just load them
-                // out from their home
-                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
-                let ty = tr_lvalue.ty.to_ty(bcx.tcx());
-                self.trans_load(bcx, tr_lvalue.llval, ty)
+                self.trans_consume(bcx, lvalue)
             }
 
             mir::Operand::Constant(ref constant) => {
@@ -174,33 +262,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         match operand.val {
             OperandValue::Ref(r) => base::memcpy_ty(bcx, lldest, r, operand.ty),
             OperandValue::Immediate(s) => base::store_ty(bcx, s, lldest, operand.ty),
-            OperandValue::FatPtr(data, extra) => {
-                base::store_fat_ptr(bcx, data, extra, lldest, operand.ty);
-            }
-        }
-    }
-
-    pub fn set_operand_dropped(&mut self,
-                               bcx: &BlockAndBuilder<'bcx, 'tcx>,
-                               operand: &mir::Operand<'tcx>) {
-        match *operand {
-            mir::Operand::Constant(_) => return,
-            mir::Operand::Consume(ref lvalue) => {
-                if let mir::Lvalue::Temp(idx) = *lvalue {
-                    if let TempRef::Operand(..) = self.temps[idx as usize] {
-                        // All lvalues which have an associated drop are promoted to an alloca
-                        // beforehand. If this is an operand, it is safe to say this is never
-                        // dropped and there’s no reason for us to zero this out at all.
-                        return
-                    }
-                }
-                let lvalue = self.trans_lvalue(bcx, lvalue);
-                let ty = lvalue.ty.to_ty(bcx.tcx());
-                if !glue::type_needs_drop(bcx.tcx(), ty) {
-                    return
-                } else {
-                    drop::drop_fill(bcx, lvalue.llval, ty);
-                }
+            OperandValue::Pair(a, b) => {
+                use build::*;
+                let a = base::from_immediate(bcx, a);
+                let b = base::from_immediate(bcx, b);
+                Store(bcx, a, StructGEP(bcx, lldest, 0));
+                Store(bcx, b, StructGEP(bcx, lldest, 1));
             }
         }
     }
index 5945e8813a48ddc96101b5e700075973537b2cde..c3f2c4f2c8bfe5bf333550dbadc1ff6687690620 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use llvm::ValueRef;
+use llvm::{self, ValueRef};
 use rustc::ty::{self, Ty};
 use rustc::ty::cast::{CastTy, IntTy};
 use rustc::mir::repr as mir;
@@ -16,7 +16,7 @@ use rustc::mir::repr as mir;
 use asm;
 use base;
 use callee::Callee;
-use common::{self, C_uint, BlockAndBuilder, Result};
+use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder, Result};
 use datum::{Datum, Lvalue};
 use debuginfo::DebugLoc;
 use adt;
@@ -25,11 +25,11 @@ use type_of;
 use tvec;
 use value::Value;
 use Disr;
-use glue;
 
 use super::MirContext;
+use super::constant::const_scalar_checked_binop;
 use super::operand::{OperandRef, OperandValue};
-use super::lvalue::{LvalueRef, get_dataptr, get_meta};
+use super::lvalue::{LvalueRef, get_dataptr};
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_rvalue(&mut self,
@@ -48,7 +48,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                // FIXME: consider not copying constants through stack. (fixable by translating
                // constants into OperandValue::Ref, why don’t we do that yet if we don’t?)
                self.store_operand(&bcx, dest.llval, tr_operand);
-               self.set_operand_dropped(&bcx, operand);
                bcx
            }
 
@@ -68,9 +67,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 // `CoerceUnsized` can be passed by a where-clause,
                 // so the (generic) MIR may not be able to expand it.
                 let operand = self.trans_operand(&bcx, source);
+                let operand = operand.pack_if_pair(&bcx);
                 bcx.with_block(|bcx| {
                     match operand.val {
-                        OperandValue::FatPtr(..) => bug!(),
+                        OperandValue::Pair(..) => bug!(),
                         OperandValue::Immediate(llval) => {
                             // unsize from an immediate structure. We don't
                             // really need a temporary alloca here, but
@@ -92,7 +92,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         }
                     }
                 });
-                self.set_operand_dropped(&bcx, source);
                 bcx
             }
 
@@ -107,7 +106,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         block
                     })
                 });
-                self.set_operand_dropped(&bcx, elem);
                 bcx
             }
 
@@ -128,7 +126,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                                                             val, disr, i);
                                 self.store_operand(&bcx, lldest_i, op);
                             }
-                            self.set_operand_dropped(&bcx, operand);
                         }
                     },
                     _ => {
@@ -136,8 +133,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         if let mir::AggregateKind::Closure(def_id, substs) = *kind {
                             use rustc::hir;
                             use syntax::ast::DUMMY_NODE_ID;
-                            use syntax::codemap::DUMMY_SP;
                             use syntax::ptr::P;
+                            use syntax_pos::DUMMY_SP;
                             use closure;
 
                             closure::trans_closure_expr(closure::Dest::Ignore(bcx.ccx()),
@@ -167,33 +164,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                 let dest = bcx.gepi(dest.llval, &[0, i]);
                                 self.store_operand(&bcx, dest, op);
                             }
-                            self.set_operand_dropped(&bcx, operand);
                         }
                     }
                 }
                 bcx
             }
 
-            mir::Rvalue::Slice { ref input, from_start, from_end } => {
-                let ccx = bcx.ccx();
-                let input = self.trans_lvalue(&bcx, input);
-                let ty = input.ty.to_ty(bcx.tcx());
-                let (llbase1, lllen) = match ty.sty {
-                    ty::TyArray(_, n) => {
-                        (bcx.gepi(input.llval, &[0, from_start]), C_uint(ccx, n))
-                    }
-                    ty::TySlice(_) | ty::TyStr => {
-                        (bcx.gepi(input.llval, &[from_start]), input.llextra)
-                    }
-                    _ => bug!("cannot slice {}", ty)
-                };
-                let adj = C_uint(ccx, from_start + from_end);
-                let lllen1 = bcx.sub(lllen, adj);
-                bcx.store(llbase1, get_dataptr(&bcx, dest.llval));
-                bcx.store(lllen1, get_meta(&bcx, dest.llval));
-                bcx
-            }
-
             mir::Rvalue::InlineAsm { ref asm, ref outputs, ref inputs } => {
                 let outputs = outputs.iter().map(|output| {
                     let lvalue = self.trans_lvalue(&bcx, output);
@@ -209,9 +185,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     asm::trans_inline_asm(bcx, asm, outputs, input_vals);
                 });
 
-                for input in inputs {
-                    self.set_operand_dropped(&bcx, input);
-                }
                 bcx
             }
 
@@ -262,17 +235,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                         assert!(common::type_is_fat_ptr(bcx.tcx(), cast_ty));
 
                         match operand.val {
-                            OperandValue::FatPtr(lldata, llextra) => {
+                            OperandValue::Pair(lldata, llextra) => {
                                 // unsize from a fat pointer - this is a
                                 // "trait-object-to-supertrait" coercion, for
                                 // example,
                                 //   &'a fmt::Debug+Send => &'a fmt::Debug,
                                 // So we need to pointercast the base to ensure
                                 // the types match up.
-                                self.set_operand_dropped(&bcx, source);
                                 let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx(), cast_ty);
                                 let lldata = bcx.pointercast(lldata, llcast_ty);
-                                OperandValue::FatPtr(lldata, llextra)
+                                OperandValue::Pair(lldata, llextra)
                             }
                             OperandValue::Immediate(lldata) => {
                                 // "standard" unsize
@@ -280,8 +252,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                     base::unsize_thin_ptr(bcx, lldata,
                                                           operand.ty, cast_ty)
                                 });
-                                self.set_operand_dropped(&bcx, source);
-                                OperandValue::FatPtr(lldata, llextra)
+                                OperandValue::Pair(lldata, llextra)
                             }
                             OperandValue::Ref(_) => {
                                 bug!("by-ref operand {:?} in trans_rvalue_operand",
@@ -352,13 +323,13 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     mir::CastKind::Misc => { // Casts from a fat-ptr.
                         let ll_cast_ty = type_of::immediate_type_of(bcx.ccx(), cast_ty);
                         let ll_from_ty = type_of::immediate_type_of(bcx.ccx(), operand.ty);
-                        if let OperandValue::FatPtr(data_ptr, meta_ptr) = operand.val {
+                        if let OperandValue::Pair(data_ptr, meta_ptr) = operand.val {
                             if common::type_is_fat_ptr(bcx.tcx(), cast_ty) {
                                 let ll_cft = ll_cast_ty.field_types();
                                 let ll_fft = ll_from_ty.field_types();
                                 let data_cast = bcx.pointercast(data_ptr, ll_cft[0]);
                                 assert_eq!(ll_cft[1].kind(), ll_fft[1].kind());
-                                OperandValue::FatPtr(data_cast, meta_ptr)
+                                OperandValue::Pair(data_cast, meta_ptr)
                             } else { // cast to thin-ptr
                                 // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and
                                 // pointer-cast of that pointer to desired pointer type.
@@ -366,7 +337,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                 OperandValue::Immediate(llval)
                             }
                         } else {
-                            bug!("Unexpected non-FatPtr operand")
+                            bug!("Unexpected non-Pair operand")
                         }
                     }
                 };
@@ -382,7 +353,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
                 let ty = tr_lvalue.ty.to_ty(bcx.tcx());
                 let ref_ty = bcx.tcx().mk_ref(
-                    bcx.tcx().mk_region(ty::ReStatic),
+                    bcx.tcx().mk_region(ty::ReErased),
                     ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() }
                 );
 
@@ -395,8 +366,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     }
                 } else {
                     OperandRef {
-                        val: OperandValue::FatPtr(tr_lvalue.llval,
-                                                  tr_lvalue.llextra),
+                        val: OperandValue::Pair(tr_lvalue.llval,
+                                                tr_lvalue.llextra),
                         ty: ref_ty,
                     }
                 };
@@ -417,8 +388,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let rhs = self.trans_operand(&bcx, rhs);
                 let llresult = if common::type_is_fat_ptr(bcx.tcx(), lhs.ty) {
                     match (lhs.val, rhs.val) {
-                        (OperandValue::FatPtr(lhs_addr, lhs_extra),
-                         OperandValue::FatPtr(rhs_addr, rhs_extra)) => {
+                        (OperandValue::Pair(lhs_addr, lhs_extra),
+                         OperandValue::Pair(rhs_addr, rhs_extra)) => {
                             bcx.with_block(|bcx| {
                                 base::compare_fat_ptrs(bcx,
                                                        lhs_addr, lhs_extra,
@@ -441,6 +412,21 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 };
                 (bcx, operand)
             }
+            mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
+                let lhs = self.trans_operand(&bcx, lhs);
+                let rhs = self.trans_operand(&bcx, rhs);
+                let result = self.trans_scalar_checked_binop(&bcx, op,
+                                                             lhs.immediate(), rhs.immediate(),
+                                                             lhs.ty);
+                let val_ty = self.mir.binop_ty(bcx.tcx(), op, lhs.ty, rhs.ty);
+                let operand_ty = bcx.tcx().mk_tup(vec![val_ty, bcx.tcx().types.bool]);
+                let operand = OperandRef {
+                    val: result,
+                    ty: operand_ty
+                };
+
+                (bcx, operand)
+            }
 
             mir::Rvalue::UnaryOp(op, ref operand) => {
                 let operand = self.trans_operand(&bcx, operand);
@@ -492,7 +478,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             }
             mir::Rvalue::Repeat(..) |
             mir::Rvalue::Aggregate(..) |
-            mir::Rvalue::Slice { .. } |
             mir::Rvalue::InlineAsm { .. } => {
                 bug!("cannot generate operand from rvalue {:?}", rvalue);
 
@@ -567,33 +552,157 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             }
         }
     }
+
+    pub fn trans_scalar_checked_binop(&mut self,
+                                      bcx: &BlockAndBuilder<'bcx, 'tcx>,
+                                      op: mir::BinOp,
+                                      lhs: ValueRef,
+                                      rhs: ValueRef,
+                                      input_ty: Ty<'tcx>) -> OperandValue {
+        // This case can currently arise only from functions marked
+        // with #[rustc_inherit_overflow_checks] and inlined from
+        // another crate (mostly core::num generic/#[inline] fns),
+        // while the current crate doesn't use overflow checks.
+        if !bcx.ccx().check_overflow() {
+            let val = self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty);
+            return OperandValue::Pair(val, C_bool(bcx.ccx(), false));
+        }
+
+        // First try performing the operation on constants, which
+        // will only succeed if both operands are constant.
+        // This is necessary to determine when an overflow Assert
+        // will always panic at runtime, and produce a warning.
+        if let Some((val, of)) = const_scalar_checked_binop(bcx.tcx(), op, lhs, rhs, input_ty) {
+            return OperandValue::Pair(val, C_bool(bcx.ccx(), of));
+        }
+
+        let (val, of) = match op {
+            // These are checked using intrinsics
+            mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => {
+                let oop = match op {
+                    mir::BinOp::Add => OverflowOp::Add,
+                    mir::BinOp::Sub => OverflowOp::Sub,
+                    mir::BinOp::Mul => OverflowOp::Mul,
+                    _ => unreachable!()
+                };
+                let intrinsic = get_overflow_intrinsic(oop, bcx, input_ty);
+                let res = bcx.call(intrinsic, &[lhs, rhs], None);
+
+                (bcx.extract_value(res, 0),
+                 bcx.extract_value(res, 1))
+            }
+            mir::BinOp::Shl | mir::BinOp::Shr => {
+                let lhs_llty = val_ty(lhs);
+                let rhs_llty = val_ty(rhs);
+                let invert_mask = bcx.with_block(|bcx| {
+                    common::shift_mask_val(bcx, lhs_llty, rhs_llty, true)
+                });
+                let outer_bits = bcx.and(rhs, invert_mask);
+
+                let of = bcx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty));
+                let val = self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty);
+
+                (val, of)
+            }
+            _ => {
+                bug!("Operator `{:?}` is not a checkable operator", op)
+            }
+        };
+
+        OperandValue::Pair(val, of)
+    }
 }
 
-pub fn rvalue_creates_operand<'bcx, 'tcx>(mir: &mir::Mir<'tcx>,
-                                          bcx: &BlockAndBuilder<'bcx, 'tcx>,
+pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>,
+                                          _bcx: &BlockAndBuilder<'bcx, 'tcx>,
                                           rvalue: &mir::Rvalue<'tcx>) -> bool {
     match *rvalue {
         mir::Rvalue::Ref(..) |
         mir::Rvalue::Len(..) |
         mir::Rvalue::Cast(..) | // (*)
         mir::Rvalue::BinaryOp(..) |
+        mir::Rvalue::CheckedBinaryOp(..) |
         mir::Rvalue::UnaryOp(..) |
-        mir::Rvalue::Box(..) =>
+        mir::Rvalue::Box(..) |
+        mir::Rvalue::Use(..) =>
             true,
         mir::Rvalue::Repeat(..) |
         mir::Rvalue::Aggregate(..) |
-        mir::Rvalue::Slice { .. } |
         mir::Rvalue::InlineAsm { .. } =>
             false,
-        mir::Rvalue::Use(ref operand) => {
-            let ty = mir.operand_ty(bcx.tcx(), operand);
-            let ty = bcx.monomorphize(&ty);
-            // Types that don't need dropping can just be an operand,
-            // this allows temporary lvalues, used as rvalues, to
-            // avoid a stack slot when it's unnecessary
-            !glue::type_needs_drop(bcx.tcx(), ty)
-        }
     }
 
     // (*) this is only true if the type is suitable
 }
+
+#[derive(Copy, Clone)]
+enum OverflowOp {
+    Add, Sub, Mul
+}
+
+fn get_overflow_intrinsic(oop: OverflowOp, bcx: &BlockAndBuilder, ty: Ty) -> ValueRef {
+    use syntax::ast::IntTy::*;
+    use syntax::ast::UintTy::*;
+    use rustc::ty::{TyInt, TyUint};
+
+    let tcx = bcx.tcx();
+
+    let new_sty = match ty.sty {
+        TyInt(Is) => match &tcx.sess.target.target.target_pointer_width[..] {
+            "32" => TyInt(I32),
+            "64" => TyInt(I64),
+            _ => panic!("unsupported target word size")
+        },
+        TyUint(Us) => match &tcx.sess.target.target.target_pointer_width[..] {
+            "32" => TyUint(U32),
+            "64" => TyUint(U64),
+            _ => panic!("unsupported target word size")
+        },
+        ref t @ TyUint(_) | ref t @ TyInt(_) => t.clone(),
+        _ => panic!("tried to get overflow intrinsic for op applied to non-int type")
+    };
+
+    let name = match oop {
+        OverflowOp::Add => match new_sty {
+            TyInt(I8) => "llvm.sadd.with.overflow.i8",
+            TyInt(I16) => "llvm.sadd.with.overflow.i16",
+            TyInt(I32) => "llvm.sadd.with.overflow.i32",
+            TyInt(I64) => "llvm.sadd.with.overflow.i64",
+
+            TyUint(U8) => "llvm.uadd.with.overflow.i8",
+            TyUint(U16) => "llvm.uadd.with.overflow.i16",
+            TyUint(U32) => "llvm.uadd.with.overflow.i32",
+            TyUint(U64) => "llvm.uadd.with.overflow.i64",
+
+            _ => unreachable!(),
+        },
+        OverflowOp::Sub => match new_sty {
+            TyInt(I8) => "llvm.ssub.with.overflow.i8",
+            TyInt(I16) => "llvm.ssub.with.overflow.i16",
+            TyInt(I32) => "llvm.ssub.with.overflow.i32",
+            TyInt(I64) => "llvm.ssub.with.overflow.i64",
+
+            TyUint(U8) => "llvm.usub.with.overflow.i8",
+            TyUint(U16) => "llvm.usub.with.overflow.i16",
+            TyUint(U32) => "llvm.usub.with.overflow.i32",
+            TyUint(U64) => "llvm.usub.with.overflow.i64",
+
+            _ => unreachable!(),
+        },
+        OverflowOp::Mul => match new_sty {
+            TyInt(I8) => "llvm.smul.with.overflow.i8",
+            TyInt(I16) => "llvm.smul.with.overflow.i16",
+            TyInt(I32) => "llvm.smul.with.overflow.i32",
+            TyInt(I64) => "llvm.smul.with.overflow.i64",
+
+            TyUint(U8) => "llvm.umul.with.overflow.i8",
+            TyUint(U16) => "llvm.umul.with.overflow.i16",
+            TyUint(U32) => "llvm.umul.with.overflow.i32",
+            TyUint(U64) => "llvm.umul.with.overflow.i64",
+
+            _ => unreachable!(),
+        },
+    };
+
+    bcx.ccx().get_intrinsic(&name)
+}
index c9a4e540fa06b92948239af8c0bb3eef96351a99..55efa75b17336aafdea56e516b496388d19a5a36 100644 (file)
@@ -9,11 +9,11 @@
 // except according to those terms.
 
 use rustc::mir::repr as mir;
+
 use common::{self, BlockAndBuilder};
-use debuginfo::DebugLoc;
 
 use super::MirContext;
-use super::TempRef;
+use super::LocalRef;
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_statement(&mut self,
@@ -22,45 +22,39 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                            -> BlockAndBuilder<'bcx, 'tcx> {
         debug!("trans_statement(statement={:?})", statement);
 
-        let debug_loc = DebugLoc::ScopeAt(self.scopes[statement.scope.index()],
-                                          statement.span);
+        let debug_loc = self.debug_loc(statement.source_info);
         debug_loc.apply_to_bcx(&bcx);
         debug_loc.apply(bcx.fcx());
         match statement.kind {
             mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
-                match *lvalue {
-                    mir::Lvalue::Temp(index) => {
-                        let index = index as usize;
-                        match self.temps[index as usize] {
-                            TempRef::Lvalue(tr_dest) => {
-                                self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
-                            }
-                            TempRef::Operand(None) => {
-                                let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue,
-                                                                               debug_loc);
-                                self.temps[index] = TempRef::Operand(Some(operand));
-                                bcx
-                            }
-                            TempRef::Operand(Some(_)) => {
-                                let ty = self.mir.lvalue_ty(bcx.tcx(), lvalue);
-                                let ty = bcx.monomorphize(&ty.to_ty(bcx.tcx()));
+                if let Some(index) = self.mir.local_index(lvalue) {
+                    match self.locals[index] {
+                        LocalRef::Lvalue(tr_dest) => {
+                            self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
+                        }
+                        LocalRef::Operand(None) => {
+                            let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue,
+                                                                           debug_loc);
+                            self.locals[index] = LocalRef::Operand(Some(operand));
+                            bcx
+                        }
+                        LocalRef::Operand(Some(_)) => {
+                            let ty = self.lvalue_ty(lvalue);
 
-                                if !common::type_is_zero_size(bcx.ccx(), ty) {
-                                    span_bug!(statement.span,
-                                              "operand {:?} already assigned",
-                                              rvalue);
-                                } else {
-                                    // If the type is zero-sized, it's already been set here,
-                                    // but we still need to make sure we translate the operand
-                                    self.trans_rvalue_operand(bcx, rvalue, debug_loc).0
-                                }
+                            if !common::type_is_zero_size(bcx.ccx(), ty) {
+                                span_bug!(statement.source_info.span,
+                                          "operand {:?} already assigned",
+                                          rvalue);
+                            } else {
+                                // If the type is zero-sized, it's already been set here,
+                                // but we still need to make sure we translate the operand
+                                self.trans_rvalue_operand(bcx, rvalue, debug_loc).0
                             }
                         }
                     }
-                    _ => {
-                        let tr_dest = self.trans_lvalue(&bcx, lvalue);
-                        self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
-                    }
+                } else {
+                    let tr_dest = self.trans_lvalue(&bcx, lvalue);
+                    self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
                 }
             }
         }
index dfaf84ecef02397faabed4ace6926b0140c51f97..ab859b88a85972d55200ba32260199974fa94422 100644 (file)
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use back::symbol_names;
 use llvm::ValueRef;
 use llvm;
 use rustc::hir::def_id::DefId;
@@ -29,7 +28,7 @@ use rustc::util::ppaux;
 use rustc::hir;
 
 use syntax::attr;
-use syntax::errors;
+use errors;
 
 use std::fmt;
 
@@ -51,12 +50,9 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     let mono_ty = apply_param_substs(ccx.tcx(), psubsts, &item_ty);
     debug!("mono_ty = {:?} (post-substitution)", mono_ty);
 
-    match ccx.instances().borrow().get(&instance) {
-        Some(&val) => {
-            debug!("leaving monomorphic fn {:?}", instance);
-            return (val, mono_ty);
-        }
-        None => ()
+    if let Some(&val) = ccx.instances().borrow().get(&instance) {
+        debug!("leaving monomorphic fn {:?}", instance);
+        return (val, mono_ty);
     }
 
     debug!("monomorphic_fn({:?})", instance);
@@ -88,7 +84,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         monomorphizing.insert(fn_id, depth + 1);
     }
 
-    let symbol = symbol_names::exported_name(ccx, &instance);
+    let symbol = instance.symbol_name(ccx.shared());
 
     debug!("monomorphize_fn mangled to {}", symbol);
     assert!(declare::get_defined_value(ccx, &symbol).is_none());
@@ -152,6 +148,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                 _ => bug!()
             };
             attributes::inline(lldecl, attributes::InlineAttr::Hint);
+            attributes::set_frame_pointer_elimination(ccx, lldecl);
             base::trans_ctor_shim(ccx, fn_node_id, disr, psubsts, lldecl);
         }
 
@@ -172,15 +169,14 @@ pub struct Instance<'tcx> {
 
 impl<'tcx> fmt::Display for Instance<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        ppaux::parameterized(f, &self.substs, self.def, ppaux::Ns::Value, &[],
-                             |tcx| tcx.lookup_item_type(self.def).generics)
+        ppaux::parameterized(f, &self.substs, self.def, ppaux::Ns::Value, &[], |_| None)
     }
 }
 
 impl<'tcx> Instance<'tcx> {
     pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>)
                -> Instance<'tcx> {
-        assert!(substs.regions.iter().all(|&r| r == ty::ReStatic));
+        assert!(substs.regions.iter().all(|&r| r == ty::ReErased));
         Instance { def: def_id, substs: substs }
     }
     pub fn mono<'a>(scx: &SharedCrateContext<'a, 'tcx>, def_id: DefId) -> Instance<'tcx> {
index 098ba759247be44d8a4b128cc290921f7e067994..2ded643ef4fdd309da56c954925557c879a8f2b1 100644 (file)
@@ -228,6 +228,17 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
     }
 
+    // always ensure we have at least one CGU; otherwise, if we have a
+    // crate with just types (for example), we could wind up with no CGU
+    if codegen_units.is_empty() {
+        let codegen_unit_name = InternedString::new(FALLBACK_CODEGEN_UNIT);
+        codegen_units.entry(codegen_unit_name.clone())
+                     .or_insert_with(|| CodegenUnit {
+                         name: codegen_unit_name.clone(),
+                         items: FnvHashMap(),
+                     });
+    }
+
     PreInliningPartitioning {
         codegen_units: codegen_units.into_iter()
                                     .map(|(_, codegen_unit)| codegen_unit)
index 284a227276dd0f2b2b1172bd169199b2f86e4f2b..11e9e9f3204a28a3b9e0860e3434596a9dbfcf61 100644 (file)
@@ -14,7 +14,6 @@
 //! item-path. This is used for unit testing the code that generates
 //! paths etc in all kinds of annoying scenarios.
 
-use back::symbol_names;
 use rustc::hir;
 use rustc::hir::intravisit::{self, Visitor};
 use syntax::ast;
@@ -53,7 +52,7 @@ impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> {
             if attr.check_name(SYMBOL_NAME) {
                 // for now, can only use on monomorphic names
                 let instance = Instance::mono(self.ccx.shared(), def_id);
-                let name = symbol_names::exported_name(self.ccx, &instance);
+                let name = instance.symbol_name(self.ccx.shared());
                 tcx.sess.span_err(attr.span, &format!("symbol-name({})", name));
             } else if attr.check_name(ITEM_PATH) {
                 let path = tcx.item_path_str(def_id);
index 35a60cd5422b40ac0dcc5be1a1aa6876c0bbbb89..001cd197e60b8a8df178068723062dc82b51c0df 100644 (file)
@@ -122,6 +122,7 @@ impl Type {
 
     pub fn int(ccx: &CrateContext) -> Type {
         match &ccx.tcx().sess.target.target.target_pointer_width[..] {
+            "16" => Type::i16(ccx),
             "32" => Type::i32(ccx),
             "64" => Type::i64(ccx),
             tws => bug!("Unsupported target word size for int: {}", tws),
index a0c4c7534fab27b78c7c69ac3c67c2fa9b1a487e..720423371a83a814324e1694a97aca276426940f 100644 (file)
@@ -19,3 +19,5 @@ rustc_back = { path = "../librustc_back" }
 rustc_const_eval = { path = "../librustc_const_eval" }
 rustc_const_math = { path = "../librustc_const_math" }
 rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" }
+syntax_pos = { path = "../libsyntax_pos" }
+rustc_errors = { path = "../librustc_errors" }
index 1df12b63e0a50e76a12f3fc9beda37f649bdde03..088ac1aac1a4016926e9507b8790a43d6e3eb335 100644 (file)
@@ -73,10 +73,10 @@ use util::nodemap::{NodeMap, FnvHashSet};
 use rustc_const_math::ConstInt;
 use std::cell::RefCell;
 use syntax::{abi, ast};
-use syntax::codemap::{Span, Pos};
-use syntax::errors::DiagnosticBuilder;
 use syntax::feature_gate::{GateIssue, emit_feature_err};
 use syntax::parse::token::{self, keywords};
+use syntax_pos::{Span, Pos};
+use errors::DiagnosticBuilder;
 
 pub trait AstConv<'gcx, 'tcx> {
     fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>;
@@ -170,7 +170,7 @@ type TraitAndProjections<'tcx> = (ty::PolyTraitRef<'tcx>, Vec<ty::PolyProjection
 
 pub fn ast_region_to_region(tcx: TyCtxt, lifetime: &hir::Lifetime)
                             -> ty::Region {
-    let r = match tcx.named_region_map.get(&lifetime.id) {
+    let r = match tcx.named_region_map.defs.get(&lifetime.id) {
         None => {
             // should have been recorded by the `resolve_lifetime` pass
             span_bug!(lifetime.span, "unresolved lifetime");
@@ -181,7 +181,20 @@ pub fn ast_region_to_region(tcx: TyCtxt, lifetime: &hir::Lifetime)
         }
 
         Some(&rl::DefLateBoundRegion(debruijn, id)) => {
-            ty::ReLateBound(debruijn, ty::BrNamed(tcx.map.local_def_id(id), lifetime.name))
+            // If this region is declared on a function, it will have
+            // an entry in `late_bound`, but if it comes from
+            // `for<'a>` in some type or something, it won't
+            // necessarily have one. In that case though, we won't be
+            // changed from late to early bound, so we can just
+            // substitute false.
+            let issue_32330 = tcx.named_region_map
+                                 .late_bound
+                                 .get(&id)
+                                 .cloned()
+                                 .unwrap_or(ty::Issue32330::WontChange);
+            ty::ReLateBound(debruijn, ty::BrNamed(tcx.map.local_def_id(id),
+                                                  lifetime.name,
+                                                  issue_32330))
         }
 
         Some(&rl::DefEarlyBoundRegion(space, index, _)) => {
@@ -193,11 +206,21 @@ pub fn ast_region_to_region(tcx: TyCtxt, lifetime: &hir::Lifetime)
         }
 
         Some(&rl::DefFreeRegion(scope, id)) => {
+            // As in DefLateBoundRegion above, could be missing for some late-bound
+            // regions, but also for early-bound regions.
+            let issue_32330 = tcx.named_region_map
+                                 .late_bound
+                                 .get(&id)
+                                 .cloned()
+                                 .unwrap_or(ty::Issue32330::WontChange);
             ty::ReFree(ty::FreeRegion {
                     scope: scope.to_code_extent(&tcx.region_maps),
                     bound_region: ty::BrNamed(tcx.map.local_def_id(id),
-                                              lifetime.name)
-                })
+                                              lifetime.name,
+                                              issue_32330)
+            })
+
+                // (*) -- not late-bound, won't change
         }
     };
 
@@ -711,7 +734,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
 
     fn trait_def_id(&self, trait_ref: &hir::TraitRef) -> DefId {
         let path = &trait_ref.path;
-        match ::lookup_full_def(self.tcx(), path.span, trait_ref.ref_id) {
+        match self.tcx().expect_def(trait_ref.ref_id) {
             Def::Trait(trait_def_id) => trait_def_id,
             Def::Err => {
                 self.tcx().sess.fatal("cannot continue compilation due to previous error");
@@ -911,7 +934,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
         debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
         for br in late_bound_in_ty.difference(&late_bound_in_trait_ref) {
             let br_name = match *br {
-                ty::BrNamed(_, name) => name,
+                ty::BrNamed(_, name, _) => name,
                 _ => {
                     span_bug!(
                         binding.span,
@@ -1041,12 +1064,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
 
         match ty.node {
             hir::TyPath(None, ref path) => {
-                let def = match self.tcx().def_map.borrow().get(&ty.id) {
-                    Some(&def::PathResolution { base_def, depth: 0, .. }) => Some(base_def),
-                    _ => None
-                };
-                match def {
-                    Some(Def::Trait(trait_def_id)) => {
+                let resolution = self.tcx().expect_resolution(ty.id);
+                match resolution.base_def {
+                    Def::Trait(trait_def_id) if resolution.depth == 0 => {
                         let mut projection_bounds = Vec::new();
                         let trait_ref =
                             self.object_path_to_poly_trait_ref(rscope,
@@ -1675,7 +1695,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                 let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
                 for br in late_bound_in_ret.difference(&late_bound_in_args) {
                     let br_name = match *br {
-                        ty::BrNamed(_, name) => name,
+                        ty::BrNamed(_, name, _) => name,
                         _ => {
                             span_bug!(
                                 bf.decl.output.span(),
@@ -1698,17 +1718,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
             }
             hir::TyPath(ref maybe_qself, ref path) => {
                 debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path);
-                let path_res = if let Some(&d) = tcx.def_map.borrow().get(&ast_ty.id) {
-                    d
-                } else if let Some(hir::QSelf { position: 0, .. }) = *maybe_qself {
-                    // Create some fake resolution that can't possibly be a type.
-                    def::PathResolution {
-                        base_def: Def::Mod(tcx.map.local_def_id(ast::CRATE_NODE_ID)),
-                        depth: path.segments.len()
-                    }
-                } else {
-                    span_bug!(ast_ty.span, "unbound path {:?}", ast_ty)
-                };
+                let path_res = tcx.expect_resolution(ast_ty.id);
                 let def = path_res.base_def;
                 let base_ty_end = path.segments.len() - path_res.depth;
                 let opt_self_ty = maybe_qself.as_ref().map(|qself| {
@@ -1725,10 +1735,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
 
                 if path_res.depth != 0 && ty.sty != ty::TyError {
                     // Write back the new resolution.
-                    tcx.def_map.borrow_mut().insert(ast_ty.id, def::PathResolution {
-                        base_def: def,
-                        depth: 0
-                    });
+                    tcx.def_map.borrow_mut().insert(ast_ty.id, def::PathResolution::new(def));
                 }
 
                 ty
@@ -1833,8 +1840,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
         // lifetime elision, we can determine it in two ways. First (determined
         // here), if self is by-reference, then the implied output region is the
         // region of the self parameter.
-        let explicit_self = decl.inputs.get(0).and_then(hir::Arg::to_self);
-        let (self_ty, explicit_self_category) = match (opt_untransformed_self_ty, explicit_self) {
+        let (self_ty, explicit_self_category) = match (opt_untransformed_self_ty, decl.get_self()) {
             (Some(untransformed_self_ty), Some(explicit_self)) => {
                 let self_type = self.determine_self_type(&rb, untransformed_self_ty,
                                                          &explicit_self);
@@ -2210,7 +2216,7 @@ pub fn partition_bounds<'a, 'b, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
     for ast_bound in ast_bounds {
         match *ast_bound {
             hir::TraitTyParamBound(ref b, hir::TraitBoundModifier::None) => {
-                match ::lookup_full_def(tcx, b.trait_ref.path.span, b.trait_ref.ref_id) {
+                match tcx.expect_def(b.trait_ref.ref_id) {
                     Def::Trait(trait_did) => {
                         if tcx.try_add_builtin_trait(trait_did,
                                                      &mut builtin_bounds) {
index 10c8ea84bfd64e3e009eb348d329331c34340fdc..6d27021832caad7df70627213611ce73ea3bd170 100644 (file)
@@ -8,10 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use hir::def::{self, Def};
+use hir::def::Def;
 use rustc::infer::{self, InferOk, TypeOrigin};
-use hir::pat_util::{PatIdMap, pat_id_map, pat_is_binding};
-use hir::pat_util::pat_is_resolved_const;
+use hir::pat_util::{EnumerateAndAdjustIterator, pat_is_resolved_const};
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
 use check::{FnCtxt, Expectation};
@@ -19,28 +18,16 @@ use lint;
 use util::nodemap::FnvHashMap;
 use session::Session;
 
-use std::cmp;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
-use std::ops::Deref;
+use std::cmp;
 use syntax::ast;
-use syntax::codemap::{Span, Spanned};
+use syntax::codemap::Spanned;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 use rustc::hir::{self, PatKind};
 use rustc::hir::print as pprust;
 
-pub struct PatCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
-    pub fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
-    pub map: PatIdMap,
-}
-
-impl<'a, 'gcx, 'tcx> Deref for PatCtxt<'a, 'gcx, 'tcx> {
-    type Target = FnCtxt<'a, 'gcx, 'tcx>;
-    fn deref(&self) -> &Self::Target {
-        self.fcx
-    }
-}
-
 // This function exists due to the warning "diagnostic code E0164 already used"
 fn bad_struct_kind_err(sess: &Session, pat: &hir::Pat, path: &hir::Path, lint: bool) {
     let name = pprust::path_to_string(path);
@@ -55,7 +42,7 @@ fn bad_struct_kind_err(sess: &Session, pat: &hir::Pat, path: &hir::Path, lint: b
     }
 }
 
-impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
+impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn check_pat(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>) {
         let tcx = self.tcx;
 
@@ -149,29 +136,23 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
                 // subtyping doesn't matter here, as the value is some kind of scalar
                 self.demand_eqtype(pat.span, expected, lhs_ty);
             }
-            PatKind::Path(..) | PatKind::Ident(..)
-                    if pat_is_resolved_const(&tcx.def_map.borrow(), pat) => {
-                if let Some(pat_def) = tcx.def_map.borrow().get(&pat.id) {
-                    let const_did = pat_def.def_id();
-                    let const_scheme = tcx.lookup_item_type(const_did);
-                    assert!(const_scheme.generics.is_empty());
-                    let const_ty = self.instantiate_type_scheme(pat.span,
-                                                                &Substs::empty(),
-                                                                &const_scheme.ty);
-                    self.write_ty(pat.id, const_ty);
-
-                    // FIXME(#20489) -- we should limit the types here to scalars or something!
-
-                    // As with PatKind::Lit, what we really want here is that there
-                    // exist a LUB, but for the cases that can occur, subtype
-                    // is good enough.
-                    self.demand_suptype(pat.span, expected, const_ty);
-                } else {
-                    self.write_error(pat.id);
-                }
+            PatKind::Path(..) if pat_is_resolved_const(&tcx.def_map.borrow(), pat) => {
+                let const_did = tcx.expect_def(pat.id).def_id();
+                let const_scheme = tcx.lookup_item_type(const_did);
+                assert!(const_scheme.generics.is_empty());
+                let const_ty = self.instantiate_type_scheme(pat.span,
+                                                            &Substs::empty(),
+                                                            &const_scheme.ty);
+                self.write_ty(pat.id, const_ty);
+
+                // FIXME(#20489) -- we should limit the types here to scalars or something!
+
+                // As with PatKind::Lit, what we really want here is that there
+                // exist a LUB, but for the cases that can occur, subtype
+                // is good enough.
+                self.demand_suptype(pat.span, expected, const_ty);
             }
-            PatKind::Ident(bm, ref path, ref sub)
-                    if pat_is_binding(&tcx.def_map.borrow(), pat) => {
+            PatKind::Binding(bm, _, ref sub) => {
                 let typ = self.local_ty(pat.span, pat.id);
                 match bm {
                     hir::BindByRef(mutbl) => {
@@ -200,48 +181,35 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
 
                 // if there are multiple arms, make sure they all agree on
                 // what the type of the binding `x` ought to be
-                if let Some(&canon_id) = self.map.get(&path.node) {
-                    if canon_id != pat.id {
-                        let ct = self.local_ty(pat.span, canon_id);
-                        self.demand_eqtype(pat.span, ct, typ);
+                match tcx.expect_def(pat.id) {
+                    Def::Err => {}
+                    Def::Local(_, var_id) => {
+                        if var_id != pat.id {
+                            let vt = self.local_ty(pat.span, var_id);
+                            self.demand_eqtype(pat.span, vt, typ);
+                        }
                     }
+                    d => bug!("bad def for pattern binding `{:?}`", d)
+                }
 
-                    if let Some(ref p) = *sub {
-                        self.check_pat(&p, expected);
-                    }
+                if let Some(ref p) = *sub {
+                    self.check_pat(&p, expected);
                 }
             }
-            PatKind::Ident(_, ref path, _) => {
-                let path = hir::Path::from_name(path.span, path.node);
-                self.check_pat_enum(pat, &path, Some(&[]), expected, false);
-            }
-            PatKind::TupleStruct(ref path, ref subpats) => {
-                self.check_pat_enum(pat, path, subpats.as_ref().map(|v| &v[..]), expected, true);
+            PatKind::TupleStruct(ref path, ref subpats, ddpos) => {
+                self.check_pat_enum(pat, path, &subpats, ddpos, expected, true);
             }
             PatKind::Path(ref path) => {
-                self.check_pat_enum(pat, path, Some(&[]), expected, false);
+                self.check_pat_enum(pat, path, &[], None, expected, false);
             }
             PatKind::QPath(ref qself, ref path) => {
                 let self_ty = self.to_ty(&qself.ty);
-                let path_res = if let Some(&d) = tcx.def_map.borrow().get(&pat.id) {
-                    if d.base_def == Def::Err {
-                        self.set_tainted_by_errors();
-                        self.write_error(pat.id);
-                        return;
-                    }
-                    d
-                } else if qself.position == 0 {
-                    // This is just a sentinel for finish_resolving_def_to_ty.
-                    let sentinel = self.tcx.map.local_def_id(ast::CRATE_NODE_ID);
-                    def::PathResolution {
-                        base_def: Def::Mod(sentinel),
-                        depth: path.segments.len()
-                    }
-                } else {
-                    debug!("unbound path {:?}", pat);
+                let path_res = tcx.expect_resolution(pat.id);
+                if path_res.base_def == Def::Err {
+                    self.set_tainted_by_errors();
                     self.write_error(pat.id);
                     return;
-                };
+                }
                 if let Some((opt_ty, segments, def)) =
                         self.resolve_ty_and_def_ufcs(path_res, Some(self_ty),
                                                      path, pat.span, pat.id) {
@@ -260,14 +228,23 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
             PatKind::Struct(ref path, ref fields, etc) => {
                 self.check_pat_struct(pat, path, fields, etc, expected);
             }
-            PatKind::Tup(ref elements) => {
-                let element_tys: Vec<_> =
-                    (0..elements.len()).map(|_| self.next_ty_var()).collect();
+            PatKind::Tuple(ref elements, ddpos) => {
+                let mut expected_len = elements.len();
+                if ddpos.is_some() {
+                    // Require known type only when `..` is present
+                    if let ty::TyTuple(ref tys) =
+                            self.structurally_resolved_type(pat.span, expected).sty {
+                        expected_len = tys.len();
+                    }
+                }
+                let max_len = cmp::max(expected_len, elements.len());
+
+                let element_tys: Vec<_> = (0 .. max_len).map(|_| self.next_ty_var()).collect();
                 let pat_ty = tcx.mk_tup(element_tys.clone());
                 self.write_ty(pat.id, pat_ty);
                 self.demand_eqtype(pat.span, expected, pat_ty);
-                for (element_pat, element_ty) in elements.iter().zip(element_tys) {
-                    self.check_pat(&element_pat, element_ty);
+                for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
+                    self.check_pat(elem, &element_tys[i]);
                 }
             }
             PatKind::Box(ref inner) => {
@@ -320,44 +297,53 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
             }
             PatKind::Vec(ref before, ref slice, ref after) => {
                 let expected_ty = self.structurally_resolved_type(pat.span, expected);
-                let inner_ty = self.next_ty_var();
-                let pat_ty = match expected_ty.sty {
-                    ty::TyArray(_, size) => tcx.mk_array(inner_ty, {
+                let (inner_ty, slice_ty) = match expected_ty.sty {
+                    ty::TyArray(inner_ty, size) => {
                         let min_len = before.len() + after.len();
-                        match *slice {
-                            Some(_) => cmp::max(min_len, size),
-                            None => min_len
+                        if slice.is_none() {
+                            if min_len != size {
+                                span_err!(tcx.sess, pat.span, E0527,
+                                          "pattern requires {} elements but array has {}",
+                                          min_len, size);
+                            }
+                            (inner_ty, tcx.types.err)
+                        } else if let Some(rest) = size.checked_sub(min_len) {
+                            (inner_ty, tcx.mk_array(inner_ty, rest))
+                        } else {
+                            span_err!(tcx.sess, pat.span, E0528,
+                                      "pattern requires at least {} elements but array has {}",
+                                      min_len, size);
+                            (inner_ty, tcx.types.err)
                         }
-                    }),
+                    }
+                    ty::TySlice(inner_ty) => (inner_ty, expected_ty),
                     _ => {
-                        let region = self.next_region_var(infer::PatternRegion(pat.span));
-                        tcx.mk_ref(tcx.mk_region(region), ty::TypeAndMut {
-                            ty: tcx.mk_slice(inner_ty),
-                            mutbl: expected_ty.builtin_deref(true, ty::NoPreference)
-                                              .map_or(hir::MutImmutable, |mt| mt.mutbl)
-                        })
+                        if !expected_ty.references_error() {
+                            let mut err = struct_span_err!(
+                                tcx.sess, pat.span, E0529,
+                                "expected an array or slice, found `{}`",
+                                expected_ty);
+                            if let ty::TyRef(_, ty::TypeAndMut { mutbl: _, ty }) = expected_ty.sty {
+                                match ty.sty {
+                                    ty::TyArray(..) | ty::TySlice(..) => {
+                                        err.help("the semantics of slice patterns changed \
+                                                  recently; see issue #23121");
+                                    }
+                                    _ => {}
+                                }
+                            }
+                            err.emit();
+                        }
+                        (tcx.types.err, tcx.types.err)
                     }
                 };
 
-                self.write_ty(pat.id, pat_ty);
-
-                // `demand::subtype` would be good enough, but using
-                // `eqtype` turns out to be equally general. See (*)
-                // below for details.
-                self.demand_eqtype(pat.span, expected, pat_ty);
+                self.write_ty(pat.id, expected_ty);
 
                 for elt in before {
                     self.check_pat(&elt, inner_ty);
                 }
                 if let Some(ref slice) = *slice {
-                    let region = self.next_region_var(infer::PatternRegion(pat.span));
-                    let mutbl = expected_ty.builtin_deref(true, ty::NoPreference)
-                        .map_or(hir::MutImmutable, |mt| mt.mutbl);
-
-                    let slice_ty = tcx.mk_ref(tcx.mk_region(region), ty::TypeAndMut {
-                        ty: tcx.mk_slice(inner_ty),
-                        mutbl: mutbl
-                    });
                     self.check_pat(&slice, slice_ty);
                 }
                 for elt in after {
@@ -366,7 +352,6 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
             }
         }
 
-
         // (*) In most of the cases above (literals and constants being
         // the exception), we relate types using strict equality, evewn
         // though subtyping would be sufficient. There are a few reasons
@@ -433,23 +418,19 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
     }
 
     pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool {
-        let tcx = self.tcx;
-        if pat_is_binding(&tcx.def_map.borrow(), inner) {
-            let expected = self.shallow_resolve(expected);
-            expected.builtin_deref(true, ty::NoPreference).map_or(true, |mt| match mt.ty.sty {
-                ty::TyTrait(_) => {
+        if let PatKind::Binding(..) = inner.node {
+            if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true, ty::NoPreference) {
+                if let ty::TyTrait(..) = mt.ty.sty {
                     // This is "x = SomeTrait" being reduced from
                     // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
-                    span_err!(tcx.sess, span, E0033,
+                    span_err!(self.tcx.sess, span, E0033,
                               "type `{}` cannot be dereferenced",
                               self.ty_to_string(expected));
-                    false
+                    return false
                 }
-                _ => true
-            })
-        } else {
-            true
+            }
         }
+        true
     }
 }
 
@@ -486,12 +467,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // Typecheck the patterns first, so that we get types for all the
         // bindings.
         for arm in arms {
-            let pcx = PatCtxt {
-                fcx: self,
-                map: pat_id_map(&tcx.def_map, &arm.pats[0]),
-            };
             for p in &arm.pats {
-                pcx.check_pat(&p, discrim_ty);
+                self.check_pat(&p, discrim_ty);
             }
         }
 
@@ -576,13 +553,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     }
 }
 
-impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
+impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn check_pat_struct(&self, pat: &'gcx hir::Pat,
                             path: &hir::Path, fields: &'gcx [Spanned<hir::FieldPat>],
                             etc: bool, expected: Ty<'tcx>) {
         let tcx = self.tcx;
 
-        let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
+        let def = self.finish_resolving_struct_path(path, pat.id, path.span);
         let variant = match self.def_struct_variant(def, path.span) {
             Some((_, variant)) => variant,
             None => {
@@ -615,28 +592,24 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
     fn check_pat_enum(&self,
                       pat: &hir::Pat,
                       path: &hir::Path,
-                      subpats: Option<&'gcx [P<hir::Pat>]>,
+                      subpats: &'gcx [P<hir::Pat>],
+                      ddpos: Option<usize>,
                       expected: Ty<'tcx>,
                       is_tuple_struct_pat: bool)
     {
         // Typecheck the path.
         let tcx = self.tcx;
 
-        let path_res = match tcx.def_map.borrow().get(&pat.id) {
-            Some(&path_res) if path_res.base_def != Def::Err => path_res,
-            _ => {
-                self.set_tainted_by_errors();
-                self.write_error(pat.id);
-
-                if let Some(subpats) = subpats {
-                    for pat in subpats {
-                        self.check_pat(&pat, tcx.types.err);
-                    }
-                }
+        let path_res = tcx.expect_resolution(pat.id);
+        if path_res.base_def == Def::Err {
+            self.set_tainted_by_errors();
+            self.write_error(pat.id);
 
-                return;
+            for pat in subpats {
+                self.check_pat(&pat, tcx.types.err);
             }
-        };
+            return;
+        }
 
         let (opt_ty, segments, def) = match self.resolve_ty_and_def_ufcs(path_res,
                                                                          None, path,
@@ -670,15 +643,12 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
         };
         self.instantiate_path(segments, path_scheme, &ctor_predicates,
                               opt_ty, def, pat.span, pat.id);
-
         let report_bad_struct_kind = |is_warning| {
             bad_struct_kind_err(tcx.sess, pat, path, is_warning);
             if is_warning { return; }
             self.write_error(pat.id);
-            if let Some(subpats) = subpats {
-                for pat in subpats {
-                    self.check_pat(&pat, tcx.types.err);
-                }
+            for pat in subpats {
+                self.check_pat(&pat, tcx.types.err);
             }
         };
 
@@ -715,11 +685,13 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
         };
 
         match (is_tuple_struct_pat, variant.kind()) {
-            (true, ty::VariantKind::Unit) => {
+            (true, ty::VariantKind::Unit) if subpats.is_empty() && ddpos.is_some() => {
                 // Matching unit structs with tuple variant patterns (`UnitVariant(..)`)
                 // is allowed for backward compatibility.
                 report_bad_struct_kind(true);
             }
+            (true, ty::VariantKind::Unit) |
+            (false, ty::VariantKind::Tuple) |
             (_, ty::VariantKind::Struct) => {
                 report_bad_struct_kind(false);
                 return
@@ -727,30 +699,21 @@ impl<'a, 'gcx, 'tcx> PatCtxt<'a, 'gcx, 'tcx> {
             _ => {}
         }
 
-        if let Some(subpats) = subpats {
-            if subpats.len() == variant.fields.len() {
-                for (subpat, field) in subpats.iter().zip(&variant.fields) {
-                    let field_ty = self.field_ty(subpat.span, field, expected_substs);
-                    self.check_pat(&subpat, field_ty);
-                }
-            } else if variant.fields.is_empty() {
-                span_err!(tcx.sess, pat.span, E0024,
-                          "this pattern has {} field{}, but the corresponding {} has no fields",
-                          subpats.len(), if subpats.len() == 1 {""} else {"s"}, kind_name);
-
-                for pat in subpats {
-                    self.check_pat(&pat, tcx.types.err);
-                }
-            } else {
-                span_err!(tcx.sess, pat.span, E0023,
-                          "this pattern has {} field{}, but the corresponding {} has {} field{}",
-                          subpats.len(), if subpats.len() == 1 {""} else {"s"},
-                          kind_name,
-                          variant.fields.len(), if variant.fields.len() == 1 {""} else {"s"});
-
-                for pat in subpats {
-                    self.check_pat(&pat, tcx.types.err);
-                }
+        if subpats.len() == variant.fields.len() ||
+                subpats.len() < variant.fields.len() && ddpos.is_some() {
+            for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
+                let field_ty = self.field_ty(subpat.span, &variant.fields[i], expected_substs);
+                self.check_pat(&subpat, field_ty);
+            }
+        } else {
+            span_err!(tcx.sess, pat.span, E0023,
+                      "this pattern has {} field{}, but the corresponding {} has {} field{}",
+                      subpats.len(), if subpats.len() == 1 {""} else {"s"},
+                      kind_name,
+                      variant.fields.len(), if variant.fields.len() == 1 {""} else {"s"});
+
+            for pat in subpats {
+                self.check_pat(&pat, tcx.types.err);
             }
         }
     }
index 04b0248ccdac25ac2b4e8efce1b8dc0e0fda3b62..41f34b9040e832b3d580cba853fe9d575d2495eb 100644 (file)
@@ -13,7 +13,7 @@ use rustc::traits::{self, FulfillmentContext, Normalized, MiscObligation,
                      SelectionContext, ObligationCause};
 use rustc::ty::fold::TypeFoldable;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 //FIXME(@jroesch): Ideally we should be able to drop the fulfillment_cx argument.
 pub fn normalize_associated_types_in<'a, 'gcx, 'tcx, T>(
diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs
new file mode 100644 (file)
index 0000000..3c17674
--- /dev/null
@@ -0,0 +1,210 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use astconv::AstConv;
+
+use super::FnCtxt;
+
+use rustc::traits;
+use rustc::ty::{self, Ty, TraitRef};
+use rustc::ty::{ToPredicate, TypeFoldable};
+use rustc::ty::{MethodCall, MethodCallee};
+use rustc::ty::subst::Substs;
+use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
+use rustc::hir;
+
+use syntax_pos::Span;
+use syntax::parse::token;
+
+#[derive(Copy, Clone, Debug)]
+enum AutoderefKind {
+    Builtin,
+    Overloaded
+}
+
+pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
+    fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
+    steps: Vec<(Ty<'tcx>, AutoderefKind)>,
+    cur_ty: Ty<'tcx>,
+    obligations: Vec<traits::PredicateObligation<'tcx>>,
+    at_start: bool,
+    span: Span
+}
+
+impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
+    type Item = (Ty<'tcx>, usize);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let tcx = self.fcx.tcx;
+
+        debug!("autoderef: steps={:?}, cur_ty={:?}",
+               self.steps, self.cur_ty);
+        if self.at_start {
+            self.at_start = false;
+            debug!("autoderef stage #0 is {:?}", self.cur_ty);
+            return Some((self.cur_ty, 0));
+        }
+
+        if self.steps.len() == tcx.sess.recursion_limit.get() {
+            // We've reached the recursion limit, error gracefully.
+            span_err!(tcx.sess, self.span, E0055,
+                      "reached the recursion limit while auto-dereferencing {:?}",
+                      self.cur_ty);
+            return None;
+        }
+
+        if self.cur_ty.is_ty_var() {
+            return None;
+        }
+
+        // Otherwise, deref if type is derefable:
+        let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) {
+            (AutoderefKind::Builtin, mt.ty)
+        } else {
+            match self.overloaded_deref_ty(self.cur_ty) {
+                Some(ty) => (AutoderefKind::Overloaded, ty),
+                _ => return None
+            }
+        };
+
+        if new_ty.references_error() {
+            return None;
+        }
+
+        self.steps.push((self.cur_ty, kind));
+        debug!("autoderef stage #{:?} is {:?} from {:?}", self.steps.len(),
+               new_ty, (self.cur_ty, kind));
+        self.cur_ty = new_ty;
+
+        Some((self.cur_ty, self.steps.len()))
+    }
+}
+
+impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
+    fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+        debug!("overloaded_deref_ty({:?})", ty);
+
+        let tcx = self.fcx.tcx();
+
+        // <cur_ty as Deref>
+        let trait_ref = TraitRef {
+            def_id: match tcx.lang_items.deref_trait() {
+                Some(f) => f,
+                None => return None
+            },
+            substs: tcx.mk_substs(Substs::new_trait(vec![], vec![], self.cur_ty))
+        };
+
+        let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
+
+        let mut selcx = traits::SelectionContext::new(self.fcx);
+        let obligation = traits::Obligation::new(cause.clone(), trait_ref.to_predicate());
+        if !selcx.evaluate_obligation(&obligation) {
+            debug!("overloaded_deref_ty: cannot match obligation");
+            return None;
+        }
+
+        let normalized = traits::normalize_projection_type(
+            &mut selcx,
+            ty::ProjectionTy {
+                trait_ref: trait_ref,
+                item_name: token::intern("Target")
+            },
+            cause,
+            0
+        );
+
+        debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized);
+        self.obligations.extend(normalized.obligations);
+
+        Some(self.fcx.resolve_type_vars_if_possible(&normalized.value))
+    }
+
+    pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
+        self.fcx.structurally_resolved_type(self.span, self.cur_ty)
+    }
+
+    pub fn finalize<'b, I>(self, pref: LvaluePreference, exprs: I)
+        where I: IntoIterator<Item=&'b hir::Expr>
+    {
+        let methods : Vec<_> = self.steps.iter().map(|&(ty, kind)| {
+            if let AutoderefKind::Overloaded = kind {
+                self.fcx.try_overloaded_deref(self.span, None, ty, pref)
+            } else {
+                None
+            }
+        }).collect();
+
+        debug!("finalize({:?}) - {:?},{:?}", pref, methods, self.obligations);
+
+        for expr in exprs {
+            debug!("finalize - finalizing #{} - {:?}", expr.id, expr);
+            for (n, method) in methods.iter().enumerate() {
+                if let &Some(method) = method {
+                    let method_call = MethodCall::autoderef(expr.id, n as u32);
+                    self.fcx.tables.borrow_mut().method_map.insert(method_call, method);
+                }
+            }
+        }
+
+        for obligation in self.obligations {
+            self.fcx.register_predicate(obligation);
+        }
+    }
+}
+
+impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
+    pub fn autoderef(&'a self,
+                     span: Span,
+                     base_ty: Ty<'tcx>)
+                     -> Autoderef<'a, 'gcx, 'tcx>
+    {
+        Autoderef {
+            fcx: self,
+            steps: vec![],
+            cur_ty: self.resolve_type_vars_if_possible(&base_ty),
+            obligations: vec![],
+            at_start: true,
+            span: span
+        }
+    }
+
+    pub fn try_overloaded_deref(&self,
+                                span: Span,
+                                base_expr: Option<&hir::Expr>,
+                                base_ty: Ty<'tcx>,
+                                lvalue_pref: LvaluePreference)
+                                -> Option<MethodCallee<'tcx>>
+    {
+        debug!("try_overloaded_deref({:?},{:?},{:?},{:?})",
+               span, base_expr, base_ty, lvalue_pref);
+        // Try DerefMut first, if preferred.
+        let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) {
+            (PreferMutLvalue, Some(trait_did)) => {
+                self.lookup_method_in_trait(span, base_expr,
+                                            token::intern("deref_mut"), trait_did,
+                                            base_ty, None)
+            }
+            _ => None
+        };
+
+        // Otherwise, fall back to Deref.
+        let method = match (method, self.tcx.lang_items.deref_trait()) {
+            (None, Some(trait_did)) => {
+                self.lookup_method_in_trait(span, base_expr,
+                                            token::intern("deref"), trait_did,
+                                            base_ty, None)
+            }
+            (method, _) => method
+        };
+
+        method
+    }
+}
index 7493ca70f556711dda8017735426446b71f91fed..2c7e7d284fa160250de1ba5b9e7b71c37be092b7 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use super::{DeferredCallResolution, Expectation, FnCtxt,
-            TupleArgumentsFlag, UnresolvedTypeAction};
+            TupleArgumentsFlag};
 
 use CrateCtxt;
 use middle::cstore::LOCAL_CRATE;
@@ -17,9 +17,9 @@ use hir::def::Def;
 use hir::def_id::DefId;
 use rustc::infer;
 use rustc::ty::{self, LvaluePreference, Ty};
-use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 use rustc::hir;
 
@@ -72,15 +72,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     {
         self.check_expr(callee_expr);
         let original_callee_ty = self.expr_ty(callee_expr);
-        let (callee_ty, _, result) =
-            self.autoderef(callee_expr.span,
-                           original_callee_ty,
-                           || Some(callee_expr),
-                           UnresolvedTypeAction::Error,
-                           LvaluePreference::NoPreference,
-                           |adj_ty, idx| {
-                self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
-        });
+
+        let mut autoderef = self.autoderef(callee_expr.span, original_callee_ty);
+        let result = autoderef.by_ref().flat_map(|(adj_ty, idx)| {
+            self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
+        }).next();
+        let callee_ty = autoderef.unambiguous_final_ty();
+        autoderef.finalize(LvaluePreference::NoPreference, Some(callee_expr));
 
         match result {
             None => {
@@ -224,7 +222,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     let tcx = self.tcx;
                     if let Some(pr) = tcx.def_map.borrow().get(&expr.id) {
                         if pr.depth == 0 && pr.base_def != Def::Err {
-                            if let Some(span) = tcx.map.span_if_local(pr.def_id()) {
+                            if let Some(span) = tcx.map.span_if_local(pr.base_def.def_id()) {
                                 err.span_note(span, "defined here");
                             }
                         }
index 690250edb8cb457ea76f30ac66e56b0ae90437a1..22ac8bc56907bfd9578e94134ea0ea1ad3117c50 100644 (file)
@@ -47,7 +47,7 @@ use rustc::traits;
 use rustc::ty::{self, Ty, TypeFoldable};
 use rustc::ty::cast::{CastKind, CastTy};
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use util::common::ErrorReported;
 
 /// Reifies a cast check to be checked once we have full type information for
index 4861ab15e2c0147f1b39ccb9fdab5fb3ed38b6d8..9dd737f3a6168af9ae99a35b6e081089f7a8aaba 100644 (file)
@@ -60,7 +60,7 @@
 //! sort of a minor point so I've opted to leave it for later---after all
 //! we may want to adjust precisely when coercions occur.
 
-use check::{FnCtxt, UnresolvedTypeAction};
+use check::{FnCtxt};
 
 use rustc::hir;
 use rustc::infer::{Coercion, InferOk, TypeOrigin, TypeTrace};
@@ -220,7 +220,8 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                                          -> CoerceResult<'tcx>
         // FIXME(eddyb) use copyable iterators when that becomes ergonomic.
         where E: Fn() -> I,
-              I: IntoIterator<Item=&'a hir::Expr> {
+              I: IntoIterator<Item=&'a hir::Expr>
+    {
 
         debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
 
@@ -240,18 +241,16 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
 
         let span = self.origin.span();
 
-        let lvalue_pref = LvaluePreference::from_mutbl(mt_b.mutbl);
         let mut first_error = None;
         let mut r_borrow_var = None;
-        let (_, autoderefs, success) = self.autoderef(span, a, exprs,
-                                                      UnresolvedTypeAction::Ignore,
-                                                      lvalue_pref,
-                                                      |referent_ty, autoderef|
-        {
-            if autoderef == 0 {
+        let mut autoderef = self.autoderef(span, a);
+        let mut success = None;
+
+        for (referent_ty, autoderefs) in autoderef.by_ref() {
+            if autoderefs == 0 {
                 // Don't let this pass, otherwise it would cause
                 // &T to autoref to &&T.
-                return None;
+                continue
             }
 
             // At this point, we have deref'd `a` to `referent_ty`.  So
@@ -326,7 +325,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             //     and let regionck figure it out.
             let r = if !self.use_lub {
                 r_b // [2] above
-            } else if autoderef == 1 {
+            } else if autoderefs == 1 {
                 r_a // [3] above
             } else {
                 if r_borrow_var.is_none() { // create var lazilly, at most once
@@ -341,23 +340,22 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
                 mutbl: mt_b.mutbl // [1] above
             });
             match self.unify(derefd_ty_a, b) {
-                Ok(ty) => Some(ty),
+                Ok(ty) => { success = Some((ty, autoderefs)); break },
                 Err(err) => {
                     if first_error.is_none() {
                         first_error = Some(err);
                     }
-                    None
                 }
             }
-        });
+        }
 
         // Extract type or return an error. We return the first error
         // we got, which should be from relating the "base" type
         // (e.g., in example above, the failure from relating `Vec<T>`
         // to the target type), since that should be the least
         // confusing.
-        let ty = match success {
-            Some(ty) => ty,
+        let (ty, autoderefs) = match success {
+            Some(d) => d,
             None => {
                 let err = first_error.expect("coerce_borrowed_pointer had no error");
                 debug!("coerce_borrowed_pointer: failed with err = {:?}", err);
@@ -365,6 +363,10 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
             }
         };
 
+        // This commits the obligations to the fulfillcx. After this succeeds,
+        // this snapshot can't be rolled back.
+        autoderef.finalize(LvaluePreference::from_mutbl(mt_b.mutbl), exprs());
+
         // Now apply the autoref. We have to extract the region out of
         // the final ref type we got.
         if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 {
index a1a6a83d34ff2c8ebf46d6cf3d7e9f533ff02bde..35a5bc9c60967d69512b3b573d26b903e59ce187 100644 (file)
@@ -15,7 +15,7 @@ use rustc::traits::{self, ProjectionMode};
 use rustc::ty::subst::{self, Subst, Substs, VecPerParamSpace};
 
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use CrateCtxt;
 use super::assoc;
@@ -279,78 +279,63 @@ pub fn compare_impl_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
         // type.
 
         // Compute skolemized form of impl and trait method tys.
-        let impl_fty = tcx.mk_fn_ptr(impl_m.fty);
-        let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
-        let trait_fty = tcx.mk_fn_ptr(trait_m.fty);
-        let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
-
-        let err = infcx.commit_if_ok(|snapshot| {
-            let tcx = infcx.tcx;
-            let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
-
-            let (impl_sig, _) =
-                infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
-                                                                infer::HigherRankedType,
-                                                                &impl_m.fty.sig);
-            let impl_sig =
-                impl_sig.subst(tcx, impl_to_skol_substs);
-            let impl_sig =
-                assoc::normalize_associated_types_in(&infcx,
-                                                     &mut fulfillment_cx,
-                                                     impl_m_span,
-                                                     impl_m_body_id,
-                                                     &impl_sig);
-            let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
-                unsafety: impl_m.fty.unsafety,
-                abi: impl_m.fty.abi,
-                sig: ty::Binder(impl_sig)
-            }));
-            debug!("compare_impl_method: impl_fty={:?}",
-                   impl_fty);
-
-            let (trait_sig, skol_map) =
-                infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
-            let trait_sig =
-                trait_sig.subst(tcx, &trait_to_skol_substs);
-            let trait_sig =
-                assoc::normalize_associated_types_in(&infcx,
-                                                     &mut fulfillment_cx,
-                                                     impl_m_span,
-                                                     impl_m_body_id,
-                                                     &trait_sig);
-            let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
-                unsafety: trait_m.fty.unsafety,
-                abi: trait_m.fty.abi,
-                sig: ty::Binder(trait_sig)
-            }));
-
-            debug!("compare_impl_method: trait_fty={:?}",
+        let tcx = infcx.tcx;
+        let origin = TypeOrigin::MethodCompatCheck(impl_m_span);
+
+        let (impl_sig, _) =
+            infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
+                                                            infer::HigherRankedType,
+                                                            &impl_m.fty.sig);
+        let impl_sig =
+            impl_sig.subst(tcx, impl_to_skol_substs);
+        let impl_sig =
+            assoc::normalize_associated_types_in(&infcx,
+                                                 &mut fulfillment_cx,
+                                                 impl_m_span,
+                                                 impl_m_body_id,
+                                                 &impl_sig);
+        let impl_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
+            unsafety: impl_m.fty.unsafety,
+            abi: impl_m.fty.abi,
+            sig: ty::Binder(impl_sig)
+        }));
+        debug!("compare_impl_method: impl_fty={:?}", impl_fty);
+
+        let trait_sig = tcx.liberate_late_bound_regions(
+            infcx.parameter_environment.free_id_outlive,
+            &trait_m.fty.sig);
+        let trait_sig =
+            trait_sig.subst(tcx, &trait_to_skol_substs);
+        let trait_sig =
+            assoc::normalize_associated_types_in(&infcx,
+                                                 &mut fulfillment_cx,
+                                                 impl_m_span,
+                                                 impl_m_body_id,
+                                                 &trait_sig);
+        let trait_fty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
+            unsafety: trait_m.fty.unsafety,
+            abi: trait_m.fty.abi,
+            sig: ty::Binder(trait_sig)
+        }));
+
+        debug!("compare_impl_method: trait_fty={:?}", trait_fty);
+
+        if let Err(terr) = infcx.sub_types(false, origin, impl_fty, trait_fty) {
+            debug!("sub_types failed: impl ty {:?}, trait ty {:?}",
+                   impl_fty,
                    trait_fty);
-
-            infcx.sub_types(false, origin, impl_fty, trait_fty)?;
-
-            infcx.leak_check(false, &skol_map, snapshot)
-        });
-
-        match err {
-            Ok(()) => { }
-            Err(terr) => {
-                debug!("checking trait method for compatibility: impl ty {:?}, trait ty {:?}",
-                       impl_fty,
-                       trait_fty);
-                span_err!(tcx.sess, impl_m_span, E0053,
-                          "method `{}` has an incompatible type for trait: {}",
-                          trait_m.name,
-                          terr);
-                return;
-            }
+            span_err!(tcx.sess, impl_m_span, E0053,
+                      "method `{}` has an incompatible type for trait: {}",
+                      trait_m.name,
+                      terr);
+            return
         }
 
         // Check that all obligations are satisfied by the implementation's
         // version.
-        match fulfillment_cx.select_all_or_error(&infcx) {
-            Err(ref errors) => { infcx.report_fulfillment_errors(errors) }
-            Ok(_) => {}
+        if let Err(ref errors) = fulfillment_cx.select_all_or_error(&infcx) {
+            infcx.report_fulfillment_errors(errors);
+            return
         }
 
         // Finally, resolve all regions. This catches wily misuses of
index 7c8eb62b0e749e8998b3360492fe6b6509298a64..eeebd6a7f626b74160e7c2d4c27d1844cc3bd5db 100644 (file)
@@ -13,7 +13,7 @@ use check::FnCtxt;
 use rustc::ty::Ty;
 use rustc::infer::{InferOk, TypeOrigin};
 
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::hir;
 
 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
index ae614d7b02157ec7ae5252aeb53ab18fd3902222..56e4108153e1e669f0a2db85a67657be56cf2801 100644 (file)
@@ -21,7 +21,7 @@ use rustc::traits::{self, ProjectionMode};
 use util::nodemap::FnvHashSet;
 
 use syntax::ast;
-use syntax::codemap::{self, Span};
+use syntax_pos::{self, Span};
 
 /// check_drop_impl confirms that the Drop implementation identfied by
 /// `drop_impl_did` is not any more specialized than the type it is
@@ -62,7 +62,7 @@ pub fn check_drop_impl(ccx: &CrateCtxt, drop_impl_did: DefId) -> Result<(), ()>
         _ => {
             // Destructors only work on nominal types.  This was
             // already checked by coherence, so we can panic here.
-            let span = ccx.tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP);
+            let span = ccx.tcx.map.def_id_span(drop_impl_did, syntax_pos::DUMMY_SP);
             span_bug!(span,
                       "should have been rejected by coherence check: {}",
                       dtor_self_type);
@@ -91,7 +91,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
         let named_type = tcx.lookup_item_type(self_type_did).ty;
         let named_type = named_type.subst(tcx, &infcx.parameter_environment.free_substs);
 
-        let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP);
+        let drop_impl_span = tcx.map.def_id_span(drop_impl_did, syntax_pos::DUMMY_SP);
         let fresh_impl_substs =
             infcx.fresh_substs_for_generics(drop_impl_span, drop_impl_generics);
         let fresh_impl_self_ty = drop_impl_ty.subst(tcx, &fresh_impl_substs);
@@ -172,7 +172,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>(
 
     let self_type_node_id = tcx.map.as_local_node_id(self_type_did).unwrap();
 
-    let drop_impl_span = tcx.map.def_id_span(drop_impl_did, codemap::DUMMY_SP);
+    let drop_impl_span = tcx.map.def_id_span(drop_impl_did, syntax_pos::DUMMY_SP);
 
     // We can assume the predicates attached to struct/enum definition
     // hold.
index c02139140aed5fa1079be3b692563fa44d134fdb..0fb08ec9855de006bebaa15071f41af6dc453d43 100644 (file)
@@ -20,8 +20,8 @@ use {CrateCtxt, require_same_types};
 use std::collections::{HashMap};
 use syntax::abi::Abi;
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::parse::token;
+use syntax_pos::Span;
 
 use rustc::hir;
 
@@ -117,6 +117,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &hir::ForeignItem) {
                                     param(ccx, 0))
                  ], ccx.tcx.types.usize)
             }
+            "rustc_peek" => (1, vec![param(ccx, 0)], param(ccx, 0)),
             "init" | "init_dropped" => (1, Vec::new(), param(ccx, 0)),
             "uninit" => (1, Vec::new(), param(ccx, 0)),
             "forget" => (1, vec!( param(ccx, 0) ), tcx.mk_nil()),
index 6faf6f415c266b5eff11705f0dbbcf5a17af3bde..5fac65bbfd6552d365eab1b611a55c93994aff70 100644 (file)
 use super::probe;
 
 use check::{FnCtxt, callee};
-use check::UnresolvedTypeAction;
 use hir::def_id::DefId;
 use rustc::ty::subst::{self};
 use rustc::traits;
-use rustc::ty::{self, NoPreference, PreferMutLvalue, Ty};
+use rustc::ty::{self, LvaluePreference, NoPreference, PreferMutLvalue, Ty};
 use rustc::ty::adjustment::{AdjustDerefRef, AutoDerefRef, AutoPtr};
 use rustc::ty::fold::TypeFoldable;
 use rustc::infer::{self, InferOk, TypeOrigin};
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::hir;
 
 use std::ops::Deref;
@@ -133,10 +132,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
             ty: fty,
             substs: all_substs
         };
-        // If this is an `&mut self` method, bias the receiver
-        // expression towards mutability (this will switch
-        // e.g. `Deref` to `DerefMut` in overloaded derefs and so on).
-        self.fixup_derefs_on_method_receiver_if_necessary(&callee);
+
+        if let Some(hir::MutMutable) = pick.autoref {
+            self.convert_lvalue_derefs_to_mutable();
+        }
 
         callee
     }
@@ -164,22 +163,14 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
             (None, None)
         };
 
-        // Commit the autoderefs by calling `autoderef again, but this
+        // Commit the autoderefs by calling `autoderef` again, but this
         // time writing the results into the various tables.
-        let (autoderefd_ty, n, result) = self.autoderef(self.span,
-                                                        unadjusted_self_ty,
-                                                        || Some(self.self_expr),
-                                                        UnresolvedTypeAction::Error,
-                                                        NoPreference,
-                                                        |_, n| {
-            if n == pick.autoderefs {
-                Some(())
-            } else {
-                None
-            }
-        });
+        let mut autoderef = self.autoderef(self.span, unadjusted_self_ty);
+        let (autoderefd_ty, n) = autoderef.nth(pick.autoderefs).unwrap();
         assert_eq!(n, pick.autoderefs);
-        assert_eq!(result, Some(()));
+
+        autoderef.unambiguous_final_ty();
+        autoderef.finalize(LvaluePreference::NoPreference, Some(self.self_expr));
 
         // Write out the final adjustment.
         self.write_adjustment(self.self_expr.id, AdjustDerefRef(AutoDerefRef {
@@ -293,27 +284,21 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
         // yield an object-type (e.g., `&Object` or `Box<Object>`
         // etc).
 
-        let (_, _, result) = self.fcx.autoderef(self.span,
-                                                self_ty,
-                                                || None,
-                                                UnresolvedTypeAction::Error,
-                                                NoPreference,
-                                                |ty, _| {
-            match ty.sty {
-                ty::TyTrait(ref data) => Some(closure(self, ty, &data)),
-                _ => None,
-            }
-        });
-
-        match result {
-            Some(r) => r,
-            None => {
+        // FIXME: this feels, like, super dubious
+        self.fcx.autoderef(self.span, self_ty)
+            .filter_map(|(ty, _)| {
+                match ty.sty {
+                    ty::TyTrait(ref data) => Some(closure(self, ty, &data)),
+                    _ => None,
+                }
+            })
+            .next()
+            .unwrap_or_else(|| {
                 span_bug!(
                     self.span,
                     "self-type `{}` for ObjectPick never dereferenced to an object",
                     self_ty)
-            }
-        }
+            })
     }
 
     fn instantiate_method_substs(&mut self,
@@ -463,24 +448,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
     ///////////////////////////////////////////////////////////////////////////
     // RECONCILIATION
 
-    /// When we select a method with an `&mut self` receiver, we have to go convert any
+    /// When we select a method with a mutable autoref, we have to go convert any
     /// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
     /// respectively.
-    fn fixup_derefs_on_method_receiver_if_necessary(&self,
-                                                    method_callee: &ty::MethodCallee) {
-        let sig = match method_callee.ty.sty {
-            ty::TyFnDef(_, _, ref f) => f.sig.clone(),
-            _ => return,
-        };
-
-        match sig.0.inputs[0].sty {
-            ty::TyRef(_, ty::TypeAndMut {
-                ty: _,
-                mutbl: hir::MutMutable,
-            }) => {}
-            _ => return,
-        }
-
+    fn convert_lvalue_derefs_to_mutable(&self) {
         // Gather up expressions we want to munge.
         let mut exprs = Vec::new();
         exprs.push(self.self_expr);
@@ -495,8 +466,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
             }
         }
 
-        debug!("fixup_derefs_on_method_receiver_if_necessary: exprs={:?}",
-               exprs);
+        debug!("convert_lvalue_derefs_to_mutable: exprs={:?}", exprs);
 
         // Fix up autoderefs and derefs.
         for (i, &expr) in exprs.iter().rev().enumerate() {
@@ -509,23 +479,17 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
                 Some(_) | None => 0,
             };
 
-            debug!("fixup_derefs_on_method_receiver_if_necessary: i={} expr={:?} \
-                                                                  autoderef_count={}",
+            debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?} \
+                                                      autoderef_count={}",
                    i, expr, autoderef_count);
 
             if autoderef_count > 0 {
-                self.autoderef(expr.span,
-                               self.expr_ty(expr),
-                               || Some(expr),
-                               UnresolvedTypeAction::Error,
-                               PreferMutLvalue,
-                               |_, autoderefs| {
-                    if autoderefs == autoderef_count + 1 {
-                        Some(())
-                    } else {
-                        None
-                    }
+                let mut autoderef = self.autoderef(expr.span, self.expr_ty(expr));
+                autoderef.nth(autoderef_count).unwrap_or_else(|| {
+                    span_bug!(expr.span, "expr was deref-able {} times but now isn't?",
+                              autoderef_count);
                 });
+                autoderef.finalize(PreferMutLvalue, Some(expr));
             }
 
             // Don't retry the first one or we might infinite loop!
index 00eeefa0449ce628eb9032c2a1710b652550d794..e6401be5b3ef655f9b78785448932ee0a92e525e 100644 (file)
@@ -20,7 +20,7 @@ use rustc::ty::adjustment::{AdjustDerefRef, AutoDerefRef, AutoPtr};
 use rustc::infer;
 
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use rustc::hir;
 
index 08c041225171ab36166d0def0da199874131f9c3..6a1baf13b273d4f2bda0c4f4c3d4cf1b688ca38c 100644 (file)
@@ -13,16 +13,16 @@ use super::NoMatchData;
 use super::{CandidateSource, ImplSource, TraitSource};
 use super::suggest;
 
-use check::{FnCtxt, UnresolvedTypeAction};
+use check::{FnCtxt};
 use hir::def_id::DefId;
 use hir::def::Def;
 use rustc::ty::subst;
 use rustc::ty::subst::Subst;
 use rustc::traits;
-use rustc::ty::{self, NoPreference, Ty, ToPolyTraitRef, TraitRef, TypeFoldable};
+use rustc::ty::{self, Ty, ToPolyTraitRef, TraitRef, TypeFoldable};
 use rustc::infer::{InferOk, TypeOrigin};
 use syntax::ast;
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
 use rustc::hir;
 use std::collections::HashSet;
 use std::mem;
@@ -208,25 +208,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     fn create_steps(&self,
                     span: Span,
                     self_ty: Ty<'tcx>)
-                    -> Option<Vec<CandidateStep<'tcx>>> {
-        let mut steps = Vec::new();
-
-        let (final_ty, dereferences, _) = self.autoderef(span,
-                                                         self_ty,
-                                                         || None,
-                                                         UnresolvedTypeAction::Error,
-                                                         NoPreference,
-                                                         |t, d| {
-            steps.push(CandidateStep {
-                self_ty: t,
-                autoderefs: d,
-                unsize: false
-            });
-            None::<()> // keep iterating until we can't anymore
-        });
+                    -> Option<Vec<CandidateStep<'tcx>>>
+    {
+        // FIXME: we don't need to create the entire steps in one pass
 
+        let mut autoderef = self.autoderef(span, self_ty);
+        let mut steps: Vec<_> = autoderef.by_ref().map(|(ty, d)| CandidateStep {
+            self_ty: ty,
+            autoderefs: d,
+            unsize: false
+        }).collect();
+
+        let final_ty = autoderef.unambiguous_final_ty();
         match final_ty.sty {
             ty::TyArray(elem_ty, _) => {
+                let dereferences = steps.len() - 1;
+
                 steps.push(CandidateStep {
                     self_ty: self.tcx.mk_slice(elem_ty),
                     autoderefs: dereferences,
@@ -237,6 +234,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             _ => (),
         }
 
+        debug!("create_steps: steps={:?}", steps);
+
         Some(steps)
     }
 }
@@ -865,9 +864,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
     // THE ACTUAL SEARCH
 
     fn pick(mut self) -> PickResult<'tcx> {
-        match self.pick_core() {
-            Some(r) => return r,
-            None => {}
+        if let Some(r) = self.pick_core() {
+            return r;
         }
 
         let static_candidates = mem::replace(&mut self.static_candidates, vec![]);
@@ -930,9 +928,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
             return None;
         }
 
-        match self.pick_by_value_method(step) {
-            Some(result) => return Some(result),
-            None => {}
+        if let Some(result) = self.pick_by_value_method(step) {
+            return Some(result);
         }
 
         self.pick_autorefd_method(step)
@@ -977,7 +974,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
 
         // In general, during probing we erase regions. See
         // `impl_self_ty()` for an explanation.
-        let region = tcx.mk_region(ty::ReStatic);
+        let region = tcx.mk_region(ty::ReErased);
 
         // Search through mutabilities in order to find one where pick works:
         [hir::MutImmutable, hir::MutMutable].iter().filter_map(|&m| {
@@ -1004,12 +1001,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
         let mut possibly_unsatisfied_predicates = Vec::new();
 
         debug!("searching inherent candidates");
-        match self.consider_candidates(self_ty, &self.inherent_candidates,
-                                       &mut possibly_unsatisfied_predicates) {
-            None => {}
-            Some(pick) => {
-                return Some(pick);
-            }
+        if let Some(pick) = self.consider_candidates(self_ty,
+                                                     &self.inherent_candidates,
+                                                     &mut possibly_unsatisfied_predicates) {
+            return Some(pick);
         }
 
         debug!("searching extension candidates");
@@ -1241,7 +1236,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
             let method_regions =
                 method.generics.regions.get_slice(subst::FnSpace)
                 .iter()
-                .map(|_| ty::ReStatic)
+                .map(|_| ty::ReErased)
                 .collect();
 
             placeholder = (*substs).clone().with_method(Vec::new(), method_regions);
@@ -1277,7 +1272,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
 
         let region_placeholders =
             impl_pty.generics.regions.map(
-                |_| ty::ReStatic); // see erase_late_bound_regions() for an expl of why 'static
+                |_| ty::ReErased); // see erase_late_bound_regions() for an expl of why 'erased
 
         let substs = subst::Substs::new(type_vars, region_placeholders);
         (impl_pty.ty, substs)
index 2cd60d20251f3f361d68a89f3a6cecbcb68d3d72..f20dcdc35aea5c7af46ded13c2b84800a42ddc63 100644 (file)
@@ -13,7 +13,7 @@
 
 use CrateCtxt;
 
-use check::{self, FnCtxt, UnresolvedTypeAction};
+use check::{FnCtxt};
 use rustc::hir::map as hir_map;
 use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable};
 use middle::cstore;
@@ -21,14 +21,13 @@ use hir::def::Def;
 use hir::def_id::DefId;
 use middle::lang_items::FnOnceTraitLangItem;
 use rustc::ty::subst::Substs;
-use rustc::ty::LvaluePreference;
 use rustc::traits::{Obligation, SelectionContext};
 use util::nodemap::{FnvHashSet};
 
-
 use syntax::ast;
-use syntax::codemap::Span;
-use syntax::errors::DiagnosticBuilder;
+use errors::DiagnosticBuilder;
+use syntax_pos::Span;
+
 use rustc::hir::print as pprust;
 use rustc::hir;
 use rustc::hir::Expr_;
@@ -48,42 +47,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
             // If it's not a simple function, look for things which implement FnOnce
             _ => {
-                if let Ok(fn_once_trait_did) =
-                        tcx.lang_items.require(FnOnceTraitLangItem) {
-                    let (_, _, opt_is_fn) = self.autoderef(span,
-                                                           ty,
-                                                           || None,
-                                                           UnresolvedTypeAction::Ignore,
-                                                           LvaluePreference::NoPreference,
-                                                           |ty, _| {
-                        self.probe(|_| {
-                            let fn_once_substs =
-                                Substs::new_trait(vec![self.next_ty_var()], vec![], ty);
-                            let trait_ref =
-                                ty::TraitRef::new(fn_once_trait_did,
-                                                  tcx.mk_substs(fn_once_substs));
-                            let poly_trait_ref = trait_ref.to_poly_trait_ref();
-                            let obligation = Obligation::misc(span,
-                                                              self.body_id,
-                                                              poly_trait_ref
-                                                                 .to_predicate());
-                            let mut selcx = SelectionContext::new(self);
-
-                            if selcx.evaluate_obligation(&obligation) {
-                                Some(())
-                            } else {
-                                None
-                            }
-                        })
-                    });
+                let fn_once = match tcx.lang_items.require(FnOnceTraitLangItem) {
+                    Ok(fn_once) => fn_once,
+                    Err(..) => return false
+                };
 
-                    opt_is_fn.is_some()
-                } else {
-                    false
-                }
+                self.autoderef(span, ty).any(|(ty, _)| self.probe(|_| {
+                    let fn_once_substs =
+                        Substs::new_trait(vec![self.next_ty_var()], vec![], ty);
+                    let trait_ref =
+                        ty::TraitRef::new(fn_once,
+                                          tcx.mk_substs(fn_once_substs));
+                    let poly_trait_ref = trait_ref.to_poly_trait_ref();
+                    let obligation = Obligation::misc(span,
+                                                      self.body_id,
+                                                      poly_trait_ref
+                                                      .to_predicate());
+                    SelectionContext::new(self).evaluate_obligation(&obligation)
+                }))
             }
         }
     }
+
     pub fn report_method_error(&self,
                                span: Span,
                                rcvr_ty: Ty<'tcx>,
@@ -384,15 +369,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             return is_local(self.resolve_type_vars_with_obligations(rcvr_ty));
         }
 
-        self.autoderef(span, rcvr_ty, || None,
-                       check::UnresolvedTypeAction::Ignore, ty::NoPreference,
-                       |ty, _| {
-            if is_local(ty) {
-                Some(())
-            } else {
-                None
-            }
-        }).2.is_some()
+        self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty))
     }
 }
 
index 264003bb62b816d02d0021842b4c4e7545a28c76..e966e201bdbed35d7044ee90bd70b7aef9ffda2c 100644 (file)
@@ -81,14 +81,13 @@ pub use self::compare_method::{compare_impl_method, compare_const_impl};
 use self::TupleArgumentsFlag::*;
 
 use astconv::{AstConv, ast_region_to_region, PathParamMode};
-use check::_match::PatCtxt;
 use dep_graph::DepNode;
 use fmt_macros::{Parser, Piece, Position};
 use middle::cstore::LOCAL_CRATE;
 use hir::def::{self, Def};
 use hir::def_id::DefId;
+use hir::pat_util;
 use rustc::infer::{self, InferCtxt, InferOk, TypeOrigin, TypeTrace, type_variable};
-use hir::pat_util::{self, pat_id_map};
 use rustc::ty::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace};
 use rustc::traits::{self, ProjectionMode};
 use rustc::ty::{GenericPredicates, TypeScheme};
@@ -102,7 +101,7 @@ use rustc::ty::util::{Representability, IntTypeExt};
 use require_c_abi_if_variadic;
 use rscope::{ElisionFailureInfo, RegionScope};
 use session::{Session, CompileResult};
-use {CrateCtxt, lookup_full_def};
+use CrateCtxt;
 use TypeAndSubsts;
 use lint;
 use util::common::{block_query, ErrorReported, indenter, loop_query};
@@ -116,11 +115,12 @@ use syntax::abi::Abi;
 use syntax::ast;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
-use syntax::codemap::{self, Span, Spanned};
-use syntax::errors::DiagnosticBuilder;
+use syntax::codemap::{self, Spanned};
 use syntax::parse::token::{self, InternedString, keywords};
 use syntax::ptr::P;
 use syntax::util::lev_distance::find_best_match_for_name;
+use syntax_pos::{self, Span};
+use errors::DiagnosticBuilder;
 
 use rustc::hir::intravisit::{self, Visitor};
 use rustc::hir::{self, PatKind};
@@ -129,6 +129,7 @@ use rustc_back::slice;
 use rustc_const_eval::eval_repeat_count;
 
 mod assoc;
+mod autoderef;
 pub mod dropck;
 pub mod _match;
 pub mod writeback;
@@ -572,19 +573,17 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for GatherLocalsVisitor<'a, 'gcx, 'tcx> {
 
     // Add pattern bindings.
     fn visit_pat(&mut self, p: &'gcx hir::Pat) {
-        if let PatKind::Ident(_, ref path1, _) = p.node {
-            if pat_util::pat_is_binding(&self.fcx.tcx.def_map.borrow(), p) {
-                let var_ty = self.assign(p.span, p.id, None);
-
-                self.fcx.require_type_is_sized(var_ty, p.span,
-                                               traits::VariableType(p.id));
-
-                debug!("Pattern binding {} is assigned to {} with type {:?}",
-                       path1.node,
-                       self.fcx.ty_to_string(
-                           self.fcx.locals.borrow().get(&p.id).unwrap().clone()),
-                       var_ty);
-            }
+        if let PatKind::Binding(_, ref path1, _) = p.node {
+            let var_ty = self.assign(p.span, p.id, None);
+
+            self.fcx.require_type_is_sized(var_ty, p.span,
+                                           traits::VariableType(p.id));
+
+            debug!("Pattern binding {} is assigned to {} with type {:?}",
+                   path1.node,
+                   self.fcx.ty_to_string(
+                       self.fcx.locals.borrow().get(&p.id).unwrap().clone()),
+                   var_ty);
         }
         intravisit::walk_pat(self, p);
     }
@@ -632,8 +631,6 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
                             body: &'gcx hir::Block)
                             -> FnCtxt<'a, 'gcx, 'tcx>
 {
-    let tcx = inherited.tcx;
-
     let arg_tys = &fn_sig.inputs;
     let ret_ty = fn_sig.output;
 
@@ -669,21 +666,13 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
             fcx.register_old_wf_obligation(arg_ty, input.ty.span, traits::MiscObligation);
 
             // Create type variables for each argument.
-            pat_util::pat_bindings(
-                &tcx.def_map,
-                &input.pat,
-                |_bm, pat_id, sp, _path| {
-                    let var_ty = visit.assign(sp, pat_id, None);
-                    fcx.require_type_is_sized(var_ty, sp,
-                                              traits::VariableType(pat_id));
-                });
+            pat_util::pat_bindings(&input.pat, |_bm, pat_id, sp, _path| {
+                let var_ty = visit.assign(sp, pat_id, None);
+                fcx.require_type_is_sized(var_ty, sp, traits::VariableType(pat_id));
+            });
 
             // Check the pattern.
-            let pcx = PatCtxt {
-                fcx: &fcx,
-                map: pat_id_map(&tcx.def_map, &input.pat),
-            };
-            pcx.check_pat(&input.pat, *arg_ty);
+            fcx.check_pat(&input.pat, *arg_ty);
         }
 
         visit.visit_block(body);
@@ -1168,6 +1157,7 @@ fn check_const<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
         let rty = ccx.tcx.node_id_to_type(id);
         let fcx = FnCtxt::new(&inh, ty::FnConverging(rty), e.id);
         let declty = fcx.tcx.lookup_item_type(ccx.tcx.map.local_def_id(id)).ty;
+        fcx.require_type_is_sized(declty, e.span, traits::ConstSized);
         fcx.check_const_with_ty(sp, e, declty);
     });
 }
@@ -1412,17 +1402,6 @@ impl<'a, 'gcx, 'tcx> RegionScope for FnCtxt<'a, 'gcx, 'tcx> {
     }
 }
 
-/// Whether `autoderef` requires types to resolve.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum UnresolvedTypeAction {
-    /// Produce an error and return `TyError` whenever a type cannot
-    /// be resolved (i.e. it is `TyInfer`).
-    Error,
-    /// Go on without emitting any errors, and return the unresolved
-    /// type. Useful for probing, e.g. in coercions.
-    Ignore
-}
-
 /// Controls whether the arguments are tupled. This is used for the call
 /// operator.
 ///
@@ -1931,7 +1910,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             for ty in &self.unsolved_variables() {
                 if let ty::TyInfer(_) = self.shallow_resolve(ty).sty {
                     debug!("default_type_parameters: defaulting `{:?}` to error", ty);
-                    self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx().types.err);
+                    self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx().types.err);
                 }
             }
             return;
@@ -1942,18 +1921,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             if self.type_var_diverges(resolved) {
                 debug!("default_type_parameters: defaulting `{:?}` to `()` because it diverges",
                        resolved);
-                self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.mk_nil());
+                self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
             } else {
                 match self.type_is_unconstrained_numeric(resolved) {
                     UnconstrainedInt => {
                         debug!("default_type_parameters: defaulting `{:?}` to `i32`",
                                resolved);
-                        self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.types.i32)
+                        self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.i32)
                     },
                     UnconstrainedFloat => {
                         debug!("default_type_parameters: defaulting `{:?}` to `f32`",
                                resolved);
-                        self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.types.f64)
+                        self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.f64)
                     }
                     Neither => { }
                 }
@@ -2016,7 +1995,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             for ty in &unsolved_variables {
                 let resolved = self.resolve_type_vars_if_possible(ty);
                 if self.type_var_diverges(resolved) {
-                    self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.mk_nil());
+                    self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
                 } else {
                     match self.type_is_unconstrained_numeric(resolved) {
                         UnconstrainedInt | UnconstrainedFloat => {
@@ -2074,14 +2053,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             let _ = self.commit_if_ok(|_: &infer::CombinedSnapshot| {
                 for ty in &unbound_tyvars {
                     if self.type_var_diverges(ty) {
-                        self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.mk_nil());
+                        self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
                     } else {
                         match self.type_is_unconstrained_numeric(ty) {
                             UnconstrainedInt => {
-                                self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.types.i32)
+                                self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.i32)
                             },
                             UnconstrainedFloat => {
-                                self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.types.f64)
+                                self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.f64)
                             }
                             Neither => {
                                 if let Some(default) = default_map.get(ty) {
@@ -2119,7 +2098,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         self.find_conflicting_default(&unbound_tyvars, &default_map, conflict)
                             .unwrap_or(type_variable::Default {
                                 ty: self.next_ty_var(),
-                                origin_span: codemap::DUMMY_SP,
+                                origin_span: syntax_pos::DUMMY_SP,
                                 def_id: self.tcx.map.local_def_id(0) // what do I put here?
                             });
 
@@ -2170,14 +2149,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // reporting for more then one conflict.
         for ty in &unbound_tyvars {
             if self.type_var_diverges(ty) {
-                self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.mk_nil());
+                self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.mk_nil());
             } else {
                 match self.type_is_unconstrained_numeric(ty) {
                     UnconstrainedInt => {
-                        self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.types.i32)
+                        self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.i32)
                     },
                     UnconstrainedFloat => {
-                        self.demand_eqtype(codemap::DUMMY_SP, *ty, self.tcx.types.f64)
+                        self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, self.tcx.types.f64)
                     },
                     Neither => {
                         if let Some(default) = default_map.get(ty) {
@@ -2228,120 +2207,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    /// Executes an autoderef loop for the type `t`. At each step, invokes `should_stop`
-    /// to decide whether to terminate the loop. Returns the final type and number of
-    /// derefs that it performed.
-    ///
-    /// Note: this method does not modify the adjustments table. The caller is responsible for
-    /// inserting an AutoAdjustment record into the `self` using one of the suitable methods.
-    pub fn autoderef<'b, E, I, T, F>(&self,
-                                     sp: Span,
-                                     base_ty: Ty<'tcx>,
-                                     maybe_exprs: E,
-                                     unresolved_type_action: UnresolvedTypeAction,
-                                     mut lvalue_pref: LvaluePreference,
-                                     mut should_stop: F)
-                                     -> (Ty<'tcx>, usize, Option<T>)
-        // FIXME(eddyb) use copyable iterators when that becomes ergonomic.
-        where E: Fn() -> I,
-              I: IntoIterator<Item=&'b hir::Expr>,
-              F: FnMut(Ty<'tcx>, usize) -> Option<T>,
-    {
-        debug!("autoderef(base_ty={:?}, lvalue_pref={:?})",
-               base_ty, lvalue_pref);
-
-        let mut t = base_ty;
-        for autoderefs in 0..self.tcx.sess.recursion_limit.get() {
-            let resolved_t = match unresolved_type_action {
-                UnresolvedTypeAction::Error => {
-                    self.structurally_resolved_type(sp, t)
-                }
-                UnresolvedTypeAction::Ignore => {
-                    // We can continue even when the type cannot be resolved
-                    // (i.e. it is an inference variable) because `Ty::builtin_deref`
-                    // and `try_overloaded_deref` both simply return `None`
-                    // in such a case without producing spurious errors.
-                    self.resolve_type_vars_if_possible(&t)
-                }
-            };
-            if resolved_t.references_error() {
-                return (resolved_t, autoderefs, None);
-            }
-
-            match should_stop(resolved_t, autoderefs) {
-                Some(x) => return (resolved_t, autoderefs, Some(x)),
-                None => {}
-            }
-
-            // Otherwise, deref if type is derefable:
-
-            // Super subtle: it might seem as though we should
-            // pass `opt_expr` to `try_overloaded_deref`, so that
-            // the (implicit) autoref of using an overloaded deref
-            // would get added to the adjustment table. However we
-            // do not do that, because it's kind of a
-            // "meta-adjustment" -- instead, we just leave it
-            // unrecorded and know that there "will be" an
-            // autoref. regionck and other bits of the code base,
-            // when they encounter an overloaded autoderef, have
-            // to do some reconstructive surgery. This is a pretty
-            // complex mess that is begging for a proper MIR.
-            let mt = if let Some(mt) = resolved_t.builtin_deref(false, lvalue_pref) {
-                mt
-            } else if let Some(method) = self.try_overloaded_deref(sp, None,
-                                                                   resolved_t, lvalue_pref) {
-                for expr in maybe_exprs() {
-                    let method_call = MethodCall::autoderef(expr.id, autoderefs as u32);
-                    self.tables.borrow_mut().method_map.insert(method_call, method);
-                }
-                self.make_overloaded_lvalue_return_type(method)
-            } else {
-                return (resolved_t, autoderefs, None);
-            };
-
-            t = mt.ty;
-            if mt.mutbl == hir::MutImmutable {
-                lvalue_pref = NoPreference;
-            }
-        }
-
-        // We've reached the recursion limit, error gracefully.
-        span_err!(self.tcx.sess, sp, E0055,
-            "reached the recursion limit while auto-dereferencing {:?}",
-            base_ty);
-        (self.tcx.types.err, 0, None)
-    }
-
-    fn try_overloaded_deref(&self,
-                            span: Span,
-                            base_expr: Option<&hir::Expr>,
-                            base_ty: Ty<'tcx>,
-                            lvalue_pref: LvaluePreference)
-                            -> Option<MethodCallee<'tcx>>
-    {
-        // Try DerefMut first, if preferred.
-        let method = match (lvalue_pref, self.tcx.lang_items.deref_mut_trait()) {
-            (PreferMutLvalue, Some(trait_did)) => {
-                self.lookup_method_in_trait(span, base_expr,
-                                            token::intern("deref_mut"), trait_did,
-                                            base_ty, None)
-            }
-            _ => None
-        };
-
-        // Otherwise, fall back to Deref.
-        let method = match (method, self.tcx.lang_items.deref_trait()) {
-            (None, Some(trait_did)) => {
-                self.lookup_method_in_trait(span, base_expr,
-                                            token::intern("deref"), trait_did,
-                                            base_ty, None)
-            }
-            (method, _) => method
-        };
-
-        method
-    }
-
     /// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait
     /// returns a type of `&T`, but the actual type we assign to the
     /// *expression* is `T`. So this function just peels off the return
@@ -2371,29 +2236,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // autoderef that normal method probing does. They could likely be
         // consolidated.
 
-        let (ty, autoderefs, final_mt) = self.autoderef(base_expr.span,
-                                                        base_ty,
-                                                        || Some(base_expr),
-                                                        UnresolvedTypeAction::Error,
-                                                        lvalue_pref,
-                                                        |adj_ty, idx| {
-            self.try_index_step(MethodCall::expr(expr.id), expr, base_expr,
-                                adj_ty, idx, false, lvalue_pref, idx_ty)
-        });
+        let mut autoderef = self.autoderef(base_expr.span, base_ty);
 
-        if final_mt.is_some() {
-            return final_mt;
-        }
+        while let Some((adj_ty, autoderefs)) = autoderef.next() {
+            if let Some(final_mt) = self.try_index_step(
+                MethodCall::expr(expr.id),
+                expr, base_expr, adj_ty, autoderefs,
+                false, lvalue_pref, idx_ty)
+            {
+                autoderef.finalize(lvalue_pref, Some(base_expr));
+                return Some(final_mt);
+            }
 
-        // After we have fully autoderef'd, if the resulting type is [T; n], then
-        // do a final unsized coercion to yield [T].
-        if let ty::TyArray(element_ty, _) = ty.sty {
-            let adjusted_ty = self.tcx.mk_slice(element_ty);
-            self.try_index_step(MethodCall::expr(expr.id), expr, base_expr,
-                                adjusted_ty, autoderefs, true, lvalue_pref, idx_ty)
-        } else {
-            None
+            if let ty::TyArray(element_ty, _) = adj_ty.sty {
+                autoderef.finalize(lvalue_pref, Some(base_expr));
+                let adjusted_ty = self.tcx.mk_slice(element_ty);
+                return self.try_index_step(
+                    MethodCall::expr(expr.id), expr, base_expr,
+                    adjusted_ty, autoderefs, true, lvalue_pref, idx_ty);
+            }
         }
+        autoderef.unambiguous_final_ty();
+        None
     }
 
     /// To type-check `base_expr[index_expr]`, we progressively autoderef
@@ -2540,29 +2404,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         let mut expected_arg_tys = expected_arg_tys;
         let expected_arg_count = fn_inputs.len();
+
+        fn parameter_count_error<'tcx>(sess: &Session, sp: Span, fn_inputs: &[Ty<'tcx>],
+                                       expected_count: usize, arg_count: usize, error_code: &str,
+                                       variadic: bool) {
+            let mut err = sess.struct_span_err_with_code(sp,
+                &format!("this function takes {}{} parameter{} but {} parameter{} supplied",
+                    if variadic {"at least "} else {""},
+                    expected_count,
+                    if expected_count == 1 {""} else {"s"},
+                    arg_count,
+                    if arg_count == 1 {" was"} else {"s were"}),
+                error_code);
+            let input_types = fn_inputs.iter().map(|i| format!("{:?}", i)).collect::<Vec<String>>();
+            if input_types.len() > 0 {
+                err.note(&format!("the following parameter type{} expected: {}",
+                        if expected_count == 1 {" was"} else {"s were"},
+                        input_types.join(", ")));
+            }
+            err.emit();
+        }
+
         let formal_tys = if tuple_arguments == TupleArguments {
             let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]);
             match tuple_type.sty {
+                ty::TyTuple(arg_types) if arg_types.len() != args.len() => {
+                    parameter_count_error(tcx.sess, sp, fn_inputs, arg_types.len(), args.len(),
+                                          "E0057", false);
+                    expected_arg_tys = &[];
+                    self.err_args(args.len())
+                }
                 ty::TyTuple(arg_types) => {
-                    if arg_types.len() != args.len() {
-                        span_err!(tcx.sess, sp, E0057,
-                            "this function takes {} parameter{} but {} parameter{} supplied",
-                            arg_types.len(),
-                            if arg_types.len() == 1 {""} else {"s"},
-                            args.len(),
-                            if args.len() == 1 {" was"} else {"s were"});
-                        expected_arg_tys = &[];
-                        self.err_args(args.len())
-                    } else {
-                        expected_arg_tys = match expected_arg_tys.get(0) {
-                            Some(&ty) => match ty.sty {
-                                ty::TyTuple(ref tys) => &tys,
-                                _ => &[]
-                            },
-                            None => &[]
-                        };
-                        arg_types.to_vec()
-                    }
+                    expected_arg_tys = match expected_arg_tys.get(0) {
+                        Some(&ty) => match ty.sty {
+                            ty::TyTuple(ref tys) => &tys,
+                            _ => &[]
+                        },
+                        None => &[]
+                    };
+                    arg_types.to_vec()
                 }
                 _ => {
                     span_err!(tcx.sess, sp, E0059,
@@ -2578,23 +2458,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             if supplied_arg_count >= expected_arg_count {
                 fn_inputs.to_vec()
             } else {
-                span_err!(tcx.sess, sp, E0060,
-                    "this function takes at least {} parameter{} \
-                     but {} parameter{} supplied",
-                    expected_arg_count,
-                    if expected_arg_count == 1 {""} else {"s"},
-                    supplied_arg_count,
-                    if supplied_arg_count == 1 {" was"} else {"s were"});
+                parameter_count_error(tcx.sess, sp, fn_inputs, expected_arg_count,
+                                      supplied_arg_count, "E0060", true);
                 expected_arg_tys = &[];
                 self.err_args(supplied_arg_count)
             }
         } else {
-            span_err!(tcx.sess, sp, E0061,
-                "this function takes {} parameter{} but {} parameter{} supplied",
-                expected_arg_count,
-                if expected_arg_count == 1 {""} else {"s"},
-                supplied_arg_count,
-                if supplied_arg_count == 1 {" was"} else {"s were"});
+            parameter_count_error(tcx.sess, sp, fn_inputs, expected_arg_count, supplied_arg_count,
+                                  "E0061", false);
             expected_arg_tys = &[];
             self.err_args(supplied_arg_count)
         };
@@ -3034,32 +2905,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let expr_t = self.structurally_resolved_type(expr.span,
                                                      self.expr_ty(base));
         let mut private_candidate = None;
-        let (_, autoderefs, field_ty) = self.autoderef(expr.span,
-                                                       expr_t,
-                                                       || Some(base),
-                                                       UnresolvedTypeAction::Error,
-                                                       lvalue_pref,
-                                                       |base_t, _| {
-                if let ty::TyStruct(base_def, substs) = base_t.sty {
-                    debug!("struct named {:?}",  base_t);
-                    if let Some(field) = base_def.struct_variant().find_field_named(field.node) {
-                        let field_ty = self.field_ty(expr.span, field, substs);
-                        if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
-                            return Some(field_ty);
-                        }
-                        private_candidate = Some((base_def.did, field_ty));
+        let mut autoderef = self.autoderef(expr.span, expr_t);
+        while let Some((base_t, autoderefs)) = autoderef.next() {
+            if let ty::TyStruct(base_def, substs) = base_t.sty {
+                debug!("struct named {:?}",  base_t);
+                if let Some(field) = base_def.struct_variant().find_field_named(field.node) {
+                    let field_ty = self.field_ty(expr.span, field, substs);
+                    if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
+                        autoderef.finalize(lvalue_pref, Some(base));
+                        self.write_ty(expr.id, field_ty);
+                        self.write_autoderef_adjustment(base.id, autoderefs);
+                        return;
                     }
+                    private_candidate = Some((base_def.did, field_ty));
                 }
-                None
-            });
-        match field_ty {
-            Some(field_ty) => {
-                self.write_ty(expr.id, field_ty);
-                self.write_autoderef_adjustment(base.id, autoderefs);
-                return;
             }
-            None => {}
         }
+        autoderef.unambiguous_final_ty();
 
         if let Some((did, field_ty)) = private_candidate {
             let struct_path = self.tcx().item_path_str(did);
@@ -3132,42 +2994,39 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                                      self.expr_ty(base));
         let mut private_candidate = None;
         let mut tuple_like = false;
-        let (_, autoderefs, field_ty) = self.autoderef(expr.span,
-                                                       expr_t,
-                                                       || Some(base),
-                                                       UnresolvedTypeAction::Error,
-                                                       lvalue_pref,
-                                                       |base_t, _| {
-                let (base_def, substs) = match base_t.sty {
-                    ty::TyStruct(base_def, substs) => (base_def, substs),
-                    ty::TyTuple(ref v) => {
-                        tuple_like = true;
-                        return if idx.node < v.len() { Some(v[idx.node]) } else { None }
-                    }
-                    _ => return None,
-                };
-
-                tuple_like = base_def.struct_variant().is_tuple_struct();
-                if !tuple_like { return None }
-
-                debug!("tuple struct named {:?}",  base_t);
-                if let Some(field) = base_def.struct_variant().fields.get(idx.node) {
-                    let field_ty = self.field_ty(expr.span, field, substs);
-                    if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
-                        return Some(field_ty);
-                    }
-                    private_candidate = Some((base_def.did, field_ty));
+        let mut autoderef = self.autoderef(expr.span, expr_t);
+        while let Some((base_t, autoderefs)) = autoderef.next() {
+            let field = match base_t.sty {
+                ty::TyStruct(base_def, substs) => {
+                    tuple_like = base_def.struct_variant().is_tuple_struct();
+                    if !tuple_like { continue }
+
+                    debug!("tuple struct named {:?}",  base_t);
+                    base_def.struct_variant().fields.get(idx.node).and_then(|field| {
+                        let field_ty = self.field_ty(expr.span, field, substs);
+                        private_candidate = Some((base_def.did, field_ty));
+                        if field.vis.is_accessible_from(self.body_id, &self.tcx().map) {
+                            Some(field_ty)
+                        } else {
+                            None
+                        }
+                    })
                 }
-                None
-            });
-        match field_ty {
-            Some(field_ty) => {
+                ty::TyTuple(ref v) => {
+                    tuple_like = true;
+                    v.get(idx.node).cloned()
+                }
+                _ => continue
+            };
+
+            if let Some(field_ty) = field {
+                autoderef.finalize(lvalue_pref, Some(base));
                 self.write_ty(expr.id, field_ty);
                 self.write_autoderef_adjustment(base.id, autoderefs);
                 return;
             }
-            None => {}
         }
+        autoderef.unambiguous_final_ty();
 
         if let Some((did, field_ty)) = private_candidate {
             let struct_path = self.tcx().item_path_str(did);
@@ -3303,7 +3162,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let tcx = self.tcx;
 
         // Find the relevant variant
-        let def = lookup_full_def(tcx, path.span, expr.id);
+        let def = tcx.expect_def(expr.id);
         if def == Def::Err {
             self.set_tainted_by_errors();
             self.check_struct_fields_on_error(expr.id, fields, base_expr);
@@ -3495,18 +3354,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                   self.to_ty(&qself.ty)
               });
 
-              let path_res = if let Some(&d) = tcx.def_map.borrow().get(&id) {
-                  d
-              } else if let Some(hir::QSelf { position: 0, .. }) = *maybe_qself {
-                    // Create some fake resolution that can't possibly be a type.
-                    def::PathResolution {
-                        base_def: Def::Mod(tcx.map.local_def_id(ast::CRATE_NODE_ID)),
-                        depth: path.segments.len()
-                    }
-                } else {
-                  span_bug!(expr.span, "unbound path {:?}", expr)
-              };
-
+              let path_res = tcx.expect_resolution(id);
               if let Some((opt_ty, segments, def)) =
                       self.resolve_ty_and_def_ufcs(path_res, opt_self_ty, path,
                                                    expr.span, expr.id) {
@@ -3856,6 +3704,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                expected);
     }
 
+    // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
+    // The newly resolved definition is written into `def_map`.
+    pub fn finish_resolving_struct_path(&self,
+                                        path: &hir::Path,
+                                        node_id: ast::NodeId,
+                                        span: Span)
+                                        -> Def
+    {
+        let path_res = self.tcx().expect_resolution(node_id);
+        if path_res.depth == 0 {
+            // If fully resolved already, we don't have to do anything.
+            path_res.base_def
+        } else {
+            let base_ty_end = path.segments.len() - path_res.depth;
+            let (_ty, def) = AstConv::finish_resolving_def_to_ty(self, self, span,
+                                                                 PathParamMode::Optional,
+                                                                 path_res.base_def,
+                                                                 None,
+                                                                 node_id,
+                                                                 &path.segments[..base_ty_end],
+                                                                 &path.segments[base_ty_end..]);
+            // Write back the new resolution.
+            self.tcx().def_map.borrow_mut().insert(node_id, def::PathResolution::new(def));
+            def
+        }
+    }
+
     pub fn resolve_ty_and_def_ufcs<'b>(&self,
                                        path_res: def::PathResolution,
                                        opt_self_ty: Option<Ty<'tcx>>,
@@ -3897,10 +3772,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
             if let Some(def) = def {
                 // Write back the new resolution.
-                self.tcx().def_map.borrow_mut().insert(node_id, def::PathResolution {
-                    base_def: def,
-                    depth: 0,
-                });
+                self.tcx().def_map.borrow_mut().insert(node_id, def::PathResolution::new(def));
                 Some((Some(ty), slice::ref_slice(item_segment), def))
             } else {
                 self.write_error(node_id);
@@ -3934,8 +3806,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     }
 
     pub fn check_decl_local(&self, local: &'gcx hir::Local)  {
-        let tcx = self.tcx;
-
         let t = self.local_ty(local.span, local.id);
         self.write_ty(local.id, t);
 
@@ -3947,11 +3817,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             }
         }
 
-        let pcx = PatCtxt {
-            fcx: self,
-            map: pat_id_map(&tcx.def_map, &local.pat),
-        };
-        pcx.check_pat(&local.pat, t);
+        self.check_pat(&local.pat, t);
         let pat_ty = self.node_ty(local.pat.id);
         if pat_ty.references_error() {
             self.write_ty(local.id, pat_ty);
@@ -4709,7 +4575,7 @@ pub fn may_break(tcx: TyCtxt, id: ast::NodeId, b: &hir::Block) -> bool {
     // <id> nested anywhere inside the loop?
     (block_query(b, |e| {
         if let hir::ExprBreak(Some(_)) = e.node {
-            lookup_full_def(tcx, e.span, e.id) == Def::Label(id)
+            tcx.expect_def(e.id) == Def::Label(id)
         } else {
             false
         }
index 7b79f2ec9bfe62a176159c9356ad6dd36ee78a81..5a7038a056982cc390709acb49a14c1beebd1f75 100644 (file)
@@ -99,7 +99,7 @@ use rustc::ty::wf::ImpliedBound;
 use std::mem;
 use std::ops::Deref;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::hir::intravisit::{self, Visitor};
 use rustc::hir::{self, PatKind};
 
@@ -452,7 +452,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
     fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat) {
         let tcx = self.tcx;
         debug!("regionck::visit_pat(pat={:?})", pat);
-        pat_util::pat_bindings(&tcx.def_map, pat, |_, id, span, _| {
+        pat_util::pat_bindings(pat, |_, id, span, _| {
             // If we have a variable that contains region'd data, that
             // data will be accessible from anywhere that the variable is
             // accessed. We must be wary of loops like this:
@@ -1157,25 +1157,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
         debug!("link_pattern(discr_cmt={:?}, root_pat={:?})",
                discr_cmt,
                root_pat);
-        let _ = mc.cat_pattern(discr_cmt, root_pat, |mc, sub_cmt, sub_pat| {
+    let _ = mc.cat_pattern(discr_cmt, root_pat, |_, sub_cmt, sub_pat| {
                 match sub_pat.node {
                     // `ref x` pattern
-                    PatKind::Ident(hir::BindByRef(mutbl), _, _) => {
+                    PatKind::Binding(hir::BindByRef(mutbl), _, _) => {
                         self.link_region_from_node_type(sub_pat.span, sub_pat.id,
                                                         mutbl, sub_cmt);
                     }
-
-                    // `[_, ..slice, _]` pattern
-                    PatKind::Vec(_, Some(ref slice_pat), _) => {
-                        match mc.cat_slice_pattern(sub_cmt, &slice_pat) {
-                            Ok((slice_cmt, slice_mutbl, slice_r)) => {
-                                self.link_region(sub_pat.span, &slice_r,
-                                                 ty::BorrowKind::from_mutbl(slice_mutbl),
-                                                 slice_cmt);
-                            }
-                            Err(()) => {}
-                        }
-                    }
                     _ => {}
                 }
             });
index 19964d736f5925763e0a29d3b3730d20809bae75..71490423f73f2d0a510cac3b9bf01ac4c14c6751 100644 (file)
@@ -47,11 +47,11 @@ use middle::mem_categorization as mc;
 use middle::mem_categorization::Categorization;
 use rustc::ty::{self, Ty};
 use rustc::infer::UpvarRegion;
-use std::collections::HashSet;
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::hir;
 use rustc::hir::intravisit::{self, Visitor};
+use rustc::util::nodemap::NodeMap;
 
 ///////////////////////////////////////////////////////////////////////////
 // PUBLIC ENTRY POINTS
@@ -60,9 +60,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn closure_analyze_fn(&self, body: &hir::Block) {
         let mut seed = SeedBorrowKind::new(self);
         seed.visit_block(body);
-        let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
 
-        let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds);
+        let mut adjust = AdjustBorrowKind::new(self, seed.temp_closure_kinds);
         adjust.visit_block(body);
 
         // it's our job to process these.
@@ -72,9 +71,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     pub fn closure_analyze_const(&self, body: &hir::Expr) {
         let mut seed = SeedBorrowKind::new(self);
         seed.visit_expr(body);
-        let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
 
-        let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds);
+        let mut adjust = AdjustBorrowKind::new(self, seed.temp_closure_kinds);
         adjust.visit_expr(body);
 
         // it's our job to process these.
@@ -87,7 +85,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
 struct SeedBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
-    closures_with_inferred_kinds: HashSet<ast::NodeId>,
+    temp_closure_kinds: NodeMap<ty::ClosureKind>,
 }
 
 impl<'a, 'gcx, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'gcx, 'tcx> {
@@ -106,7 +104,7 @@ impl<'a, 'gcx, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'gcx, 'tcx> {
 
 impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
     fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>) -> SeedBorrowKind<'a, 'gcx, 'tcx> {
-        SeedBorrowKind { fcx: fcx, closures_with_inferred_kinds: HashSet::new() }
+        SeedBorrowKind { fcx: fcx, temp_closure_kinds: NodeMap() }
     }
 
     fn check_closure(&mut self,
@@ -116,11 +114,8 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
     {
         let closure_def_id = self.fcx.tcx.map.local_def_id(expr.id);
         if !self.fcx.tables.borrow().closure_kinds.contains_key(&closure_def_id) {
-            self.closures_with_inferred_kinds.insert(expr.id);
-            self.fcx.tables.borrow_mut().closure_kinds
-                                        .insert(closure_def_id, ty::ClosureKind::Fn);
-            debug!("check_closure: adding closure_id={:?} to closures_with_inferred_kinds",
-                   closure_def_id);
+            self.temp_closure_kinds.insert(expr.id, ty::ClosureKind::Fn);
+            debug!("check_closure: adding closure {:?} as Fn", expr.id);
         }
 
         self.fcx.tcx.with_freevars(expr.id, |freevars| {
@@ -154,14 +149,14 @@ impl<'a, 'gcx, 'tcx> SeedBorrowKind<'a, 'gcx, 'tcx> {
 
 struct AdjustBorrowKind<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
-    closures_with_inferred_kinds: &'a HashSet<ast::NodeId>,
+    temp_closure_kinds: NodeMap<ty::ClosureKind>,
 }
 
 impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
     fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
-           closures_with_inferred_kinds: &'a HashSet<ast::NodeId>)
+           temp_closure_kinds: NodeMap<ty::ClosureKind>)
            -> AdjustBorrowKind<'a, 'gcx, 'tcx> {
-        AdjustBorrowKind { fcx: fcx, closures_with_inferred_kinds: closures_with_inferred_kinds }
+        AdjustBorrowKind { fcx: fcx, temp_closure_kinds: temp_closure_kinds }
     }
 
     fn analyze_closure(&mut self,
@@ -176,7 +171,12 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
         debug!("analyze_closure(id={:?}, body.id={:?})", id, body.id);
 
         {
-            let mut euv = euv::ExprUseVisitor::new(self, self.fcx);
+            let mut euv =
+                euv::ExprUseVisitor::with_options(self,
+                                                  self.fcx,
+                                                  mc::MemCategorizationOptions {
+                                                      during_closure_kind_inference: true
+                                                  });
             euv.walk_fn(decl, body);
         }
 
@@ -211,10 +211,14 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
             self.fcx.demand_eqtype(span, final_upvar_ty, upvar_ty);
         }
 
-        // Now we must process and remove any deferred resolutions,
-        // since we have a concrete closure kind.
+        // If we are also inferred the closure kind here, update the
+        // main table and process any deferred resolutions.
         let closure_def_id = self.fcx.tcx.map.local_def_id(id);
-        if self.closures_with_inferred_kinds.contains(&id) {
+        if let Some(&kind) = self.temp_closure_kinds.get(&id) {
+            self.fcx.tables.borrow_mut().closure_kinds
+                                        .insert(closure_def_id, kind);
+            debug!("closure_kind({:?}) = {:?}", closure_def_id, kind);
+
             let mut deferred_call_resolutions =
                 self.fcx.remove_deferred_call_resolutions(closure_def_id);
             for deferred_call_resolution in &mut deferred_call_resolutions {
@@ -259,7 +263,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
             })
     }
 
-    fn adjust_upvar_borrow_kind_for_consume(&self,
+    fn adjust_upvar_borrow_kind_for_consume(&mut self,
                                             cmt: mc::cmt<'tcx>,
                                             mode: euv::ConsumeMode)
     {
@@ -350,7 +354,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn adjust_upvar_borrow_kind_for_unique(&self, cmt: mc::cmt<'tcx>) {
+    fn adjust_upvar_borrow_kind_for_unique(&mut self, cmt: mc::cmt<'tcx>) {
         debug!("adjust_upvar_borrow_kind_for_unique(cmt={:?})",
                cmt);
 
@@ -381,7 +385,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn try_adjust_upvar_deref(&self,
+    fn try_adjust_upvar_deref(&mut self,
                               note: &mc::Note,
                               borrow_kind: ty::BorrowKind)
                               -> bool
@@ -430,7 +434,7 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
     /// moving from left to right as needed (but never right to left).
     /// Here the argument `mutbl` is the borrow_kind that is required by
     /// some particular use.
-    fn adjust_upvar_borrow_kind(&self,
+    fn adjust_upvar_borrow_kind(&mut self,
                                 upvar_id: ty::UpvarId,
                                 upvar_capture: &mut ty::UpvarCapture,
                                 kind: ty::BorrowKind) {
@@ -460,36 +464,30 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
         }
     }
 
-    fn adjust_closure_kind(&self,
+    fn adjust_closure_kind(&mut self,
                            closure_id: ast::NodeId,
                            new_kind: ty::ClosureKind) {
         debug!("adjust_closure_kind(closure_id={}, new_kind={:?})",
                closure_id, new_kind);
 
-        if !self.closures_with_inferred_kinds.contains(&closure_id) {
-            return;
-        }
-
-        let closure_def_id = self.fcx.tcx.map.local_def_id(closure_id);
-        let closure_kinds = &mut self.fcx.tables.borrow_mut().closure_kinds;
-        let existing_kind = *closure_kinds.get(&closure_def_id).unwrap();
+        if let Some(&existing_kind) = self.temp_closure_kinds.get(&closure_id) {
+            debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
+                   closure_id, existing_kind, new_kind);
 
-        debug!("adjust_closure_kind: closure_id={}, existing_kind={:?}, new_kind={:?}",
-               closure_id, existing_kind, new_kind);
-
-        match (existing_kind, new_kind) {
-            (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
-            (ty::ClosureKind::FnMut, ty::ClosureKind::Fn) |
-            (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
-            (ty::ClosureKind::FnOnce, _) => {
-                // no change needed
-            }
+            match (existing_kind, new_kind) {
+                (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
+                (ty::ClosureKind::FnMut, ty::ClosureKind::Fn) |
+                (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
+                (ty::ClosureKind::FnOnce, _) => {
+                    // no change needed
+                }
 
-            (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) |
-            (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
-            (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
-                // new kind is stronger than the old kind
-                closure_kinds.insert(closure_def_id, new_kind);
+                (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) |
+                (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
+                (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
+                    // new kind is stronger than the old kind
+                    self.temp_closure_kinds.insert(closure_id, new_kind);
+                }
             }
         }
     }
index e0a34189773b13d87eafd3b604590220ae76492a..d101381e2565c72823332aefc602e721c228bfb8 100644 (file)
@@ -19,9 +19,10 @@ use rustc::ty::{self, Ty, TyCtxt};
 
 use std::collections::HashSet;
 use syntax::ast;
-use syntax::codemap::{Span};
-use syntax::errors::DiagnosticBuilder;
 use syntax::parse::token::keywords;
+use syntax_pos::Span;
+use errors::DiagnosticBuilder;
+
 use rustc::hir::intravisit::{self, Visitor};
 use rustc::hir;
 
index e6500747c05b3e775a2c86de0c6dd2555e2b0a7a..9786132dc537b53c57c0a4b47abde6fe863536ac 100644 (file)
@@ -15,7 +15,6 @@ use self::ResolveReason::*;
 
 use check::FnCtxt;
 use hir::def_id::DefId;
-use hir::pat_util;
 use rustc::ty::{self, Ty, TyCtxt, MethodCall, MethodCallee};
 use rustc::ty::adjustment;
 use rustc::ty::fold::{TypeFolder,TypeFoldable};
@@ -26,10 +25,11 @@ use write_ty_to_tcx;
 use std::cell::Cell;
 
 use syntax::ast;
-use syntax::codemap::{DUMMY_SP, Span};
+use syntax_pos::{DUMMY_SP, Span};
+
 use rustc::hir::print::pat_to_string;
 use rustc::hir::intravisit::{self, Visitor};
-use rustc::hir;
+use rustc::hir::{self, PatKind};
 
 ///////////////////////////////////////////////////////////////////////////
 // Entry point functions
@@ -54,9 +54,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             wbcx.visit_pat(&arg.pat);
 
             // Privacy needs the type for the whole pattern, not just each binding
-            if !pat_util::pat_is_binding(&self.tcx.def_map.borrow(), &arg.pat) {
-                wbcx.visit_node_id(ResolvingPattern(arg.pat.span),
-                                   arg.pat.id);
+            if let PatKind::Binding(..) = arg.pat.node {} else {
+                wbcx.visit_node_id(ResolvingPattern(arg.pat.span), arg.pat.id);
             }
         }
         wbcx.visit_upvar_borrow_map();
@@ -335,13 +334,8 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
         };
 
         //NB(jroesch): We need to match twice to avoid a double borrow which would cause an ICE
-        match new_method {
-            Some(method) => {
-                self.tcx().tables.borrow_mut().method_map.insert(
-                    method_call,
-                    method);
-            }
-            None => {}
+        if let Some(method) = new_method {
+            self.tcx().tables.borrow_mut().method_map.insert(method_call, method);
         }
     }
 
index c3538ace347491b3346cb3d4bd8f18181d87e415..2ee0927f3c8ea44e5d8d945aab7fabf0a1c0bff5 100644 (file)
@@ -13,7 +13,7 @@ use rustc::dep_graph::DepNode;
 use rustc::ty::TyCtxt;
 
 use syntax::ast;
-use syntax::codemap::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP};
 
 use rustc::hir;
 use rustc::hir::intravisit::Visitor;
index 8bee0467f11b3cd7f3688d42f2e59ea1e3f28b16..198e9afd5e12c77f1604d753ba612ba8414ad38c 100644 (file)
@@ -34,7 +34,7 @@ use CrateCtxt;
 use rustc::infer::{self, InferCtxt, TypeOrigin};
 use std::cell::RefCell;
 use std::rc::Rc;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use util::nodemap::{DefIdMap, FnvHashMap};
 use rustc::dep_graph::DepNode;
 use rustc::hir::map as hir_map;
@@ -174,12 +174,9 @@ impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> {
     }
 
     fn add_inherent_impl(&self, base_def_id: DefId, impl_def_id: DefId) {
-        match self.inherent_impls.borrow().get(&base_def_id) {
-            Some(implementation_list) => {
-                implementation_list.borrow_mut().push(impl_def_id);
-                return;
-            }
-            None => {}
+        if let Some(implementation_list) = self.inherent_impls.borrow().get(&base_def_id) {
+            implementation_list.borrow_mut().push(impl_def_id);
+            return;
         }
 
         self.inherent_impls.borrow_mut().insert(
index d9ad03222029d765c27364f8ad00f871c9625008..15d4026254fa57ec1c8f2ee03cb25e99c81916a9 100644 (file)
@@ -16,7 +16,7 @@ use hir::def_id::DefId;
 use rustc::traits;
 use rustc::ty::{self, TyCtxt};
 use syntax::ast;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 use rustc::dep_graph::DepNode;
 use rustc::hir::intravisit;
 use rustc::hir;
index 5896a34b0d1603b953b0281c414d7d80bdff1e96..2c33d1a81556eb9f28cae32520627a8aefa6d822 100644 (file)
@@ -64,7 +64,6 @@ use hir::def::Def;
 use hir::def_id::DefId;
 use constrained_type_params as ctp;
 use middle::lang_items::SizedTraitLangItem;
-use middle::resolve_lifetime;
 use middle::const_val::ConstVal;
 use rustc_const_eval::EvalHint::UncheckedExprHint;
 use rustc_const_eval::{eval_const_expr_partial, ConstEvalErr};
@@ -89,9 +88,10 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::rc::Rc;
 
 use syntax::{abi, ast, attr};
-use syntax::codemap::Span;
 use syntax::parse::token::keywords;
 use syntax::ptr::P;
+use syntax_pos::Span;
+
 use rustc::hir::{self, PatKind};
 use rustc::hir::intravisit;
 use rustc::hir::print as pprust;
@@ -533,17 +533,13 @@ fn is_param<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                       -> bool
 {
     if let hir::TyPath(None, _) = ast_ty.node {
-        let path_res = *tcx.def_map.borrow().get(&ast_ty.id).unwrap();
+        let path_res = tcx.expect_resolution(ast_ty.id);
         match path_res.base_def {
-            Def::SelfTy(Some(def_id), None) => {
-                path_res.depth == 0 && def_id == tcx.map.local_def_id(param_id)
-            }
-            Def::TyParam(_, _, def_id, _) => {
-                path_res.depth == 0 && def_id == tcx.map.local_def_id(param_id)
-            }
-            _ => {
-                false
+            Def::SelfTy(Some(def_id), None) |
+            Def::TyParam(_, _, def_id, _) if path_res.depth == 0 => {
+                def_id == tcx.map.local_def_id(param_id)
             }
+            _ => false
         }
     } else {
         false
@@ -1720,7 +1716,7 @@ fn add_unsized_bound<'tcx>(astconv: &AstConv<'tcx, 'tcx>,
     match unbound {
         Some(ref tpb) => {
             // FIXME(#8559) currently requires the unbound to be built-in.
-            let trait_def_id = tcx.trait_ref_to_def_id(tpb);
+            let trait_def_id = tcx.expect_def(tpb.ref_id).def_id();
             match kind_id {
                 Ok(kind_id) if trait_def_id != kind_id => {
                     tcx.sess.span_warn(span,
@@ -1745,14 +1741,16 @@ fn add_unsized_bound<'tcx>(astconv: &AstConv<'tcx, 'tcx>,
 /// the lifetimes that are declared. For fns or methods, we have to
 /// screen out those that do not appear in any where-clauses etc using
 /// `resolve_lifetime::early_bound_lifetimes`.
-fn early_bound_lifetimes_from_generics(space: ParamSpace,
-                                       ast_generics: &hir::Generics)
-                                       -> Vec<hir::LifetimeDef>
+fn early_bound_lifetimes_from_generics<'a, 'tcx, 'hir>(
+    ccx: &CrateCtxt<'a, 'tcx>,
+    ast_generics: &'hir hir::Generics)
+    -> Vec<&'hir hir::LifetimeDef>
 {
-    match space {
-        SelfSpace | TypeSpace => ast_generics.lifetimes.to_vec(),
-        FnSpace => resolve_lifetime::early_bound_lifetimes(ast_generics),
-    }
+    ast_generics
+        .lifetimes
+        .iter()
+        .filter(|l| !ccx.tcx.named_region_map.late_bound.contains_key(&l.lifetime.id))
+        .collect()
 }
 
 fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
@@ -1781,7 +1779,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
     // Collect the region predicates that were declared inline as
     // well. In the case of parameters declared on a fn or method, we
     // have to be careful to only iterate over early-bound regions.
-    let early_lifetimes = early_bound_lifetimes_from_generics(space, ast_generics);
+    let early_lifetimes = early_bound_lifetimes_from_generics(ccx, ast_generics);
     for (index, param) in early_lifetimes.iter().enumerate() {
         let index = index as u32;
         let region =
@@ -1864,7 +1862,7 @@ fn ty_generics<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
     let tcx = ccx.tcx;
     let mut result = base_generics.clone();
 
-    let early_lifetimes = early_bound_lifetimes_from_generics(space, ast_generics);
+    let early_lifetimes = early_bound_lifetimes_from_generics(ccx, ast_generics);
     for (i, l) in early_lifetimes.iter().enumerate() {
         let bounds = l.bounds.iter()
                              .map(|l| ast_region_to_region(tcx, l))
@@ -2152,12 +2150,9 @@ fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
 {
     for i in &decl.inputs {
         match i.pat.node {
-            PatKind::Ident(_, _, _) => (),
-            PatKind::Wild => (),
-            _ => {
-                span_err!(ccx.tcx.sess, i.pat.span, E0130,
-                          "patterns aren't allowed in foreign function declarations");
-            }
+            PatKind::Binding(..) | PatKind::Wild => {}
+            _ => span_err!(ccx.tcx.sess, i.pat.span, E0130,
+                           "patterns aren't allowed in foreign function declarations")
         }
     }
 
index 45aec9558feaf2415d09defdc1eb63a01af2feb5..cdac66a2272379f43011c6ed51162afc3d7a5338 100644 (file)
@@ -62,31 +62,6 @@ Check how many fields the enum was declared with and ensure that your pattern
 uses the same number.
 "##,
 
-E0024: r##"
-This error indicates that a pattern attempted to extract the fields of an enum
-variant with no fields. Here's a tiny example of this error:
-
-```compile_fail
-// This enum has two variants.
-enum Number {
-    // This variant has no fields.
-    Zero,
-    // This variant has one field.
-    One(u32)
-}
-
-// Assuming x is a Number we can pattern match on its contents.
-match x {
-    Number::Zero(inside) => {},
-    Number::One(inside) => {},
-}
-```
-
-The pattern match `Zero(inside)` is incorrect because the `Zero` variant
-contains no fields, yet the `inside` name attempts to bind the first field of
-the enum.
-"##,
-
 E0025: r##"
 Each field of a struct can only be bound once in a pattern. Erroneous code
 example:
@@ -956,7 +931,7 @@ first instance of `Foo` could be made to initialize another instance!
 
 Here's an example of a struct that has this problem:
 
-```compile_fail
+```ignore
 struct Foo { x: Box<Foo> } // error
 ```
 
@@ -977,19 +952,19 @@ are generic.
 
 This will cause an error:
 
-```compile_fail
-#![feature(simd)]
+```ignore
+#![feature(repr_simd)]
 
-#[simd]
+#[repr(simd)]
 struct Bad<T>(T, T, T);
 ```
 
 This will not:
 
 ```
-#![feature(simd)]
+#![feature(repr_simd)]
 
-#[simd]
+#[repr(simd)]
 struct Good(u32, u32, u32);
 ```
 "##,
@@ -1026,18 +1001,18 @@ will trigger this error.
 This will cause an error:
 
 ```compile_fail
-#![feature(simd)]
+#![feature(repr_simd)]
 
-#[simd]
+#[repr(simd)]
 struct Bad(u16, u32, u32);
 ```
 
 This will not:
 
 ```
-#![feature(simd)]
+#![feature(repr_simd)]
 
-#[simd]
+#[repr(simd)]
 struct Good(u32, u32, u32);
 ```
 "##,
@@ -1049,18 +1024,18 @@ must be machine types so SIMD operations can be applied to them.
 This will cause an error:
 
 ```compile_fail
-#![feature(simd)]
+#![feature(repr_simd)]
 
-#[simd]
+#[repr(simd)]
 struct Bad(String);
 ```
 
 This will not:
 
 ```
-#![feature(simd)]
+#![feature(repr_simd)]
 
-#[simd]
+#[repr(simd)]
 struct Good(u32, u32, u32);
 ```
 "##,
@@ -1168,7 +1143,7 @@ for an explicit choice of the discriminant type. In either cases, the
 discriminant values must fall within a valid range for the expected type;
 otherwise this error is raised. For example:
 
-```compile_fail
+```ignore
 #[repr(u8)]
 enum Thing {
     A = 1024,
@@ -1179,7 +1154,7 @@ enum Thing {
 Here, 1024 lies outside the valid range for `u8`, so the discriminant for `A` is
 invalid. Here is another, more subtle example which depends on target word size:
 
-```compile_fail
+```ignore
 enum DependsOnPointerSize {
     A = 1 << 32
 }
@@ -1193,12 +1168,32 @@ discriminant values so that they fit within the existing type.
 "##,
 
 E0084: r##"
+An unsupported representation was attempted on a zero-variant enum.
+
+Erroneous code example:
+
+```compile_fail
+#[repr(i32)]
+enum NightsWatch {} // error: unsupported representation for zero-variant enum
+```
+
 It is impossible to define an integer type to be used to represent zero-variant
 enum values because there are no zero-variant enum values. There is no way to
-construct an instance of the following type using only safe code:
+construct an instance of the following type using only safe code. So you have
+two solutions. Either you add variants in your enum:
+
+```
+#[repr(i32)]
+enum NightsWatch {
+    JonSnow,
+    Commander,
+}
+```
+
+or you remove the integer represention of your enum:
 
 ```
-enum Empty {}
+enum NightsWatch {}
 ```
 "##,
 
@@ -1869,12 +1864,35 @@ fn main<T>() { // error: main function is not allowed to have type parameters
 "##,
 
 E0132: r##"
+A function with the `start` attribute was declared with type parameters.
+
+Erroneous code example:
+
+```compile_fail
+#![feature(start)]
+
+#[start]
+fn f<T>() {}
+```
+
 It is not possible to declare type parameters on a function that has the `start`
-attribute. Such a function must have the following type signature:
+attribute. Such a function must have the following type signature (for more
+information: http://doc.rust-lang.org/stable/book/no-stdlib.html):
 
 ```ignore
 fn(isize, *const *const u8) -> isize;
 ```
+
+Example:
+
+```
+#![feature(start)]
+
+#[start]
+fn my_start(argc: isize, argv: *const *const u8) -> isize {
+    0
+}
+```
 "##,
 
 E0163: r##"
@@ -1972,6 +1990,89 @@ To learn more about traits, take a look at the Book:
 https://doc.rust-lang.org/book/traits.html
 "##,
 
+E0174: r##"
+This error occurs because of the explicit use of unboxed closure methods
+that are an experimental feature in current Rust version.
+
+Example of erroneous code:
+
+```compile_fail
+fn foo<F: Fn(&str)>(mut f: F) {
+    f.call(("call",));
+    // error: explicit use of unboxed closure method `call`
+    f.call_mut(("call_mut",));
+    // error: explicit use of unboxed closure method `call_mut`
+    f.call_once(("call_once",));
+    // error: explicit use of unboxed closure method `call_once`
+}
+
+fn bar(text: &str) {
+    println!("Calling {} it works!", text);
+}
+
+fn main() {
+    foo(bar);
+}
+```
+
+Rust's implementation of closures is a bit different than other languages.
+They are effectively syntax sugar for traits `Fn`, `FnMut` and `FnOnce`.
+To understand better how the closures are implemented see here:
+https://doc.rust-lang.org/book/closures.html#closure-implementation
+
+To fix this you can call them using parenthesis, like this: `foo()`.
+When you execute the closure with parenthesis, under the hood you are executing
+the method `call`, `call_mut` or `call_once`. However, using them explicitly is
+currently an experimental feature.
+
+Example of an implicit call:
+
+```
+fn foo<F: Fn(&str)>(f: F) {
+    f("using ()"); // Calling using () it works!
+}
+
+fn bar(text: &str) {
+    println!("Calling {} it works!", text);
+}
+
+fn main() {
+    foo(bar);
+}
+```
+
+To enable the explicit calls you need to add `#![feature(unboxed_closures)]`.
+
+This feature is still unstable so you will also need to add
+`#![feature(fn_traits)]`.
+More details about this issue here:
+https://github.com/rust-lang/rust/issues/29625
+
+Example of use:
+
+```
+#![feature(fn_traits)]
+#![feature(unboxed_closures)]
+
+fn foo<F: Fn(&str)>(mut f: F) {
+    f.call(("call",)); // Calling 'call' it works!
+    f.call_mut(("call_mut",)); // Calling 'call_mut' it works!
+    f.call_once(("call_once",)); // Calling 'call_once' it works!
+}
+
+fn bar(text: &str) {
+    println!("Calling '{}' it works!", text);
+}
+
+fn main() {
+    foo(bar);
+}
+```
+
+To see more about closures take a look here:
+https://doc.rust-lang.org/book/closures.html`
+"##,
+
 E0178: r##"
 In types, the `+` type operator has low precedence, so it is often necessary
 to use parentheses.
@@ -2022,6 +2123,7 @@ impl Foo for Bar {
     // the trait
     fn foo(&self) {}
 }
+```
 "##,
 
 E0186: r##"
@@ -2081,7 +2183,7 @@ E0193: r##"
 `where` clauses must use generic type parameters: it does not make sense to use
 them otherwise. An example causing this error:
 
-```compile_fail
+```ignore
 trait Foo {
     fn bar(&self);
 }
@@ -2387,39 +2489,135 @@ impl Copy for &'static Bar { } // error
 "##,
 
 E0207: r##"
-You declared an unused type parameter when implementing a trait on an object.
-Erroneous code example:
+Any type parameter or lifetime parameter of an `impl` must meet at least one of
+the following criteria:
+
+ - it appears in the self type of the impl
+ - for a trait impl, it appears in the trait reference
+ - it is bound as an associated type
+
+### Error example 1
+
+Suppose we have a struct `Foo` and we would like to define some methods for it.
+The following definition leads to a compiler error:
 
 ```compile_fail
-trait MyTrait {
-    fn get(&self) -> usize;
+struct Foo;
+
+impl<T: Default> Foo {
+// error: the type parameter `T` is not constrained by the impl trait, self
+// type, or predicates [E0207]
+    fn get(&self) -> T {
+        <T as Default>::default()
+    }
 }
+```
+
+The problem is that the parameter `T` does not appear in the self type (`Foo`)
+of the impl. In this case, we can fix the error by moving the type parameter
+from the `impl` to the method `get`:
 
+
+```
 struct Foo;
 
-impl<T> MyTrait for Foo {
-    fn get(&self) -> usize {
-        0
+// Move the type parameter from the impl to the method
+impl Foo {
+    fn get<T: Default>(&self) -> T {
+        <T as Default>::default()
     }
 }
 ```
 
-Please check your object definition and remove unused type
-parameter(s). Example:
+### Error example 2
+
+As another example, suppose we have a `Maker` trait and want to establish a
+type `FooMaker` that makes `Foo`s:
+
+```compile_fail
+trait Maker {
+    type Item;
+    fn make(&mut self) -> Self::Item;
+}
+
+struct Foo<T> {
+    foo: T
+}
 
+struct FooMaker;
+
+impl<T: Default> Maker for FooMaker {
+// error: the type parameter `T` is not constrained by the impl trait, self
+// type, or predicates [E0207]
+    type Item = Foo<T>;
+
+    fn make(&mut self) -> Foo<T> {
+        Foo { foo: <T as Default>::default() }
+    }
+}
 ```
-trait MyTrait {
-    fn get(&self) -> usize;
+
+This fails to compile because `T` does not appear in the trait or in the
+implementing type.
+
+One way to work around this is to introduce a phantom type parameter into
+`FooMaker`, like so:
+
+```
+use std::marker::PhantomData;
+
+trait Maker {
+    type Item;
+    fn make(&mut self) -> Self::Item;
 }
 
-struct Foo;
+struct Foo<T> {
+    foo: T
+}
 
-impl MyTrait for Foo {
-    fn get(&self) -> usize {
-        0
+// Add a type parameter to `FooMaker`
+struct FooMaker<T> {
+    phantom: PhantomData<T>,
+}
+
+impl<T: Default> Maker for FooMaker<T> {
+    type Item = Foo<T>;
+
+    fn make(&mut self) -> Foo<T> {
+        Foo {
+            foo: <T as Default>::default(),
+        }
+    }
+}
+```
+
+Another way is to do away with the associated type in `Maker` and use an input
+type parameter instead:
+
+```
+// Use a type parameter instead of an associated type here
+trait Maker<Item> {
+    fn make(&mut self) -> Item;
+}
+
+struct Foo<T> {
+    foo: T
+}
+
+struct FooMaker;
+
+impl<T: Default> Maker<Foo<T>> for FooMaker {
+    fn make(&mut self) -> Foo<T> {
+        Foo { foo: <T as Default>::default() }
     }
 }
 ```
+
+### Additional information
+
+For more information, please see [RFC 447].
+
+[RFC 447]: https://github.com/rust-lang/rfcs/blob/master/text/0447-no-unused-impl-parameters.md
 "##,
 
 E0210: r##"
@@ -2589,23 +2787,42 @@ You used an associated type which isn't defined in the trait.
 Erroneous code example:
 
 ```compile_fail
-trait Trait {
+trait T1 {
     type Bar;
 }
 
-type Foo = Trait<F=i32>; // error: associated type `F` not found for
-                         //        `Trait`
+type Foo = T1<F=i32>; // error: associated type `F` not found for `T1`
+
+// or:
+
+trait T2 {
+    type Bar;
+
+    // error: Baz is used but not declared
+    fn return_bool(&self, &Self::Bar, &Self::Baz) -> bool;
+}
 ```
 
-Please verify you used the right trait or you didn't misspell the
+Make sure that you have defined the associated type in the trait body.
+Also, verify that you used the right trait or you didn't misspell the
 associated type name. Example:
 
 ```
-trait Trait {
+trait T1 {
     type Bar;
 }
 
-type Foo = Trait<Bar=i32>; // ok!
+type Foo = T1<Bar=i32>; // ok!
+
+// or:
+
+trait T2 {
+    type Bar;
+    type Baz; // we declare `Baz` in our trait.
+
+    // and now we can use it here:
+    fn return_bool(&self, &Self::Bar, &Self::Baz) -> bool;
+}
 ```
 "##,
 
@@ -3049,7 +3266,7 @@ An attempt was made to access an associated constant through either a generic
 type parameter or `Self`. This is not supported yet. An example causing this
 error is shown below:
 
-```compile_fail
+```ignore
 #![feature(associated_consts)]
 
 trait Foo {
@@ -3236,6 +3453,7 @@ The maximum value of an enum was reached, so it cannot be automatically
 set in the next enum value. Erroneous code example:
 
 ```compile_fail
+#[deny(overflowing_literals)]
 enum Foo {
     X = 0x7fffffffffffffff,
     Y, // error: enum discriminant overflowed on value after
@@ -3891,7 +4109,6 @@ register_diagnostics! {
     E0167,
 //  E0168,
 //  E0173, // manual implementations of unboxed closure traits are experimental
-    E0174, // explicit use of unboxed closure methods are experimental
     E0182,
     E0183,
 //  E0187, // can't infer the kind of the closure
@@ -3927,7 +4144,7 @@ register_diagnostics! {
 //  E0239, // `next` method of `Iterator` trait has unexpected type
 //  E0240,
 //  E0241,
-    E0242, // internal error looking up a definition
+//  E0242,
     E0245, // not a trait
 //  E0246, // invalid recursive type
 //  E0247,
@@ -3941,5 +4158,8 @@ register_diagnostics! {
            // type `{}` was overridden
     E0436, // functional record update requires a struct
     E0513, // no type for local variable ..
-    E0521  // redundant default implementations of trait
+    E0521, // redundant default implementations of trait
+    E0527, // expected {} elements, found {}
+    E0528, // expected at least {} elements, found {}
+    E0529, // slice pattern expects array or slice, not `{}`
 }
index 0b23951db36612b53a35541fae408c6ae322f210..84452589dfda389a9bc7e708c55dc16f9f497e9f 100644 (file)
@@ -76,7 +76,6 @@ This API is completely unstable and subject to change.
 
 #![feature(box_patterns)]
 #![feature(box_syntax)]
-#![feature(iter_arith)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(rustc_private)]
@@ -85,6 +84,7 @@ This API is completely unstable and subject to change.
 
 #[macro_use] extern crate log;
 #[macro_use] extern crate syntax;
+extern crate syntax_pos;
 
 extern crate arena;
 extern crate fmt_macros;
@@ -93,6 +93,7 @@ extern crate rustc_platform_intrinsics as intrinsics;
 extern crate rustc_back;
 extern crate rustc_const_math;
 extern crate rustc_const_eval;
+extern crate rustc_errors as errors;
 
 pub use rustc::dep_graph;
 pub use rustc::hir;
@@ -103,7 +104,6 @@ pub use rustc::util;
 
 use dep_graph::DepNode;
 use hir::map as hir_map;
-use hir::def::Def;
 use rustc::infer::TypeOrigin;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
@@ -111,9 +111,9 @@ use rustc::traits::ProjectionMode;
 use session::{config, CompileResult};
 use util::common::time;
 
-use syntax::codemap::Span;
 use syntax::ast;
 use syntax::abi::Abi;
+use syntax_pos::Span;
 
 use std::cell::RefCell;
 use util::nodemap::NodeMap;
@@ -176,15 +176,6 @@ fn write_substs_to_tcx<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
     }
 }
 
-fn lookup_full_def(tcx: TyCtxt, sp: Span, id: ast::NodeId) -> Def {
-    match tcx.def_map.borrow().get(&id) {
-        Some(x) => x.full_def(),
-        None => {
-            span_fatal!(tcx.sess, sp, E0242, "internal error looking up a definition")
-        }
-    }
-}
-
 fn require_c_abi_if_variadic(tcx: TyCtxt,
                              decl: &hir::FnDecl,
                              abi: Abi,
@@ -321,14 +312,13 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
 fn check_for_entry_fn(ccx: &CrateCtxt) {
     let tcx = ccx.tcx;
     let _task = tcx.dep_graph.in_task(DepNode::CheckEntryFn);
-    match *tcx.sess.entry_fn.borrow() {
-        Some((id, sp)) => match tcx.sess.entry_type.get() {
+    if let Some((id, sp)) = *tcx.sess.entry_fn.borrow() {
+        match tcx.sess.entry_type.get() {
             Some(config::EntryMain) => check_main_fn_ty(ccx, id, sp),
             Some(config::EntryStart) => check_start_fn_ty(ccx, id, sp),
             Some(config::EntryNone) => {}
             None => bug!("entry function without a type")
-        },
-        None => {}
+        }
     }
 }
 
index 793dba1e3f5f8bac6693f5eac7f4af2eb9f7ba1d..336a61708683e0d6b05bea15cc3b490a54d67632 100644 (file)
@@ -12,7 +12,7 @@
 use rustc::ty;
 
 use std::cell::Cell;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 #[derive(Clone)]
 pub struct ElisionFailureInfo {
index a532f9744f49813346a6c7a686540a1b1fc4f729..01a310da25ddd26a364f0526a51df43c7e8f844f 100644 (file)
@@ -144,7 +144,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
     fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId {
         let tcx = self.terms_cx.tcx;
         assert!(is_lifetime(&tcx.map, param_id));
-        match tcx.named_region_map.get(&param_id) {
+        match tcx.named_region_map.defs.get(&param_id) {
             Some(&rl::DefEarlyBoundRegion(_, _, lifetime_decl_id))
                 => lifetime_decl_id,
             Some(_) => bug!("should not encounter non early-bound cases"),
@@ -520,7 +520,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
             }
 
             ty::ReFree(..) | ty::ReScope(..) | ty::ReVar(..) |
-            ty::ReSkolemized(..) | ty::ReEmpty => {
+            ty::ReSkolemized(..) | ty::ReEmpty | ty::ReErased => {
                 // We don't expect to see anything but 'static or bound
                 // regions when visiting member types or method types.
                 bug!("unexpected region encountered in variance \
index 863cada5b88090f09a9eb434c37b93add48ca19d..f570375de5ea156ed8828fc75d78880b7031dfb0 100644 (file)
 
 use core::char::CharExt as C;
 use core::fmt;
-use tables::{derived_property, property, general_category, conversions};
+use tables::{conversions, derived_property, general_category, property};
 
 // stable reexports
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use core::char::{MAX, from_u32, from_u32_unchecked, from_digit};
+pub use core::char::{MAX, from_digit, from_u32, from_u32_unchecked};
 #[stable(feature = "rust1", since = "1.0.0")]
-pub use core::char::{EscapeUnicode, EscapeDefault, EncodeUtf8, EncodeUtf16};
+pub use core::char::{EncodeUtf16, EncodeUtf8, EscapeDefault, EscapeUnicode};
 
 // unstable reexports
 #[unstable(feature = "unicode", issue = "27783")]
@@ -668,10 +668,13 @@ impl char {
     /// Basic usage:
     ///
     /// ```
-    /// assert_eq!('C'.to_lowercase().next(), Some('c'));
+    /// assert_eq!('C'.to_lowercase().collect::<String>(), "c");
+    ///
+    /// // Sometimes the result is more than one character:
+    /// assert_eq!('İ'.to_lowercase().collect::<String>(), "i\u{307}");
     ///
     /// // Japanese scripts do not have case, and so:
-    /// assert_eq!('山'.to_lowercase().next(), Some('山'));
+    /// assert_eq!('山'.to_lowercase().collect::<String>(), "山");
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
@@ -702,10 +705,13 @@ impl char {
     /// Basic usage:
     ///
     /// ```
-    /// assert_eq!('c'.to_uppercase().next(), Some('C'));
+    /// assert_eq!('c'.to_uppercase().collect::<String>(), "C");
+    ///
+    /// // Sometimes the result is more than one character:
+    /// assert_eq!('ß'.to_uppercase().collect::<String>(), "SS");
     ///
     /// // Japanese does not have case, and so:
-    /// assert_eq!('山'.to_uppercase().next(), Some('山'));
+    /// assert_eq!('山'.to_uppercase().collect::<String>(), "山");
     /// ```
     ///
     /// In Turkish, the equivalent of 'i' in Latin has five forms instead of two:
@@ -716,17 +722,17 @@ impl char {
     /// Note that the lowercase dotted 'i' is the same as the Latin. Therefore:
     ///
     /// ```
-    /// let upper_i = 'i'.to_uppercase().next();
+    /// let upper_i: String = 'i'.to_uppercase().collect();
     /// ```
     ///
     /// The value of `upper_i` here relies on the language of the text: if we're
-    /// in `en-US`, it should be `Some('I')`, but if we're in `tr_TR`, it should
-    /// be `Some('İ')`. `to_uppercase()` does not take this into account, and so:
+    /// in `en-US`, it should be `"I"`, but if we're in `tr_TR`, it should
+    /// be `"İ"`. `to_uppercase()` does not take this into account, and so:
     ///
     /// ```
-    /// let upper_i = 'i'.to_uppercase().next();
+    /// let upper_i: String = 'i'.to_uppercase().collect();
     ///
-    /// assert_eq!(Some('I'), upper_i);
+    /// assert_eq!(upper_i, "I");
     /// ```
     ///
     /// holds across languages.
@@ -808,16 +814,18 @@ pub fn decode_utf16<I: IntoIterator<Item = u16>>(iter: I) -> DecodeUtf16<I::Into
 }
 
 #[stable(feature = "decode_utf16", since = "1.9.0")]
-impl<I: Iterator<Item=u16>> Iterator for DecodeUtf16<I> {
+impl<I: Iterator<Item = u16>> Iterator for DecodeUtf16<I> {
     type Item = Result<char, DecodeUtf16Error>;
 
     fn next(&mut self) -> Option<Result<char, DecodeUtf16Error>> {
         let u = match self.buf.take() {
             Some(buf) => buf,
-            None => match self.iter.next() {
-                Some(u) => u,
-                None => return None,
-            },
+            None => {
+                match self.iter.next() {
+                    Some(u) => u,
+                    None => return None,
+                }
+            }
         };
 
         if u < 0xD800 || 0xDFFF < u {
index 2f7f724e6af8fcfe606630f7359829d384c00da1..b03d7ee79e89c3d8d6f4777e6da40792f023cad6 100644 (file)
@@ -43,14 +43,14 @@ pub mod char;
 
 #[allow(deprecated)]
 pub mod str {
-    pub use u_str::{UnicodeStr, SplitWhitespace};
-    pub use u_str::{utf8_char_width, is_utf16};
-    pub use u_str::{Utf16Encoder};
+    pub use u_str::{SplitWhitespace, UnicodeStr};
+    pub use u_str::{is_utf16, utf8_char_width};
+    pub use u_str::Utf16Encoder;
 }
 
 // For use in libcollections, not re-exported in libstd.
 pub mod derived_property {
-    pub use tables::derived_property::{Cased, Case_Ignorable};
+    pub use tables::derived_property::{Case_Ignorable, Cased};
 }
 
 // For use in libsyntax
index 18734a66871f6fd4541cb35706057adc8d18710f..0bac44b837ff247dc80743994b5fe58daff5d3d9 100644 (file)
@@ -144,7 +144,9 @@ impl<I> Utf16Encoder<I> {
     }
 }
 
-impl<I> Iterator for Utf16Encoder<I> where I: Iterator<Item=char> {
+impl<I> Iterator for Utf16Encoder<I>
+    where I: Iterator<Item = char>
+{
     type Item = u16;
 
     #[inline]
index 9d76dd81e5f76041ff3404539ad98f8aad599a27..cf87aabdfdb5e6c572a637569d6ed8176da35cc6 100644 (file)
@@ -15,12 +15,14 @@ rustc = { path = "../librustc" }
 rustc_back = { path = "../librustc_back" }
 rustc_const_eval = { path = "../librustc_const_eval" }
 rustc_driver = { path = "../librustc_driver" }
+rustc_errors = { path = "../librustc_errors" }
 rustc_lint = { path = "../librustc_lint" }
 rustc_metadata = { path = "../librustc_metadata" }
 rustc_resolve = { path = "../librustc_resolve" }
 rustc_trans = { path = "../librustc_trans" }
 serialize = { path = "../libserialize" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
 log = { path = "../liblog" }
 
 [build-dependencies]
index f5d54123f37b52a018bee19ad27259ba87b8fba6..8894b9732fe89250f32f7ead6c3ab0de0cf60b4d 100644 (file)
@@ -22,6 +22,7 @@ use rustc::hir::def_id::DefId;
 use rustc::hir::print as pprust;
 use rustc::ty::{self, TyCtxt};
 use rustc::ty::subst;
+use rustc::util::common::slice_pat;
 
 use rustc_const_eval::lookup_const_by_id;
 
@@ -49,8 +50,8 @@ pub fn try_inline(cx: &DocContext, id: ast::NodeId, into: Option<ast::Name>)
         Some(tcx) => tcx,
         None => return None,
     };
-    let def = match tcx.def_map.borrow().get(&id) {
-        Some(d) => d.full_def(),
+    let def = match tcx.expect_def_or_none(id) {
+        Some(def) => def,
         None => return None,
     };
     let did = def.def_id();
@@ -142,8 +143,14 @@ pub fn load_attrs<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>,
 pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
     if let Some(tcx) = cx.tcx_opt() {
         let crate_name = tcx.sess.cstore.crate_name(did.krate).to_string();
-        let relative = tcx.def_path(did).data.into_iter().map(|elem| {
-            elem.data.to_string()
+        let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| {
+            // extern blocks have an empty name
+            let s = elem.data.to_string();
+            if !s.is_empty() {
+                Some(s)
+            } else {
+                None
+            }
         });
         let fqn = once(crate_name).chain(relative).collect();
         cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
@@ -197,10 +204,10 @@ fn build_struct<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let variant = tcx.lookup_adt_def(did).struct_variant();
 
     clean::Struct {
-        struct_type: match &*variant.fields {
-            [] => doctree::Unit,
-            [_] if variant.kind == ty::VariantKind::Tuple => doctree::Newtype,
-            [..] if variant.kind == ty::VariantKind::Tuple => doctree::Tuple,
+        struct_type: match slice_pat(&&*variant.fields) {
+            &[] => doctree::Unit,
+            &[_] if variant.kind == ty::VariantKind::Tuple => doctree::Newtype,
+            &[..] if variant.kind == ty::VariantKind::Tuple => doctree::Tuple,
             _ => doctree::Plain,
         },
         generics: (&t.generics, &predicates, subst::TypeSpace).clean(cx),
@@ -425,7 +432,6 @@ pub fn build_impl<'a, 'tcx>(cx: &DocContext,
     ret.push(clean::Item {
         inner: clean::ImplItem(clean::Impl {
             unsafety: hir::Unsafety::Normal, // FIXME: this should be decoded
-            derived: clean::detect_derived(&attrs),
             provided_trait_methods: provided,
             trait_: trait_,
             for_: for_,
index ca138168b2954207f5f4ec0af0b41d520214df11..139dedeb70fdf061cee3dc7dc1ee9277836134a3 100644 (file)
@@ -24,15 +24,14 @@ pub use self::SelfTy::*;
 pub use self::FunctionRetTy::*;
 pub use self::Visibility::*;
 
-use syntax;
 use syntax::abi::Abi;
 use syntax::ast;
 use syntax::attr;
 use syntax::attr::{AttributeMethods, AttrMetaMethods};
-use syntax::codemap;
-use syntax::codemap::{DUMMY_SP, Pos, Spanned};
+use syntax::codemap::Spanned;
 use syntax::parse::token::{self, InternedString, keywords};
 use syntax::ptr::P;
+use syntax_pos::{self, DUMMY_SP, Pos};
 
 use rustc_trans::back::link;
 use rustc::middle::cstore;
@@ -533,7 +532,7 @@ impl attr::AttrMetaMethods for Attribute {
         }
     }
     fn meta_item_list<'a>(&'a self) -> Option<&'a [P<ast::MetaItem>]> { None }
-    fn span(&self) -> codemap::Span { unimplemented!() }
+    fn span(&self) -> syntax_pos::Span { unimplemented!() }
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)]
@@ -819,7 +818,7 @@ impl Clean<Option<Lifetime>> for ty::Region {
     fn clean(&self, cx: &DocContext) -> Option<Lifetime> {
         match *self {
             ty::ReStatic => Some(Lifetime::statik()),
-            ty::ReLateBound(_, ty::BrNamed(_, name)) => Some(Lifetime(name.to_string())),
+            ty::ReLateBound(_, ty::BrNamed(_, name, _)) => Some(Lifetime(name.to_string())),
             ty::ReEarlyBound(ref data) => Some(Lifetime(data.name.clean(cx))),
 
             ty::ReLateBound(..) |
@@ -827,7 +826,8 @@ impl Clean<Option<Lifetime>> for ty::Region {
             ty::ReScope(..) |
             ty::ReVar(..) |
             ty::ReSkolemized(..) |
-            ty::ReEmpty => None
+            ty::ReEmpty |
+            ty::ReErased => None
         }
     }
 }
@@ -1976,7 +1976,7 @@ impl Span {
     }
 }
 
-impl Clean<Span> for syntax::codemap::Span {
+impl Clean<Span> for syntax_pos::Span {
     fn clean(&self, cx: &DocContext) -> Span {
         if *self == DUMMY_SP {
             return Span::empty();
@@ -2239,14 +2239,9 @@ pub struct Impl {
     pub trait_: Option<Type>,
     pub for_: Type,
     pub items: Vec<Item>,
-    pub derived: bool,
     pub polarity: Option<ImplPolarity>,
 }
 
-fn detect_derived<M: AttrMetaMethods>(attrs: &[M]) -> bool {
-    attr::contains_name(attrs, "automatically_derived")
-}
-
 impl Clean<Vec<Item>> for doctree::Impl {
     fn clean(&self, cx: &DocContext) -> Vec<Item> {
         let mut ret = Vec::new();
@@ -2283,7 +2278,6 @@ impl Clean<Vec<Item>> for doctree::Impl {
                 trait_: trait_,
                 for_: self.for_.clean(cx),
                 items: items,
-                derived: detect_derived(&self.attrs),
                 polarity: Some(self.polarity.clean(cx)),
             }),
         });
@@ -2388,9 +2382,11 @@ impl Clean<Vec<Item>> for doctree::Import {
         // We consider inlining the documentation of `pub use` statements, but we
         // forcefully don't inline if this is not public or if the
         // #[doc(no_inline)] attribute is present.
+        // Don't inline doc(hidden) imports so they can be stripped at a later stage.
         let denied = self.vis != hir::Public || self.attrs.iter().any(|a| {
             &a.name()[..] == "doc" && match a.meta_item_list() {
-                Some(l) => attr::contains_name(l, "no_inline"),
+                Some(l) => attr::contains_name(l, "no_inline") ||
+                           attr::contains_name(l, "hidden"),
                 None => false,
             }
         });
@@ -2540,7 +2536,7 @@ trait ToSource {
     fn to_src(&self, cx: &DocContext) -> String;
 }
 
-impl ToSource for syntax::codemap::Span {
+impl ToSource for syntax_pos::Span {
     fn to_src(&self, cx: &DocContext) -> String {
         debug!("converting span {:?} to snippet", self.clean(cx));
         let sn = match cx.sess().codemap().span_to_snippet(*self) {
@@ -2578,8 +2574,8 @@ fn name_from_pat(p: &hir::Pat) -> String {
 
     match p.node {
         PatKind::Wild => "_".to_string(),
-        PatKind::Ident(_, ref p, _) => p.node.to_string(),
-        PatKind::TupleStruct(ref p, _) | PatKind::Path(ref p) => path_to_string(p),
+        PatKind::Binding(_, ref p, _) => p.node.to_string(),
+        PatKind::TupleStruct(ref p, _, _) | PatKind::Path(ref p) => path_to_string(p),
         PatKind::QPath(..) => panic!("tried to get argument name from PatKind::QPath, \
                                 which is not allowed in function arguments"),
         PatKind::Struct(ref name, ref fields, etc) => {
@@ -2590,7 +2586,7 @@ fn name_from_pat(p: &hir::Pat) -> String {
                 if etc { ", ..." } else { "" }
             )
         },
-        PatKind::Tup(ref elts) => format!("({})", elts.iter().map(|p| name_from_pat(&**p))
+        PatKind::Tuple(ref elts, _) => format!("({})", elts.iter().map(|p| name_from_pat(&**p))
                                             .collect::<Vec<String>>().join(", ")),
         PatKind::Box(ref p) => name_from_pat(&**p),
         PatKind::Ref(ref p, _) => name_from_pat(&**p),
@@ -2630,7 +2626,7 @@ fn resolve_type(cx: &DocContext,
             };
         }
     };
-    let def = tcx.def_map.borrow().get(&id).expect("unresolved id not in defmap").full_def();
+    let def = tcx.expect_def(id);
     debug!("resolve_type: def={:?}", def);
 
     let is_generic = match def {
@@ -2699,7 +2695,7 @@ fn resolve_use_source(cx: &DocContext, path: Path, id: ast::NodeId) -> ImportSou
 
 fn resolve_def(cx: &DocContext, id: ast::NodeId) -> Option<DefId> {
     cx.tcx_opt().and_then(|tcx| {
-        tcx.def_map.borrow().get(&id).map(|d| register_def(cx, d.full_def()))
+        tcx.expect_def_or_none(id).map(|def| register_def(cx, def))
     })
 }
 
index 61985d390801bd132e9fae4423e201343a38487a..85ea94e02e8d794014a13263b2d9db970337abe6 100644 (file)
@@ -21,12 +21,12 @@ use rustc::lint;
 use rustc_trans::back::link;
 use rustc_resolve as resolve;
 use rustc_metadata::cstore::CStore;
-use rustc_metadata::creader::read_local_crates;
 
-use syntax::{ast, codemap, errors};
-use syntax::errors::emitter::ColorConfig;
+use syntax::{ast, codemap};
 use syntax::feature_gate::UnstableFeatures;
 use syntax::parse::token;
+use errors;
+use errors::emitter::ColorConfig;
 
 use std::cell::{RefCell, Cell};
 use std::collections::{HashMap, HashSet};
@@ -146,21 +146,12 @@ pub fn run_core(search_paths: SearchPaths,
 
     let krate = panictry!(driver::phase_1_parse_input(&sess, cfg, &input));
 
-    let name = link::find_crate_name(Some(&sess), &krate.attrs,
-                                     &input);
+    let name = link::find_crate_name(Some(&sess), &krate.attrs, &input);
 
-    let krate = driver::phase_2_configure_and_expand(&sess, &cstore, krate, &name, None)
-                    .expect("phase_2_configure_and_expand aborted in rustdoc!");
-
-    let krate = driver::assign_node_ids(&sess, krate);
-
-    let mut defs = hir_map::collect_definitions(&krate);
-    read_local_crates(&sess, &cstore, &defs, &krate, &name, &dep_graph);
-
-    // Lower ast -> hir and resolve.
-    let (analysis, resolutions, mut hir_forest) = {
-        driver::lower_and_resolve(&sess, &name, &mut defs, &krate,
-                                  &sess.dep_graph, resolve::MakeGlobMap::No)
+    let driver::ExpansionResult { defs, analysis, resolutions, mut hir_forest, .. } = {
+        driver::phase_2_configure_and_expand(
+            &sess, &cstore, krate, &name, None, resolve::MakeGlobMap::No, |_| Ok(()),
+        ).expect("phase_2_configure_and_expand aborted in rustdoc!")
     };
 
     let arenas = ty::CtxtArenas::new();
index 408782a698a2ac3474a444f73a4c8aeea5583208..04d176c36c8cf3c7c0eb7d17ae5261803b5658cb 100644 (file)
 pub use self::StructType::*;
 pub use self::TypeBound::*;
 
-use syntax;
-use syntax::codemap::Span;
 use syntax::abi;
 use syntax::ast;
 use syntax::ast::{Name, NodeId};
 use syntax::attr;
 use syntax::ptr::P;
+use syntax_pos::{self, Span};
+
 use rustc::hir;
 
 pub struct Module {
@@ -56,8 +56,8 @@ impl Module {
             vis: hir::Inherited,
             stab: None,
             depr: None,
-            where_outer: syntax::codemap::DUMMY_SP,
-            where_inner: syntax::codemap::DUMMY_SP,
+            where_outer: syntax_pos::DUMMY_SP,
+            where_inner: syntax_pos::DUMMY_SP,
             attrs      : hir::HirVec::new(),
             extern_crates: Vec::new(),
             imports    : Vec::new(),
index d4fdafea88adeccf1a61c58b14dd55485f939b21..77c4a0f8174a3296d00a2a94a480d912375cead7 100644 (file)
@@ -20,6 +20,7 @@ use std::iter::repeat;
 
 use rustc::middle::cstore::LOCAL_CRATE;
 use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
+use rustc::util::common::slice_pat;
 use syntax::abi::Abi;
 use rustc::hir;
 
@@ -111,28 +112,27 @@ impl fmt::Display for clean::Generics {
 
         for (i, life) in self.lifetimes.iter().enumerate() {
             if i > 0 {
-                f.write_str(", ")?;
+                f.write_str(",&nbsp;")?;
             }
             write!(f, "{}", *life)?;
         }
 
         if !self.type_params.is_empty() {
             if !self.lifetimes.is_empty() {
-                f.write_str(", ")?;
+                f.write_str(",&nbsp;")?;
             }
             for (i, tp) in self.type_params.iter().enumerate() {
                 if i > 0 {
-                    f.write_str(", ")?
+                    f.write_str(",&nbsp;")?
                 }
                 f.write_str(&tp.name)?;
 
                 if !tp.bounds.is_empty() {
-                    write!(f, ": {}", TyParamBounds(&tp.bounds))?;
+                    write!(f, ":&nbsp;{}", TyParamBounds(&tp.bounds))?;
                 }
 
-                match tp.default {
-                    Some(ref ty) => { write!(f, " = {}", ty)?; },
-                    None => {}
+                if let Some(ref ty) = tp.default {
+                    write!(f, "&nbsp;=&nbsp;{}", ty)?;
                 };
             }
         }
@@ -229,21 +229,21 @@ impl fmt::Display for clean::PathParameters {
                     let mut comma = false;
                     for lifetime in lifetimes {
                         if comma {
-                            f.write_str(", ")?;
+                            f.write_str(",&nbsp;")?;
                         }
                         comma = true;
                         write!(f, "{}", *lifetime)?;
                     }
                     for ty in types {
                         if comma {
-                            f.write_str(", ")?;
+                            f.write_str(",&nbsp;")?;
                         }
                         comma = true;
                         write!(f, "{}", *ty)?;
                     }
                     for binding in bindings {
                         if comma {
-                            f.write_str(", ")?;
+                            f.write_str(",&nbsp;")?;
                         }
                         comma = true;
                         write!(f, "{}", *binding)?;
@@ -400,15 +400,12 @@ fn primitive_link(f: &mut fmt::Formatter,
                 }
                 (_, render::Unknown) => None,
             };
-            match loc {
-                Some(root) => {
-                    write!(f, "<a class='primitive' href='{}{}/primitive.{}.html'>",
-                           root,
-                           path.0.first().unwrap(),
-                           prim.to_url_str())?;
-                    needs_termination = true;
-                }
-                None => {}
+            if let Some(root) = loc {
+                write!(f, "<a class='primitive' href='{}{}/primitive.{}.html'>",
+                       root,
+                       path.0.first().unwrap(),
+                       prim.to_url_str())?;
+                needs_termination = true;
             }
         }
         None => {}
@@ -474,9 +471,9 @@ impl fmt::Display for clean::Type {
                        decl.decl)
             }
             clean::Tuple(ref typs) => {
-                match &**typs {
-                    [] => primitive_link(f, clean::PrimitiveTuple, "()"),
-                    [ref one] => {
+                match slice_pat(&&**typs) {
+                    &[] => primitive_link(f, clean::PrimitiveTuple, "()"),
+                    &[ref one] => {
                         primitive_link(f, clean::PrimitiveTuple, "(")?;
                         write!(f, "{},", one)?;
                         primitive_link(f, clean::PrimitiveTuple, ")")
index 7ccf51a46295385258f63af2bf372ac2ed5abbcf..84e98a6739193e9b917135c3b218e00cb15bd08f 100644 (file)
@@ -26,16 +26,17 @@ use std::fmt::Display;
 use std::io;
 use std::io::prelude::*;
 
-use syntax::codemap::{CodeMap, Span};
+use syntax::codemap::CodeMap;
 use syntax::parse::lexer::{self, Reader, TokenAndSpan};
 use syntax::parse::token;
 use syntax::parse;
+use syntax_pos::Span;
 
 /// Highlights `src`, returning the HTML output.
 pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str>) -> String {
     debug!("highlighting: ================\n{}\n==============", src);
     let sess = parse::ParseSess::new();
-    let fm = sess.codemap().new_filemap("<stdin>".to_string(), src.to_string());
+    let fm = sess.codemap().new_filemap("<stdin>".to_string(), None, src.to_string());
 
     let mut out = Vec::new();
     write_header(class, id, &mut out).unwrap();
@@ -55,7 +56,7 @@ pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str>
 /// an enclosing `<pre>` block.
 pub fn render_inner_with_highlighting(src: &str) -> io::Result<String> {
     let sess = parse::ParseSess::new();
-    let fm = sess.codemap().new_filemap("<stdin>".to_string(), src.to_string());
+    let fm = sess.codemap().new_filemap("<stdin>".to_string(), None, src.to_string());
 
     let mut out = Vec::new();
     let mut classifier = Classifier::new(lexer::StringReader::new(&sess.span_diagnostic, fm),
@@ -337,7 +338,7 @@ impl Class {
             Class::MacroNonTerminal => "macro-nonterminal",
             Class::String => "string",
             Class::Number => "number",
-            Class::Bool => "boolvalue",
+            Class::Bool => "bool-val",
             Class::Ident => "ident",
             Class::Lifetime => "lifetime",
             Class::PreludeTy => "prelude-ty",
@@ -351,9 +352,8 @@ fn write_header(class: Option<&str>,
                 out: &mut Write)
                 -> io::Result<()> {
     write!(out, "<pre ")?;
-    match id {
-        Some(id) => write!(out, "id='{}' ", id)?,
-        None => {}
+    if let Some(id) = id {
+        write!(out, "id='{}' ", id)?;
     }
     write!(out, "class='rust {}'>\n", class.unwrap_or(""))
 }
index 3baf22b38ef6874b2fca3e4199c3c11c96fead9e..139e1033175ea1e49834aa22299bb374bf56156c 100644 (file)
@@ -408,7 +408,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) {
             tests.add_test(text.to_owned(),
                            block_info.should_panic, block_info.no_run,
                            block_info.ignore, block_info.test_harness,
-                           block_info.compile_fail);
+                           block_info.compile_fail, block_info.error_codes);
         }
     }
 
@@ -454,6 +454,7 @@ struct LangString {
     rust: bool,
     test_harness: bool,
     compile_fail: bool,
+    error_codes: Vec<String>,
 }
 
 impl LangString {
@@ -465,6 +466,7 @@ impl LangString {
             rust: true,  // NB This used to be `notrust = false`
             test_harness: false,
             compile_fail: false,
+            error_codes: Vec::new(),
         }
     }
 
@@ -472,9 +474,14 @@ impl LangString {
         let mut seen_rust_tags = false;
         let mut seen_other_tags = false;
         let mut data = LangString::all_false();
-        let allow_compile_fail = match get_unstable_features_setting() {
-            UnstableFeatures::Allow | UnstableFeatures::Cheat=> true,
-            _ => false,
+        let mut allow_compile_fail = false;
+        let mut allow_error_code_check = false;
+        match get_unstable_features_setting() {
+            UnstableFeatures::Allow | UnstableFeatures::Cheat => {
+                allow_compile_fail = true;
+                allow_error_code_check = true;
+            }
+            _ => {},
         };
 
         let tokens = string.split(|c: char|
@@ -493,7 +500,15 @@ impl LangString {
                     data.compile_fail = true;
                     seen_rust_tags = true;
                     data.no_run = true;
-                },
+                }
+                x if allow_error_code_check && x.starts_with("E") && x.len() == 5 => {
+                    if let Ok(_) = x[1..].parse::<u32>() {
+                        data.error_codes.push(x.to_owned());
+                        seen_rust_tags = true;
+                    } else {
+                        seen_other_tags = true;
+                    }
+                }
                 _ => { seen_other_tags = true }
             }
         }
@@ -577,7 +592,7 @@ mod tests {
     fn test_lang_string_parse() {
         fn t(s: &str,
             should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool,
-            compile_fail: bool) {
+            compile_fail: bool, error_codes: Vec<String>) {
             assert_eq!(LangString::parse(s), LangString {
                 should_panic: should_panic,
                 no_run: no_run,
@@ -585,22 +600,26 @@ mod tests {
                 rust: rust,
                 test_harness: test_harness,
                 compile_fail: compile_fail,
+                error_codes: error_codes,
             })
         }
 
         // marker                | should_panic| no_run| ignore| rust | test_harness| compile_fail
-        t("",                      false,        false,  false,  true,  false,        false);
-        t("rust",                  false,        false,  false,  true,  false,        false);
-        t("sh",                    false,        false,  false,  false, false,        false);
-        t("ignore",                false,        false,  true,   true,  false,        false);
-        t("should_panic",          true,         false,  false,  true,  false,        false);
-        t("no_run",                false,        true,   false,  true,  false,        false);
-        t("test_harness",          false,        false,  false,  true,  true,         false);
-        t("compile_fail",          false,        true,   false,  true,  false,        true);
-        t("{.no_run .example}",    false,        true,   false,  true,  false,        false);
-        t("{.sh .should_panic}",   true,         false,  false,  true,  false,        false);
-        t("{.example .rust}",      false,        false,  false,  true,  false,        false);
-        t("{.test_harness .rust}", false,        false,  false,  true,  true,         false);
+        //                       | error_codes
+        t("",                      false,        false,  false,  true,  false, false, Vec::new());
+        t("rust",                  false,        false,  false,  true,  false, false, Vec::new());
+        t("sh",                    false,        false,  false,  false, false, false, Vec::new());
+        t("ignore",                false,        false,  true,   true,  false, false, Vec::new());
+        t("should_panic",          true,         false,  false,  true,  false, false, Vec::new());
+        t("no_run",                false,        true,   false,  true,  false, false, Vec::new());
+        t("test_harness",          false,        false,  false,  true,  true,  false, Vec::new());
+        t("compile_fail",          false,        true,   false,  true,  false, true,  Vec::new());
+        t("E0450",                 false,        false,  false,  true,  false, false,
+                                   vec!("E0450".to_owned()));
+        t("{.no_run .example}",    false,        true,   false,  true,  false, false, Vec::new());
+        t("{.sh .should_panic}",   true,         false,  false,  true,  false, false, Vec::new());
+        t("{.example .rust}",      false,        false,  false,  true,  false, false, Vec::new());
+        t("{.test_harness .rust}", false,        false,  false,  true,  true,  false, Vec::new());
     }
 
     #[test]
index 7357ff3abac131e96c76ffcc45d71863b67d6032..acf867561a6370aa6084c3426f6497071dd7118b 100644 (file)
@@ -399,7 +399,6 @@ fn init_ids() -> HashMap<String, usize> {
      "methods",
      "deref-methods",
      "implementations",
-     "derived_implementations"
      ].into_iter().map(|id| (String::from(*id), 1)).collect()
 }
 
@@ -590,19 +589,16 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
     // Attach all orphan methods to the type's definition if the type
     // has since been learned.
     for &(did, ref item) in orphan_methods {
-        match paths.get(&did) {
-            Some(&(ref fqp, _)) => {
-                search_index.push(IndexItem {
-                    ty: shortty(item),
-                    name: item.name.clone().unwrap(),
-                    path: fqp[..fqp.len() - 1].join("::"),
-                    desc: Escape(&shorter(item.doc_value())).to_string(),
-                    parent: Some(did),
-                    parent_idx: None,
-                    search_type: get_index_search_type(&item),
-                });
-            },
-            None => {}
+        if let Some(&(ref fqp, _)) = paths.get(&did) {
+            search_index.push(IndexItem {
+                ty: shortty(item),
+                name: item.name.clone().unwrap(),
+                path: fqp[..fqp.len() - 1].join("::"),
+                desc: Escape(&shorter(item.doc_value())).to_string(),
+                parent: Some(did),
+                parent_idx: None,
+                search_type: get_index_search_type(&item),
+            });
         }
     }
 
@@ -1120,7 +1116,9 @@ impl DocFolder for Cache {
             clean::StructItem(..) | clean::EnumItem(..) |
             clean::TypedefItem(..) | clean::TraitItem(..) |
             clean::FunctionItem(..) | clean::ModuleItem(..) |
-            clean::ForeignFunctionItem(..) if !self.stripped_mod => {
+            clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) |
+            clean::ConstantItem(..) | clean::StaticItem(..)
+            if !self.stripped_mod => {
                 // Reexported items mean that the same id can show up twice
                 // in the rustdoc ast that we're looking at. We know,
                 // however, that a reexported item doesn't show up in the
@@ -1255,7 +1253,6 @@ impl Context {
 
         info!("Recursing into {}", self.dst.display());
 
-        mkdir(&self.dst).unwrap();
         let ret = f(self);
 
         info!("Recursed; leaving {}", self.dst.display());
@@ -1299,7 +1296,7 @@ impl Context {
     fn item<F>(&mut self, item: clean::Item, mut f: F) -> Result<(), Error> where
         F: FnMut(&mut Context, clean::Item),
     {
-        fn render(w: File, cx: &Context, it: &clean::Item,
+        fn render(writer: &mut io::Write, cx: &Context, it: &clean::Item,
                   pushname: bool) -> io::Result<()> {
             // A little unfortunate that this is done like this, but it sure
             // does make formatting *a lot* nicer.
@@ -1334,28 +1331,24 @@ impl Context {
 
             reset_ids(true);
 
-            // We have a huge number of calls to write, so try to alleviate some
-            // of the pain by using a buffered writer instead of invoking the
-            // write syscall all the time.
-            let mut writer = BufWriter::new(w);
             if !cx.render_redirect_pages {
-                layout::render(&mut writer, &cx.shared.layout, &page,
+                layout::render(writer, &cx.shared.layout, &page,
                                &Sidebar{ cx: cx, item: it },
                                &Item{ cx: cx, item: it },
                                cx.shared.css_file_extension.is_some())?;
             } else {
                 let mut url = repeat("../").take(cx.current.len())
                                            .collect::<String>();
-                if let Some(&(ref names, _)) = cache().paths.get(&it.def_id) {
+                if let Some(&(ref names, ty)) = cache().paths.get(&it.def_id) {
                     for name in &names[..names.len() - 1] {
                         url.push_str(name);
                         url.push_str("/");
                     }
-                    url.push_str(&item_path(it));
-                    layout::redirect(&mut writer, &url)?;
+                    url.push_str(&item_path(ty, names.last().unwrap()));
+                    layout::redirect(writer, &url)?;
                 }
             }
-            writer.flush()
+            Ok(())
         }
 
         // Stripped modules survive the rustdoc passes (i.e. `strip-private`)
@@ -1376,9 +1369,16 @@ impl Context {
             let mut item = Some(item);
             self.recurse(name, |this| {
                 let item = item.take().unwrap();
-                let joint_dst = this.dst.join("index.html");
-                let dst = try_err!(File::create(&joint_dst), &joint_dst);
-                try_err!(render(dst, this, &item, false), &joint_dst);
+
+                let mut buf = Vec::new();
+                render(&mut buf, this, &item, false).unwrap();
+                // buf will be empty if the module is stripped and there is no redirect for it
+                if !buf.is_empty() {
+                    let joint_dst = this.dst.join("index.html");
+                    try_err!(fs::create_dir_all(&this.dst), &this.dst);
+                    let mut dst = try_err!(File::create(&joint_dst), &joint_dst);
+                    try_err!(dst.write_all(&buf), &joint_dst);
+                }
 
                 let m = match item.inner {
                     clean::StrippedItem(box clean::ModuleItem(m)) |
@@ -1387,7 +1387,7 @@ impl Context {
                 };
 
                 // render sidebar-items.js used throughout this module
-                {
+                if !this.render_redirect_pages {
                     let items = this.build_sidebar_items(&m);
                     let js_dst = this.dst.join("sidebar-items.js");
                     let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst));
@@ -1401,10 +1401,16 @@ impl Context {
                 Ok(())
             })
         } else if item.name.is_some() {
-            let joint_dst = self.dst.join(&item_path(&item));
-
-            let dst = try_err!(File::create(&joint_dst), &joint_dst);
-            try_err!(render(dst, self, &item, true), &joint_dst);
+            let mut buf = Vec::new();
+            render(&mut buf, self, &item, true).unwrap();
+            // buf will be empty if the item is stripped and there is no redirect for it
+            if !buf.is_empty() {
+                let joint_dst = self.dst.join(&item_path(shortty(&item),
+                                                         item.name.as_ref().unwrap()));
+                try_err!(fs::create_dir_all(&self.dst), &self.dst);
+                let mut dst = try_err!(File::create(&joint_dst), &joint_dst);
+                try_err!(dst.write_all(&buf), &joint_dst);
+            }
             Ok(())
         } else {
             Ok(())
@@ -1509,20 +1515,23 @@ impl<'a> Item<'a> {
         // located, then we return `None`.
         } else {
             let cache = cache();
-            let path = match cache.external_paths.get(&self.item.def_id) {
+            let external_path = match cache.external_paths.get(&self.item.def_id) {
                 Some(path) => path,
                 None => return None,
             };
-            let root = match cache.extern_locations.get(&self.item.def_id.krate) {
+            let mut path = match cache.extern_locations.get(&self.item.def_id.krate) {
                 Some(&(_, Remote(ref s))) => s.to_string(),
                 Some(&(_, Local)) => self.cx.root_path.clone(),
                 Some(&(_, Unknown)) => return None,
                 None => return None,
             };
-            Some(format!("{root}{path}/{file}?gotosrc={goto}",
-                         root = root,
-                         path = path[..path.len() - 1].join("/"),
-                         file = item_path(self.item),
+            for item in &external_path[..external_path.len() - 1] {
+                path.push_str(item);
+                path.push_str("/");
+            }
+            Some(format!("{path}{file}?gotosrc={goto}",
+                         path = path,
+                         file = item_path(shortty(self.item), external_path.last().unwrap()),
                          goto = self.item.def_id.index.as_usize()))
         }
     }
@@ -1614,13 +1623,10 @@ impl<'a> fmt::Display for Item<'a> {
     }
 }
 
-fn item_path(item: &clean::Item) -> String {
-    if item.is_mod() {
-        format!("{}/index.html", item.name.as_ref().unwrap())
-    } else {
-        format!("{}.{}.html",
-                shortty(item).to_static_str(),
-                *item.name.as_ref().unwrap())
+fn item_path(ty: ItemType, name: &str) -> String {
+    match ty {
+        ItemType::Module => format!("{}/index.html", name),
+        _ => format!("{}.{}.html", ty.to_static_str(), name),
     }
 }
 
@@ -1649,12 +1655,8 @@ fn plain_summary_line(s: Option<&str>) -> String {
 }
 
 fn document(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result {
-    for stability in short_stability(item, cx, true) {
-        write!(w, "<div class='stability'>{}</div>", stability)?;
-    }
-    if let Some(s) = item.doc_value() {
-        write!(w, "<div class='docblock'>{}</div>", Markdown(s))?;
-    }
+    document_stability(w, cx, item)?;
+    document_full(w, item)?;
     Ok(())
 }
 
@@ -1671,11 +1673,28 @@ fn document_short(w: &mut fmt::Formatter, item: &clean::Item, link: AssocItemLin
     Ok(())
 }
 
+fn document_full(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
+    if let Some(s) = item.doc_value() {
+        write!(w, "<div class='docblock'>{}</div>", Markdown(s))?;
+    }
+    Ok(())
+}
+
+fn document_stability(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result {
+    for stability in short_stability(item, cx, true) {
+        write!(w, "<div class='stability'>{}</div>", stability)?;
+    }
+    Ok(())
+}
+
 fn item_module(w: &mut fmt::Formatter, cx: &Context,
                item: &clean::Item, items: &[clean::Item]) -> fmt::Result {
     document(w, cx, item)?;
 
     let mut indices = (0..items.len()).filter(|i| {
+        if let clean::DefaultImplItem(..) = items[*i].inner {
+            return false;
+        }
         !cx.maybe_ignore_item(&items[*i])
     }).collect::<Vec<usize>>();
 
@@ -1812,13 +1831,16 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
                        docs = shorter(Some(&Markdown(doc_value).to_string())),
                        class = shortty(myitem),
                        stab = myitem.stability_class(),
-                       href = item_path(myitem),
+                       href = item_path(shortty(myitem), myitem.name.as_ref().unwrap()),
                        title = full_path(cx, myitem))?;
             }
         }
     }
 
-    write!(w, "</table>")
+    if curty.is_some() {
+        write!(w, "</table>")?;
+    }
+    Ok(())
 }
 
 fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec<String> {
@@ -2068,15 +2090,12 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
         <h2 id='implementors'>Implementors</h2>
         <ul class='item-list' id='implementors-list'>
     ")?;
-    match cache.implementors.get(&it.def_id) {
-        Some(implementors) => {
-            for i in implementors {
-                write!(w, "<li><code>")?;
-                fmt_impl_for_trait_page(&i.impl_, w)?;
-                writeln!(w, "</code></li>")?;
-            }
+    if let Some(implementors) = cache.implementors.get(&it.def_id) {
+        for i in implementors {
+            write!(w, "<li><code>")?;
+            fmt_impl_for_trait_page(&i.impl_, w)?;
+            writeln!(w, "</code></li>")?;
         }
-        None => {}
     }
     write!(w, "</ul>")?;
     write!(w, r#"<script type="text/javascript" async
@@ -2243,26 +2262,24 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
     write!(w, "</pre>")?;
 
     document(w, cx, it)?;
-    let mut fields = s.fields.iter().filter(|f| {
+    let mut fields = s.fields.iter().filter_map(|f| {
         match f.inner {
-            clean::StructFieldItem(..) => true,
-            _ => false,
+            clean::StructFieldItem(ref ty) => Some((f, ty)),
+            _ => None,
         }
     }).peekable();
     if let doctree::Plain = s.struct_type {
         if fields.peek().is_some() {
-            write!(w, "<h2 class='fields'>Fields</h2>\n<table>")?;
-            for field in fields {
-                write!(w, "<tr class='stab {stab}'>
-                             <td id='{shortty}.{name}'>\
-                               <code>{name}</code></td><td>",
+            write!(w, "<h2 class='fields'>Fields</h2>")?;
+            for (field, ty) in fields {
+                write!(w, "<span id='{shortty}.{name}' class='{shortty}'><code>{name}: {ty}</code>
+                           </span><span class='stab {stab}'></span>",
                        shortty = ItemType::StructField,
                        stab = field.stability_class(),
-                       name = field.name.as_ref().unwrap())?;
+                       name = field.name.as_ref().unwrap(),
+                       ty = ty)?;
                 document(w, cx, field)?;
-                write!(w, "</td></tr>")?;
             }
-            write!(w, "</table>")?;
         }
     }
     render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
@@ -2292,7 +2309,7 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
                             write!(w, "{}(", name)?;
                             for (i, ty) in tys.iter().enumerate() {
                                 if i > 0 {
-                                    write!(w, ", ")?
+                                    write!(w, ",&nbsp;")?
                                 }
                                 write!(w, "{}", *ty)?;
                             }
@@ -2324,40 +2341,47 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
 
     document(w, cx, it)?;
     if !e.variants.is_empty() {
-        write!(w, "<h2 class='variants'>Variants</h2>\n<table class='variants_table'>")?;
+        write!(w, "<h2 class='variants'>Variants</h2>\n")?;
         for variant in &e.variants {
-            write!(w, "<tr><td id='{shortty}.{name}'><code>{name}</code></td><td>",
+            write!(w, "<span id='{shortty}.{name}' class='variant'><code>{name}",
                    shortty = ItemType::Variant,
                    name = variant.name.as_ref().unwrap())?;
+            if let clean::VariantItem(ref var) = variant.inner {
+                if let clean::TupleVariant(ref tys) = var.kind {
+                    write!(w, "(")?;
+                    for (i, ty) in tys.iter().enumerate() {
+                        if i > 0 {
+                            write!(w, ",&nbsp;")?;
+                        }
+                        write!(w, "{}", *ty)?;
+                    }
+                    write!(w, ")")?;
+                }
+            }
+            write!(w, "</code></span>")?;
             document(w, cx, variant)?;
 
             use clean::{Variant, StructVariant};
             if let clean::VariantItem( Variant { kind: StructVariant(ref s) } ) = variant.inner {
-                let fields = s.fields.iter().filter(|f| {
-                    match f.inner {
-                        clean::StructFieldItem(..) => true,
-                        _ => false,
-                    }
-                });
                 write!(w, "<h3 class='fields'>Fields</h3>\n
                            <table>")?;
-                for field in fields {
-                    write!(w, "<tr><td \
-                               id='{shortty}.{v}.field.{f}'>\
-                               <code>{f}</code></td><td>",
-                           shortty = ItemType::Variant,
-                           v = variant.name.as_ref().unwrap(),
-                           f = field.name.as_ref().unwrap())?;
-                    document(w, cx, field)?;
-                    write!(w, "</td></tr>")?;
+                for field in &s.fields {
+                    use clean::StructFieldItem;
+                    if let StructFieldItem(ref ty) = field.inner {
+                        write!(w, "<tr><td \
+                                   id='variant.{v}.field.{f}'>\
+                                   <code>{f}:&nbsp;{t}</code></td><td>",
+                               v = variant.name.as_ref().unwrap(),
+                               f = field.name.as_ref().unwrap(),
+                               t = *ty)?;
+                        document(w, cx, field)?;
+                        write!(w, "</td></tr>")?;
+                    }
                 }
                 write!(w, "</table>")?;
             }
-            write!(w, "</td><td>")?;
             render_stability_since(w, variant, it)?;
-            write!(w, "</td></tr>")?;
         }
-        write!(w, "</table>")?;
     }
     render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)?;
     Ok(())
@@ -2496,25 +2520,11 @@ fn render_assoc_items(w: &mut fmt::Formatter,
         }
         write!(w, "<h2 id='implementations'>Trait \
                    Implementations</h2>")?;
-        let (derived, manual): (Vec<_>, Vec<&Impl>) = traits.iter().partition(|i| {
-            i.inner_impl().derived
-        });
-        for i in &manual {
+        for i in &traits {
             let did = i.trait_did().unwrap();
             let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods);
             render_impl(w, cx, i, assoc_link, true, containing_item.stable_since())?;
         }
-        if !derived.is_empty() {
-            write!(w, "<h3 id='derived_implementations'>\
-                           Derived Implementations \
-                       </h3>")?;
-            for i in &derived {
-                let did = i.trait_did().unwrap();
-                let assoc_link = AssocItemLink::GotoSource(did,
-                                                           &i.inner_impl().provided_trait_methods);
-                render_impl(w, cx, i, assoc_link, true, containing_item.stable_since())?;
-            }
-        }
     }
     Ok(())
 }
@@ -2623,20 +2633,26 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi
 
         if !is_static || render_static {
             if !is_default_item {
-
-                if item.doc_value().is_some() {
-                    document(w, cx, item)?;
-                } else {
-                    // In case the item isn't documented,
-                    // provide short documentation from the trait
-                    if let Some(t) = trait_ {
-                        if let Some(it) = t.items.iter()
-                                           .find(|i| i.name == item.name) {
+                if let Some(t) = trait_ {
+                    // The trait item may have been stripped so we might not
+                    // find any documentation or stability for it.
+                    if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
+                        // We need the stability of the item from the trait
+                        // because impls can't have a stability.
+                        document_stability(w, cx, it)?;
+                        if item.doc_value().is_some() {
+                            document_full(w, item)?;
+                        } else {
+                            // In case the item isn't documented,
+                            // provide short documentation from the trait.
                             document_short(w, it, link)?;
                         }
                     }
+                } else {
+                    document(w, cx, item)?;
                 }
             } else {
+                document_stability(w, cx, item)?;
                 document_short(w, item, link)?;
             }
         }
index 0ec5cab78bc7b93459fb40a49c4b8f3403900bf4..68035e5abe4c28278f908ce180aca006b0c5b41b 100644 (file)
             break;
 
         case "+":
+            ev.preventDefault();
             toggleAllDocs();
             break;
 
             sidebar.append(div);
         }
 
+        block("primitive", "Primitive Types");
         block("mod", "Modules");
+        block("macro", "Macros");
         block("struct", "Structs");
         block("enum", "Enums");
+        block("constant", "Constants");
+        block("static", "Statics");
         block("trait", "Traits");
         block("fn", "Functions");
-        block("macro", "Macros");
+        block("type", "Type Definitions");
     }
 
     window.initSidebarItems = initSidebarItems;
index 8e4245d4ebf12db4ec78344a3d02b05c92b23d6a..b45e059e6d5e9d87c671223d20dfa3da090e6327 100644 (file)
@@ -265,6 +265,10 @@ nav.sub {
 .docblock h2 { font-size: 1.15em; }
 .docblock h3, .docblock h4, .docblock h5 { font-size: 1em; }
 
+.docblock {
+    margin-left: 24px;
+}
+
 .content .out-of-band {
     font-size: 23px;
     margin: 0px;
@@ -405,8 +409,8 @@ a {
 
 .content span.enum, .content a.enum, .block a.current.enum { color: #5e9766; }
 .content span.struct, .content a.struct, .block a.current.struct { color: #df3600; }
-.content a.type { color: #e57300; }
-.content a.macro { color: #068000; }
+.content span.type, .content a.type, .block a.current.type { color: #e57300; }
+.content span.macro, .content a.macro, .block a.current.macro { color: #068000; }
 .block a.current.crate { font-weight: 500; }
 
 .search-input {
@@ -449,7 +453,7 @@ a {
 .content .search-results td:first-child { padding-right: 0; }
 .content .search-results td:first-child a { padding-right: 10px; }
 
-tr.result span.primitive::after { content: ' (primitive type)'; font-style: italic; }
+tr.result span.primitive::after { content: ' (primitive type)'; font-style: italic; color: black}
 
 body.blur > :not(#help) {
     filter: blur(8px);
@@ -550,7 +554,7 @@ td.summary-column {
 pre.rust .kw { color: #8959A8; }
 pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
 pre.rust .number, pre.rust .string { color: #718C00; }
-pre.rust .self, pre.rust .boolval, pre.rust .prelude-val,
+pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val,
 pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; }
 pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; }
 pre.rust .lifetime { color: #B76514; }
@@ -640,6 +644,25 @@ span.since {
     margin-right: 5px;
 }
 
+.enum > .toggle-wrapper > .collapse-toggle, .struct > .toggle-wrapper > .collapse-toggle {
+    left: 0;
+    margin-top: 5px;
+}
+
+.enum > .toggle-wrapper + .docblock, .struct > .toggle-wrapper + .docblock {
+    margin-left: 30px;
+    margin-bottom: 20px;
+    margin-top: 5px;
+}
+
+.enum > .collapsed, .struct > .collapsed {
+    margin-bottom: 25px;
+}
+
+.enum .variant, .struct .structfield {
+    display: block;
+}
+
 :target > code {
    background: #FDFFD3;
 }
index 59b2ff7e3d64911703ae8f1594264056671313c8..aee6d15b7ca37084fb45d70b45efae85b3d5bdc2 100644 (file)
@@ -30,6 +30,10 @@ h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.t
     background-color: white;
 }
 
+div.stability > em > code {
+    background-color: initial;
+}
+
 .docblock code {
     background-color: #F5F5F5;
 }
@@ -84,8 +88,9 @@ pre {
     border-bottom-color: #ddd;
 }
 
-.content a.primitive { color: #39a7bf; }
-.content span.externcrate, span.mod, .content a.mod, block a.current.mod { color: #4d76ae; }
+.content span.primitive, .content a.primitive, .block a.current.primitive { color: #39a7bf; }
+.content span.externcrate,
+.content span.mod, .content a.mod, .block a.current.mod { color: #4d76ae; }
 .content span.fn, .content a.fn, .block a.current.fn,
 .content span.method, .content a.method, .block a.current.method,
 .content span.tymethod, .content a.tymethod, .block a.current.tymethod,
index 86aad10e02fc7354c273d5cdf38a0b0e55a57623..2015bb295eabd7be1f04f7b14bcd4008a61988f4 100644 (file)
@@ -42,9 +42,11 @@ extern crate rustc_back;
 extern crate rustc_metadata;
 extern crate serialize;
 #[macro_use] extern crate syntax;
+extern crate syntax_pos;
 extern crate test as testing;
 extern crate rustc_unicode;
 #[macro_use] extern crate log;
+extern crate rustc_errors as errors;
 
 extern crate serialize as rustc_serialize; // used by deriving
 
index 1980d1f9cc45fc54ab9ccffde89ed3938a580e7f..b8e40790646a7eab87cdf78d558404baf8628823 100644 (file)
@@ -12,6 +12,7 @@ use rustc::hir::def_id::DefId;
 use rustc::middle::privacy::AccessLevels;
 use rustc::util::nodemap::DefIdSet;
 use std::cmp;
+use std::mem;
 use std::string::String;
 use std::usize;
 
@@ -29,7 +30,8 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
     // strip all #[doc(hidden)] items
     let krate = {
         struct Stripper<'a> {
-            retained: &'a mut DefIdSet
+            retained: &'a mut DefIdSet,
+            update_retained: bool,
         }
         impl<'a> fold::DocFolder for Stripper<'a> {
             fn fold_item(&mut self, i: Item) -> Option<Item> {
@@ -38,17 +40,25 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
                     // use a dedicated hidden item for given item type if any
                     match i.inner {
                         clean::StructFieldItem(..) | clean::ModuleItem(..) => {
-                            return Strip(i).fold()
+                            // We need to recurse into stripped modules to
+                            // strip things like impl methods but when doing so
+                            // we must not add any items to the `retained` set.
+                            let old = mem::replace(&mut self.update_retained, false);
+                            let ret = Strip(self.fold_item_recur(i).unwrap()).fold();
+                            self.update_retained = old;
+                            return ret;
                         }
                         _ => return None,
                     }
                 } else {
-                    self.retained.insert(i.def_id);
+                    if self.update_retained {
+                        self.retained.insert(i.def_id);
+                    }
                 }
                 self.fold_item_recur(i)
             }
         }
-        let mut stripper = Stripper{ retained: &mut retained };
+        let mut stripper = Stripper{ retained: &mut retained, update_retained: true };
         stripper.fold_crate(krate)
     };
 
@@ -69,6 +79,7 @@ pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult {
         let mut stripper = Stripper {
             retained: &mut retained,
             access_levels: &access_levels,
+            update_retained: true,
         };
         krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
     }
@@ -81,12 +92,21 @@ pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult {
 struct Stripper<'a> {
     retained: &'a mut DefIdSet,
     access_levels: &'a AccessLevels<DefId>,
+    update_retained: bool,
 }
 
 impl<'a> fold::DocFolder for Stripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
         match i.inner {
-            clean::StrippedItem(..) => return Some(i),
+            clean::StrippedItem(..) => {
+                // We need to recurse into stripped modules to strip things
+                // like impl methods but when doing so we must not add any
+                // items to the `retained` set.
+                let old = mem::replace(&mut self.update_retained, false);
+                let ret = self.fold_item_recur(i);
+                self.update_retained = old;
+                return ret;
+            }
             // These items can all get re-exported
             clean::TypedefItem(..) | clean::StaticItem(..) |
             clean::StructItem(..) | clean::EnumItem(..) |
@@ -109,18 +129,13 @@ impl<'a> fold::DocFolder for Stripper<'a> {
 
             clean::ModuleItem(..) => {
                 if i.def_id.is_local() && i.visibility != Some(clean::Public) {
-                    return Strip(self.fold_item_recur(i).unwrap()).fold()
+                    let old = mem::replace(&mut self.update_retained, false);
+                    let ret = Strip(self.fold_item_recur(i).unwrap()).fold();
+                    self.update_retained = old;
+                    return ret;
                 }
             }
 
-            // trait impls for private items should be stripped
-            clean::ImplItem(clean::Impl{
-                for_: clean::ResolvedPath{ did, is_generic, .. }, ..
-            }) => {
-                if did.is_local() && !is_generic && !self.access_levels.is_exported(did) {
-                    return None;
-                }
-            }
             // handled in the `strip-priv-imports` pass
             clean::ExternCrateItem(..) | clean::ImportItem(..) => {}
 
@@ -152,7 +167,9 @@ impl<'a> fold::DocFolder for Stripper<'a> {
         };
 
         let i = if fastreturn {
-            self.retained.insert(i.def_id);
+            if self.update_retained {
+                self.retained.insert(i.def_id);
+            }
             return Some(i);
         } else {
             self.fold_item_recur(i)
@@ -160,13 +177,14 @@ impl<'a> fold::DocFolder for Stripper<'a> {
 
         i.and_then(|i| {
             match i.inner {
-                // emptied modules/impls have no need to exist
+                // emptied modules have no need to exist
                 clean::ModuleItem(ref m)
                     if m.items.is_empty() &&
                        i.doc_value().is_none() => None,
-                clean::ImplItem(ref i) if i.items.is_empty() => None,
                 _ => {
-                    self.retained.insert(i.def_id);
+                    if self.update_retained {
+                        self.retained.insert(i.def_id);
+                    }
                     Some(i)
                 }
             }
@@ -182,6 +200,10 @@ struct ImplStripper<'a> {
 impl<'a> fold::DocFolder for ImplStripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
         if let clean::ImplItem(ref imp) = i.inner {
+            // emptied none trait impls can be stripped
+            if imp.trait_.is_none() && imp.items.is_empty() {
+                return None;
+            }
             if let Some(did) = imp.for_.def_id() {
                 if did.is_local() && !imp.for_.is_generic() &&
                     !self.retained.contains(&did)
index 2754f77444c67544a24cb442d09e0828a3c39f98..e3bc8037d13b67b0395baeb7560bb6b964a8c283 100644 (file)
@@ -28,14 +28,15 @@ use rustc::hir::map as hir_map;
 use rustc::session::{self, config};
 use rustc::session::config::{get_unstable_features_setting, OutputType};
 use rustc::session::search_paths::{SearchPaths, PathKind};
-use rustc::hir::lowering::{lower_crate, DummyResolver};
 use rustc_back::dynamic_lib::DynamicLibrary;
 use rustc_back::tempdir::TempDir;
 use rustc_driver::{driver, Compilation};
+use rustc_driver::driver::phase_2_configure_and_expand;
 use rustc_metadata::cstore::CStore;
+use rustc_resolve::MakeGlobMap;
 use syntax::codemap::CodeMap;
-use syntax::errors;
-use syntax::errors::emitter::ColorConfig;
+use errors;
+use errors::emitter::ColorConfig;
 use syntax::parse::token;
 
 use core;
@@ -93,21 +94,16 @@ pub fn run(input: &str,
     let mut cfg = config::build_configuration(&sess);
     cfg.extend(config::parse_cfgspecs(cfgs.clone()));
     let krate = panictry!(driver::phase_1_parse_input(&sess, cfg, &input));
-    let krate = driver::phase_2_configure_and_expand(&sess, &cstore, krate,
-                                                     "rustdoc-test", None)
-        .expect("phase_2_configure_and_expand aborted in rustdoc!");
-    let krate = driver::assign_node_ids(&sess, krate);
-    let dep_graph = DepGraph::new(false);
-    let defs = hir_map::collect_definitions(&krate);
-
-    let mut dummy_resolver = DummyResolver;
-    let krate = lower_crate(&sess, &krate, &sess, &mut dummy_resolver);
-
-    let opts = scrape_test_config(&krate);
+    let driver::ExpansionResult { defs, mut hir_forest, .. } = {
+        phase_2_configure_and_expand(
+            &sess, &cstore, krate, "rustdoc-test", None, MakeGlobMap::No, |_| Ok(())
+        ).expect("phase_2_configure_and_expand aborted in rustdoc!")
+    };
 
+    let dep_graph = DepGraph::new(false);
+    let opts = scrape_test_config(hir_forest.krate());
     let _ignore = dep_graph.in_ignore();
-    let mut forest = hir_map::Forest::new(krate, &dep_graph);
-    let map = hir_map::map_crate(&mut forest, defs);
+    let map = hir_map::map_crate(&mut hir_forest, defs);
 
     let ctx = core::DocContext {
         map: &map,
@@ -180,7 +176,7 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions {
 fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
            externs: core::Externs,
            should_panic: bool, no_run: bool, as_test_harness: bool,
-           compile_fail: bool, opts: &TestOptions) {
+           compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions) {
     // the test harness wants its own `main` & top level functions, so
     // never wrap the test in `fn main() { ... }`
     let test = maketest(test, Some(cratename), as_test_harness, opts);
@@ -233,10 +229,11 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
     let data = Arc::new(Mutex::new(Vec::new()));
     let codemap = Rc::new(CodeMap::new());
     let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()),
-                                                      None,
-                                                      codemap.clone());
+                                                None,
+                                                codemap.clone(),
+                                                errors::snippet::FormatMode::EnvironmentSelected);
     let old = io::set_panic(box Sink(data.clone()));
-    let _bomb = Bomb(data, old.unwrap_or(box io::stdout()));
+    let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout()));
 
     // Compile the code
     let diagnostic_handler = errors::Handler::with_emitter(true, false, box emitter);
@@ -271,15 +268,34 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
     match res {
         Ok(r) => {
             match r {
-                Err(count) if count > 0 && compile_fail == false => {
-                    sess.fatal("aborting due to previous error(s)")
+                Err(count) => {
+                    if count > 0 && compile_fail == false {
+                        sess.fatal("aborting due to previous error(s)")
+                    } else if count == 0 && compile_fail == true {
+                        panic!("test compiled while it wasn't supposed to")
+                    }
+                    if count > 0 && error_codes.len() > 0 {
+                        let out = String::from_utf8(data.lock().unwrap().to_vec()).unwrap();
+                        error_codes.retain(|err| !out.contains(err));
+                    }
                 }
                 Ok(()) if compile_fail => panic!("test compiled while it wasn't supposed to"),
                 _ => {}
             }
         }
-        Err(_) if compile_fail == false => panic!("couldn't compile the test"),
-        _ => {}
+        Err(_) => {
+            if compile_fail == false {
+                panic!("couldn't compile the test");
+            }
+            if error_codes.len() > 0 {
+                let out = String::from_utf8(data.lock().unwrap().to_vec()).unwrap();
+                error_codes.retain(|err| !out.contains(err));
+            }
+        }
+    }
+
+    if error_codes.len() > 0 {
+        panic!("Some expected error codes were not found: {:?}", error_codes);
     }
 
     if no_run { return }
@@ -411,7 +427,7 @@ impl Collector {
 
     pub fn add_test(&mut self, test: String,
                     should_panic: bool, no_run: bool, should_ignore: bool,
-                    as_test_harness: bool, compile_fail: bool) {
+                    as_test_harness: bool, compile_fail: bool, error_codes: Vec<String>) {
         let name = if self.use_headers {
             let s = self.current_header.as_ref().map(|s| &**s).unwrap_or("");
             format!("{}_{}", s, self.cnt)
@@ -442,6 +458,7 @@ impl Collector {
                         no_run,
                         as_test_harness,
                         compile_fail,
+                        error_codes,
                         &opts);
             })
         });
index d5309d7433449713940a2d1fc97735d90bad001c..0334c5ef5c4f4397120d1408d1f8aa6227c8e5f8 100644 (file)
@@ -18,7 +18,7 @@ use syntax::abi;
 use syntax::ast;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
-use syntax::codemap::Span;
+use syntax_pos::Span;
 
 use rustc::hir::map as hir_map;
 use rustc::hir::def::Def;
@@ -189,8 +189,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             }
             hir::ViewPathList(p, paths) => {
                 let mine = paths.into_iter().filter(|path| {
-                    !self.maybe_inline_local(path.node.id(), None, false, om,
-                                     please_inline)
+                    !self.maybe_inline_local(path.node.id(), path.node.rename(),
+                                             false, om, please_inline)
                 }).collect::<hir::HirVec<hir::PathListItem>>();
 
                 if mine.is_empty() {
@@ -241,20 +241,22 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             Some(tcx) => tcx,
             None => return false
         };
-        let def = tcx.def_map.borrow()[&id];
+        let def = tcx.expect_def(id);
         let def_did = def.def_id();
 
         let use_attrs = tcx.map.attrs(id).clean(self.cx);
-        let is_no_inline = use_attrs.list("doc").has_word("no_inline");
+        // Don't inline doc(hidden) imports so they can be stripped at a later stage.
+        let is_no_inline = use_attrs.list("doc").has_word("no_inline") ||
+                           use_attrs.list("doc").has_word("hidden");
 
         // For cross-crate impl inlining we need to know whether items are
         // reachable in documentation - a previously nonreachable item can be
         // made reachable by cross-crate inlining which we're checking here.
         // (this is done here because we need to know this upfront)
-        if !def.def_id().is_local() && !is_no_inline {
+        if !def_did.is_local() && !is_no_inline {
             let attrs = clean::inline::load_attrs(self.cx, tcx, def_did);
             let self_is_hidden = attrs.list("doc").has_word("hidden");
-            match def.base_def {
+            match def {
                 Def::Trait(did) |
                 Def::Struct(did) |
                 Def::Enum(did) |
index 90b2c6116038c4024c764499ee0107631dc00772..6c4b6c4506b814fd7d19b5575841b6703ef54f52 100644 (file)
@@ -1764,9 +1764,8 @@ impl<T: Iterator<Item=char>> Parser<T> {
                     return self.parse_array(first);
                 }
                 ParseArrayComma => {
-                    match self.parse_array_comma_or_end() {
-                        Some(evt) => { return evt; }
-                        None => {}
+                    if let Some(evt) = self.parse_array_comma_or_end() {
+                        return evt;
                     }
                 }
                 ParseObject(first) => {
@@ -2583,9 +2582,8 @@ impl<'a, T: Encodable> fmt::Display for AsPrettyJson<'a, T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         let mut shim = FormatShim { inner: f };
         let mut encoder = PrettyEncoder::new(&mut shim);
-        match self.indent {
-            Some(n) => encoder.set_indent(n),
-            None => {}
+        if let Some(n) = self.indent {
+            encoder.set_indent(n);
         }
         match self.inner.encode(&mut encoder) {
             Ok(_) => Ok(()),
index ff9dacbb6799b7192e1874267307a25ad6b156ea..9c408366f8b481d38e983d83f4bdcac9cdcfbbde 100644 (file)
@@ -80,7 +80,8 @@ fn build_libbacktrace(host: &str, target: &str) {
     }
 
     let compiler = gcc::Config::new().get_compiler();
-    let ar = build_helper::cc2ar(compiler.path(), target);
+    // only msvc returns None for ar so unwrap is okay
+    let ar = build_helper::cc2ar(compiler.path(), target).unwrap();
     let cflags = compiler.args().iter().map(|s| s.to_str().unwrap())
                          .collect::<Vec<_>>().join(" ");
     run(Command::new("sh")
index 37045822d47ec01d6c3b2f28d5167e71b786b2e7..60d7e01d98814a99dc61c725a6fd97c09713c7e2 100644 (file)
@@ -14,7 +14,7 @@ use self::VacantEntryState::*;
 use borrow::Borrow;
 use cmp::max;
 use fmt::{self, Debug};
-use hash::{Hash, SipHasher, BuildHasher};
+use hash::{Hash, Hasher, BuildHasher, SipHasher13};
 use iter::FromIterator;
 use mem::{self, replace};
 use ops::{Deref, Index};
@@ -1552,6 +1552,12 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
         self.elem.read().0
     }
 
+    /// Take the ownership of the key and value from the map.
+    #[unstable(feature = "map_entry_recover_keys", issue = "34285")]
+    pub fn remove_pair(self) -> (K, V) {
+        pop_internal(self.elem)
+    }
+
     /// Gets a reference to the value in the entry.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn get(&self) -> &V {
@@ -1584,6 +1590,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
     pub fn remove(self) -> V {
         pop_internal(self.elem).1
     }
+
     /// Returns a key that was used for search.
     ///
     /// The key was retained for further use.
@@ -1600,6 +1607,12 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
         &self.key
     }
 
+    /// Take ownership of the key.
+    #[unstable(feature = "map_entry_recover_keys", issue = "34285")]
+    pub fn into_key(self) -> K {
+        self.key
+    }
+
     /// Sets the value of the entry with the VacantEntry's key,
     /// and returns a mutable reference to it
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -1698,10 +1711,30 @@ impl RandomState {
 
 #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
 impl BuildHasher for RandomState {
-    type Hasher = SipHasher;
+    type Hasher = DefaultHasher;
+    #[inline]
+    fn build_hasher(&self) -> DefaultHasher {
+        DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1))
+    }
+}
+
+/// The default `Hasher` used by `RandomState`.
+///
+/// The internal algorithm is not specified, and so it and its hashes should
+/// not be relied upon over releases.
+#[unstable(feature = "hashmap_default_hasher", issue = "0")]
+pub struct DefaultHasher(SipHasher13);
+
+#[unstable(feature = "hashmap_default_hasher", issue = "0")]
+impl Hasher for DefaultHasher {
+    #[inline]
+    fn write(&mut self, msg: &[u8]) {
+        self.0.write(msg)
+    }
+
     #[inline]
-    fn build_hasher(&self) -> SipHasher {
-        SipHasher::new_with_keys(self.k0, self.k1)
+    fn finish(&self) -> u64 {
+        self.0.finish()
     }
 }
 
index cf64e5d333639c5f11d88b854bcfa418cc81029b..c4c4cb453134f5e6fc15a58a12068041de7c7931 100644 (file)
@@ -8,10 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use alloc::heap::{allocate, deallocate, EMPTY};
+use alloc::heap::{EMPTY, allocate, deallocate};
 
 use cmp;
-use hash::{Hash, Hasher, BuildHasher};
+use hash::{BuildHasher, Hash, Hasher};
 use intrinsics::needs_drop;
 use marker;
 use mem::{align_of, size_of};
@@ -62,12 +62,12 @@ const EMPTY_BUCKET: u64 = 0;
 #[unsafe_no_drop_flag]
 pub struct RawTable<K, V> {
     capacity: usize,
-    size:     usize,
-    hashes:   Unique<u64>,
+    size: usize,
+    hashes: Unique<u64>,
 
     // Because K/V do not appear directly in any of the types in the struct,
     // inform rustc that in fact instances of K and V are reachable from here.
-    marker:   marker::PhantomData<(K,V)>,
+    marker: marker::PhantomData<(K, V)>,
 }
 
 unsafe impl<K: Send, V: Send> Send for RawTable<K, V> {}
@@ -77,44 +77,48 @@ struct RawBucket<K, V> {
     hash: *mut u64,
 
     // We use *const to ensure covariance with respect to K and V
-    key:  *const K,
-    val:  *const V,
-    _marker: marker::PhantomData<(K,V)>,
+    key: *const K,
+    val: *const V,
+    _marker: marker::PhantomData<(K, V)>,
 }
 
-impl<K,V> Copy for RawBucket<K,V> {}
-impl<K,V> Clone for RawBucket<K,V> {
-    fn clone(&self) -> RawBucket<K, V> { *self }
+impl<K, V> Copy for RawBucket<K, V> {}
+impl<K, V> Clone for RawBucket<K, V> {
+    fn clone(&self) -> RawBucket<K, V> {
+        *self
+    }
 }
 
 pub struct Bucket<K, V, M> {
-    raw:   RawBucket<K, V>,
-    idx:   usize,
-    table: M
+    raw: RawBucket<K, V>,
+    idx: usize,
+    table: M,
 }
 
-impl<K,V,M:Copy> Copy for Bucket<K,V,M> {}
-impl<K,V,M:Copy> Clone for Bucket<K,V,M> {
-    fn clone(&self) -> Bucket<K,V,M> { *self }
+impl<K, V, M: Copy> Copy for Bucket<K, V, M> {}
+impl<K, V, M: Copy> Clone for Bucket<K, V, M> {
+    fn clone(&self) -> Bucket<K, V, M> {
+        *self
+    }
 }
 
 pub struct EmptyBucket<K, V, M> {
-    raw:   RawBucket<K, V>,
-    idx:   usize,
-    table: M
+    raw: RawBucket<K, V>,
+    idx: usize,
+    table: M,
 }
 
 pub struct FullBucket<K, V, M> {
-    raw:   RawBucket<K, V>,
-    idx:   usize,
-    table: M
+    raw: RawBucket<K, V>,
+    idx: usize,
+    table: M,
 }
 
 pub type EmptyBucketImm<'table, K, V> = EmptyBucket<K, V, &'table RawTable<K, V>>;
-pub type  FullBucketImm<'table, K, V> =  FullBucket<K, V, &'table RawTable<K, V>>;
+pub type FullBucketImm<'table, K, V> = FullBucket<K, V, &'table RawTable<K, V>>;
 
 pub type EmptyBucketMut<'table, K, V> = EmptyBucket<K, V, &'table mut RawTable<K, V>>;
-pub type  FullBucketMut<'table, K, V> =  FullBucket<K, V, &'table mut RawTable<K, V>>;
+pub type FullBucketMut<'table, K, V> = FullBucket<K, V, &'table mut RawTable<K, V>>;
 
 pub enum BucketState<K, V, M> {
     Empty(EmptyBucket<K, V, M>),
@@ -139,14 +143,17 @@ pub struct SafeHash {
 impl SafeHash {
     /// Peek at the hash value, which is guaranteed to be non-zero.
     #[inline(always)]
-    pub fn inspect(&self) -> u64 { self.hash }
+    pub fn inspect(&self) -> u64 {
+        self.hash
+    }
 }
 
 /// We need to remove hashes of 0. That's reserved for empty buckets.
 /// This function wraps up `hash_keyed` to be the only way outside this
 /// module to generate a SafeHash.
 pub fn make_hash<T: ?Sized, S>(hash_state: &S, t: &T) -> SafeHash
-    where T: Hash, S: BuildHasher
+    where T: Hash,
+          S: BuildHasher
 {
     let mut state = hash_state.build_hasher();
     t.hash(&mut state);
@@ -175,8 +182,8 @@ impl<K, V> RawBucket<K, V> {
     unsafe fn offset(self, count: isize) -> RawBucket<K, V> {
         RawBucket {
             hash: self.hash.offset(count),
-            key:  self.key.offset(count),
-            val:  self.val.offset(count),
+            key: self.key.offset(count),
+            val: self.val.offset(count),
             _marker: marker::PhantomData,
         }
     }
@@ -212,7 +219,9 @@ impl<K, V, M> Bucket<K, V, M> {
     }
 }
 
-impl<K, V, M> Deref for FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> {
+impl<K, V, M> Deref for FullBucket<K, V, M>
+    where M: Deref<Target = RawTable<K, V>>
+{
     type Target = RawTable<K, V>;
     fn deref(&self) -> &RawTable<K, V> {
         &self.table
@@ -232,19 +241,23 @@ impl<'t, K, V> Put<K, V> for &'t mut RawTable<K, V> {
     }
 }
 
-impl<K, V, M> Put<K, V> for Bucket<K, V, M> where M: Put<K, V> {
+impl<K, V, M> Put<K, V> for Bucket<K, V, M>
+    where M: Put<K, V>
+{
     unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> {
         self.table.borrow_table_mut()
     }
 }
 
-impl<K, V, M> Put<K, V> for FullBucket<K, V, M> where M: Put<K, V> {
+impl<K, V, M> Put<K, V> for FullBucket<K, V, M>
+    where M: Put<K, V>
+{
     unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> {
         self.table.borrow_table_mut()
     }
 }
 
-impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> {
+impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> {
     pub fn new(table: M, hash: SafeHash) -> Bucket<K, V, M> {
         Bucket::at_index(table, hash.inspect() as usize)
     }
@@ -252,14 +265,13 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> {
     pub fn at_index(table: M, ib_index: usize) -> Bucket<K, V, M> {
         // if capacity is 0, then the RawBucket will be populated with bogus pointers.
         // This is an uncommon case though, so avoid it in release builds.
-        debug_assert!(table.capacity() > 0, "Table should have capacity at this point");
+        debug_assert!(table.capacity() > 0,
+                      "Table should have capacity at this point");
         let ib_index = ib_index & (table.capacity() - 1);
         Bucket {
-            raw: unsafe {
-               table.first_bucket_raw().offset(ib_index as isize)
-            },
+            raw: unsafe { table.first_bucket_raw().offset(ib_index as isize) },
             idx: ib_index,
-            table: table
+            table: table,
         }
     }
 
@@ -267,7 +279,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> {
         Bucket {
             raw: table.first_bucket_raw(),
             idx: 0,
-            table: table
+            table: table,
         }
     }
 
@@ -277,18 +289,20 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> {
     /// this module.
     pub fn peek(self) -> BucketState<K, V, M> {
         match unsafe { *self.raw.hash } {
-            EMPTY_BUCKET =>
+            EMPTY_BUCKET => {
                 Empty(EmptyBucket {
                     raw: self.raw,
                     idx: self.idx,
-                    table: self.table
-                }),
-            _ =>
+                    table: self.table,
+                })
+            }
+            _ => {
                 Full(FullBucket {
                     raw: self.raw,
                     idx: self.idx,
-                    table: self.table
+                    table: self.table,
                 })
+            }
         }
     }
 
@@ -308,7 +322,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> {
     }
 }
 
-impl<K, V, M: Deref<Target=RawTable<K, V>>> EmptyBucket<K, V, M> {
+impl<K, V, M: Deref<Target = RawTable<K, V>>> EmptyBucket<K, V, M> {
     #[inline]
     pub fn next(self) -> Bucket<K, V, M> {
         let mut bucket = self.into_bucket();
@@ -321,7 +335,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> EmptyBucket<K, V, M> {
         Bucket {
             raw: self.raw,
             idx: self.idx,
-            table: self.table
+            table: self.table,
         }
     }
 
@@ -329,22 +343,24 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> EmptyBucket<K, V, M> {
         let gap = EmptyBucket {
             raw: self.raw,
             idx: self.idx,
-            table: ()
+            table: (),
         };
 
         match self.next().peek() {
             Full(bucket) => {
                 Some(GapThenFull {
                     gap: gap,
-                    full: bucket
+                    full: bucket,
                 })
             }
-            Empty(..) => None
+            Empty(..) => None,
         }
     }
 }
 
-impl<K, V, M> EmptyBucket<K, V, M> where M: Put<K, V> {
+impl<K, V, M> EmptyBucket<K, V, M>
+    where M: Put<K, V>
+{
     /// Puts given key and value pair, along with the key's hash,
     /// into this bucket in the hashtable. Note how `self` is 'moved' into
     /// this function, because this slot will no longer be empty when
@@ -352,8 +368,7 @@ impl<K, V, M> EmptyBucket<K, V, M> where M: Put<K, V> {
     /// the newly-filled slot in the hashtable.
     ///
     /// Use `make_hash` to construct a `SafeHash` to pass to this function.
-    pub fn put(mut self, hash: SafeHash, key: K, value: V)
-               -> FullBucket<K, V, M> {
+    pub fn put(mut self, hash: SafeHash, key: K, value: V) -> FullBucket<K, V, M> {
         unsafe {
             *self.raw.hash = hash.inspect();
             ptr::write(self.raw.key as *mut K, key);
@@ -362,11 +377,15 @@ impl<K, V, M> EmptyBucket<K, V, M> where M: Put<K, V> {
             self.table.borrow_table_mut().size += 1;
         }
 
-        FullBucket { raw: self.raw, idx: self.idx, table: self.table }
+        FullBucket {
+            raw: self.raw,
+            idx: self.idx,
+            table: self.table,
+        }
     }
 }
 
-impl<K, V, M: Deref<Target=RawTable<K, V>>> FullBucket<K, V, M> {
+impl<K, V, M: Deref<Target = RawTable<K, V>>> FullBucket<K, V, M> {
     #[inline]
     pub fn next(self) -> Bucket<K, V, M> {
         let mut bucket = self.into_bucket();
@@ -379,7 +398,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> FullBucket<K, V, M> {
         Bucket {
             raw: self.raw,
             idx: self.idx,
-            table: self.table
+            table: self.table,
         }
     }
 
@@ -407,19 +426,12 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> FullBucket<K, V, M> {
 
     #[inline]
     pub fn hash(&self) -> SafeHash {
-        unsafe {
-            SafeHash {
-                hash: *self.raw.hash
-            }
-        }
+        unsafe { SafeHash { hash: *self.raw.hash } }
     }
 
     /// Gets references to the key and value at a given index.
     pub fn read(&self) -> (&K, &V) {
-        unsafe {
-            (&*self.raw.key,
-             &*self.raw.val)
-        }
+        unsafe { (&*self.raw.key, &*self.raw.val) }
     }
 }
 
@@ -436,69 +448,68 @@ impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> {
 
         unsafe {
             *self.raw.hash = EMPTY_BUCKET;
-            (
-                EmptyBucket {
-                    raw: self.raw,
-                    idx: self.idx,
-                    table: self.table
-                },
-                ptr::read(self.raw.key),
-                ptr::read(self.raw.val)
-            )
+            (EmptyBucket {
+                raw: self.raw,
+                idx: self.idx,
+                table: self.table,
+            },
+             ptr::read(self.raw.key),
+             ptr::read(self.raw.val))
         }
     }
 }
 
 // This use of `Put` is misleading and restrictive, but safe and sufficient for our use cases
 // where `M` is a full bucket or table reference type with mutable access to the table.
-impl<K, V, M> FullBucket<K, V, M> where M: Put<K, V> {
+impl<K, V, M> FullBucket<K, V, M>
+    where M: Put<K, V>
+{
     pub fn replace(&mut self, h: SafeHash, k: K, v: V) -> (SafeHash, K, V) {
         unsafe {
             let old_hash = ptr::replace(self.raw.hash as *mut SafeHash, h);
-            let old_key  = ptr::replace(self.raw.key as *mut K,  k);
-            let old_val  = ptr::replace(self.raw.val as *mut V,  v);
+            let old_key = ptr::replace(self.raw.key as *mut K, k);
+            let old_val = ptr::replace(self.raw.val as *mut V, v);
 
             (old_hash, old_key, old_val)
         }
     }
 }
 
-impl<K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + DerefMut {
+impl<K, V, M> FullBucket<K, V, M>
+    where M: Deref<Target = RawTable<K, V>> + DerefMut
+{
     /// Gets mutable references to the key and value at a given index.
     pub fn read_mut(&mut self) -> (&mut K, &mut V) {
-        unsafe {
-            (&mut *(self.raw.key as *mut K),
-             &mut *(self.raw.val as *mut V))
-        }
+        unsafe { (&mut *(self.raw.key as *mut K), &mut *(self.raw.val as *mut V)) }
     }
 }
 
-impl<'t, K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + 't {
+impl<'t, K, V, M> FullBucket<K, V, M>
+    where M: Deref<Target = RawTable<K, V>> + 't
+{
     /// Exchange a bucket state for immutable references into the table.
     /// Because the underlying reference to the table is also consumed,
     /// no further changes to the structure of the table are possible;
     /// in exchange for this, the returned references have a longer lifetime
     /// than the references returned by `read()`.
     pub fn into_refs(self) -> (&'t K, &'t V) {
-        unsafe {
-            (&*self.raw.key,
-             &*self.raw.val)
-        }
+        unsafe { (&*self.raw.key, &*self.raw.val) }
     }
 }
 
-impl<'t, K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + DerefMut + 't {
+impl<'t, K, V, M> FullBucket<K, V, M>
+    where M: Deref<Target = RawTable<K, V>> + DerefMut + 't
+{
     /// This works similarly to `into_refs`, exchanging a bucket state
     /// for mutable references into the table.
     pub fn into_mut_refs(self) -> (&'t mut K, &'t mut V) {
-        unsafe {
-            (&mut *(self.raw.key as *mut K),
-             &mut *(self.raw.val as *mut V))
-        }
+        unsafe { (&mut *(self.raw.key as *mut K), &mut *(self.raw.val as *mut V)) }
     }
 }
 
-impl<K, V, M> GapThenFull<K, V, M> where M: Deref<Target=RawTable<K, V>> {
+impl<K, V, M> GapThenFull<K, V, M>
+    where M: Deref<Target = RawTable<K, V>>
+{
     #[inline]
     pub fn full(&self) -> &FullBucket<K, V, M> {
         &self.full
@@ -522,7 +533,7 @@ impl<K, V, M> GapThenFull<K, V, M> where M: Deref<Target=RawTable<K, V>> {
 
                 Some(self)
             }
-            Empty(..) => None
+            Empty(..) => None,
         }
     }
 }
@@ -554,7 +565,8 @@ fn test_rounding() {
 // from the start of a mallocated array.
 #[inline]
 fn calculate_offsets(hashes_size: usize,
-                     keys_size: usize, keys_align: usize,
+                     keys_size: usize,
+                     keys_align: usize,
                      vals_align: usize)
                      -> (usize, usize, bool) {
     let keys_offset = round_up_to_next(hashes_size, keys_align);
@@ -567,14 +579,15 @@ fn calculate_offsets(hashes_size: usize,
 
 // Returns a tuple of (minimum required malloc alignment, hash_offset,
 // array_size), from the start of a mallocated array.
-fn calculate_allocation(hash_size: usize, hash_align: usize,
-                        keys_size: usize, keys_align: usize,
-                        vals_size: usize, vals_align: usize)
+fn calculate_allocation(hash_size: usize,
+                        hash_align: usize,
+                        keys_size: usize,
+                        keys_align: usize,
+                        vals_size: usize,
+                        vals_align: usize)
                         -> (usize, usize, usize, bool) {
     let hash_offset = 0;
-    let (_, vals_offset, oflo) = calculate_offsets(hash_size,
-                                                   keys_size, keys_align,
-                                                              vals_align);
+    let (_, vals_offset, oflo) = calculate_offsets(hash_size, keys_size, keys_align, vals_align);
     let (end_of_vals, oflo2) = vals_offset.overflowing_add(vals_size);
 
     let align = cmp::max(hash_align, cmp::max(keys_align, vals_align));
@@ -584,12 +597,13 @@ fn calculate_allocation(hash_size: usize, hash_align: usize,
 
 #[test]
 fn test_offset_calculation() {
-    assert_eq!(calculate_allocation(128, 8, 15, 1, 4,  4), (8, 0, 148, false));
-    assert_eq!(calculate_allocation(3,   1, 2,  1, 1,  1), (1, 0, 6, false));
-    assert_eq!(calculate_allocation(6,   2, 12, 4, 24, 8), (8, 0, 48, false));
+    assert_eq!(calculate_allocation(128, 8, 15, 1, 4, 4),
+               (8, 0, 148, false));
+    assert_eq!(calculate_allocation(3, 1, 2, 1, 1, 1), (1, 0, 6, false));
+    assert_eq!(calculate_allocation(6, 2, 12, 4, 24, 8), (8, 0, 48, false));
     assert_eq!(calculate_offsets(128, 15, 1, 4), (128, 144, false));
-    assert_eq!(calculate_offsets(3,   2,  1, 1), (3,   5, false));
-    assert_eq!(calculate_offsets(6,   12, 4, 8), (8,   24, false));
+    assert_eq!(calculate_offsets(3, 2, 1, 1), (3, 5, false));
+    assert_eq!(calculate_offsets(6, 12, 4, 8), (8, 24, false));
 }
 
 impl<K, V> RawTable<K, V> {
@@ -608,8 +622,8 @@ impl<K, V> RawTable<K, V> {
         // No need for `checked_mul` before a more restrictive check performed
         // later in this method.
         let hashes_size = capacity * size_of::<u64>();
-        let keys_size   = capacity * size_of::< K >();
-        let vals_size   = capacity * size_of::< V >();
+        let keys_size = capacity * size_of::<K>();
+        let vals_size = capacity * size_of::<V>();
 
         // Allocating hashmaps is a little tricky. We need to allocate three
         // arrays, but since we know their sizes and alignments up front,
@@ -619,31 +633,38 @@ impl<K, V> RawTable<K, V> {
         // This is great in theory, but in practice getting the alignment
         // right is a little subtle. Therefore, calculating offsets has been
         // factored out into a different function.
-        let (malloc_alignment, hash_offset, size, oflo) =
-            calculate_allocation(
-                hashes_size, align_of::<u64>(),
-                keys_size,   align_of::< K >(),
-                vals_size,   align_of::< V >());
+        let (malloc_alignment, hash_offset, size, oflo) = calculate_allocation(hashes_size,
+                                                                               align_of::<u64>(),
+                                                                               keys_size,
+                                                                               align_of::<K>(),
+                                                                               vals_size,
+                                                                               align_of::<V>());
 
         assert!(!oflo, "capacity overflow");
 
         // One check for overflow that covers calculation and rounding of size.
-        let size_of_bucket = size_of::<u64>().checked_add(size_of::<K>()).unwrap()
-                                             .checked_add(size_of::<V>()).unwrap();
-        assert!(size >= capacity.checked_mul(size_of_bucket)
-                                .expect("capacity overflow"),
+        let size_of_bucket = size_of::<u64>()
+                                 .checked_add(size_of::<K>())
+                                 .unwrap()
+                                 .checked_add(size_of::<V>())
+                                 .unwrap();
+        assert!(size >=
+                capacity.checked_mul(size_of_bucket)
+                        .expect("capacity overflow"),
                 "capacity overflow");
 
         let buffer = allocate(size, malloc_alignment);
-        if buffer.is_null() { ::alloc::oom() }
+        if buffer.is_null() {
+            ::alloc::oom()
+        }
 
         let hashes = buffer.offset(hash_offset as isize) as *mut u64;
 
         RawTable {
             capacity: capacity,
-            size:     0,
-            hashes:   Unique::new(hashes),
-            marker:   marker::PhantomData,
+            size: 0,
+            hashes: Unique::new(hashes),
+            marker: marker::PhantomData,
         }
     }
 
@@ -652,16 +673,16 @@ impl<K, V> RawTable<K, V> {
         let keys_size = self.capacity * size_of::<K>();
 
         let buffer = *self.hashes as *const u8;
-        let (keys_offset, vals_offset, oflo) =
-            calculate_offsets(hashes_size,
-                              keys_size, align_of::<K>(),
-                              align_of::<V>());
+        let (keys_offset, vals_offset, oflo) = calculate_offsets(hashes_size,
+                                                                 keys_size,
+                                                                 align_of::<K>(),
+                                                                 align_of::<V>());
         debug_assert!(!oflo, "capacity overflow");
         unsafe {
             RawBucket {
                 hash: *self.hashes,
-                key:  buffer.offset(keys_offset as isize) as *const K,
-                val:  buffer.offset(vals_offset as isize) as *const V,
+                key: buffer.offset(keys_offset as isize) as *const K,
+                val: buffer.offset(vals_offset as isize) as *const V,
                 _marker: marker::PhantomData,
             }
         }
@@ -691,9 +712,7 @@ impl<K, V> RawTable<K, V> {
     fn raw_buckets(&self) -> RawBuckets<K, V> {
         RawBuckets {
             raw: self.first_bucket_raw(),
-            hashes_end: unsafe {
-                self.hashes.offset(self.capacity as isize)
-            },
+            hashes_end: unsafe { self.hashes.offset(self.capacity as isize) },
             marker: marker::PhantomData,
         }
     }
@@ -747,7 +766,7 @@ impl<K, V> RawTable<K, V> {
             raw: raw_bucket.offset(self.capacity as isize),
             hashes_end: raw_bucket.hash,
             elems_left: self.size,
-            marker:     marker::PhantomData,
+            marker: marker::PhantomData,
         }
     }
 }
@@ -827,10 +846,7 @@ impl<'a, K, V> Iterator for RevMoveBuckets<'a, K, V> {
 
                 if *self.raw.hash != EMPTY_BUCKET {
                     self.elems_left -= 1;
-                    return Some((
-                        ptr::read(self.raw.key),
-                        ptr::read(self.raw.val)
-                    ));
+                    return Some((ptr::read(self.raw.key), ptr::read(self.raw.val)));
                 }
             }
         }
@@ -851,7 +867,7 @@ impl<'a, K, V> Clone for Iter<'a, K, V> {
     fn clone(&self) -> Iter<'a, K, V> {
         Iter {
             iter: self.iter.clone(),
-            elems_left: self.elems_left
+            elems_left: self.elems_left,
         }
     }
 }
@@ -873,7 +889,7 @@ unsafe impl<'a, K: Send, V: Send> Send for IterMut<'a, K, V> {}
 /// Iterator over the entries in a table, consuming the table.
 pub struct IntoIter<K, V> {
     table: RawTable<K, V>,
-    iter: RawBuckets<'static, K, V>
+    iter: RawBuckets<'static, K, V>,
 }
 
 unsafe impl<K: Sync, V: Sync> Sync for IntoIter<K, V> {}
@@ -894,10 +910,7 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> {
     fn next(&mut self) -> Option<(&'a K, &'a V)> {
         self.iter.next().map(|bucket| {
             self.elems_left -= 1;
-            unsafe {
-                (&*bucket.key,
-                 &*bucket.val)
-            }
+            unsafe { (&*bucket.key, &*bucket.val) }
         })
     }
 
@@ -906,7 +919,9 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> {
     }
 }
 impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {
-    fn len(&self) -> usize { self.elems_left }
+    fn len(&self) -> usize {
+        self.elems_left
+    }
 }
 
 impl<'a, K, V> Iterator for IterMut<'a, K, V> {
@@ -915,10 +930,7 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> {
     fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
         self.iter.next().map(|bucket| {
             self.elems_left -= 1;
-            unsafe {
-                (&*bucket.key,
-                 &mut *(bucket.val as *mut V))
-            }
+            unsafe { (&*bucket.key, &mut *(bucket.val as *mut V)) }
         })
     }
 
@@ -927,7 +939,9 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> {
     }
 }
 impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {
-    fn len(&self) -> usize { self.elems_left }
+    fn len(&self) -> usize {
+        self.elems_left
+    }
 }
 
 impl<K, V> Iterator for IntoIter<K, V> {
@@ -937,13 +951,7 @@ impl<K, V> Iterator for IntoIter<K, V> {
         self.iter.next().map(|bucket| {
             self.table.size -= 1;
             unsafe {
-                (
-                    SafeHash {
-                        hash: *bucket.hash,
-                    },
-                    ptr::read(bucket.key),
-                    ptr::read(bucket.val)
-                )
+                (SafeHash { hash: *bucket.hash }, ptr::read(bucket.key), ptr::read(bucket.val))
             }
         })
     }
@@ -954,7 +962,9 @@ impl<K, V> Iterator for IntoIter<K, V> {
     }
 }
 impl<K, V> ExactSizeIterator for IntoIter<K, V> {
-    fn len(&self) -> usize { self.table.size() }
+    fn len(&self) -> usize {
+        self.table.size()
+    }
 }
 
 impl<'a, K, V> Iterator for Drain<'a, K, V> {
@@ -965,13 +975,9 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> {
         self.iter.next().map(|bucket| {
             self.table.size -= 1;
             unsafe {
-                (
-                    SafeHash {
-                        hash: ptr::replace(bucket.hash, EMPTY_BUCKET),
-                    },
-                    ptr::read(bucket.key),
-                    ptr::read(bucket.val)
-                )
+                (SafeHash { hash: ptr::replace(bucket.hash, EMPTY_BUCKET) },
+                 ptr::read(bucket.key),
+                 ptr::read(bucket.val))
             }
         })
     }
@@ -982,7 +988,9 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> {
     }
 }
 impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> {
-    fn len(&self) -> usize { self.table.size() }
+    fn len(&self) -> usize {
+        self.table.size()
+    }
 }
 
 impl<'a, K: 'a, V: 'a> Drop for Drain<'a, K, V> {
@@ -1040,7 +1048,8 @@ impl<K, V> Drop for RawTable<K, V> {
         // dropping empty tables such as on resize.
         // Also avoid double drop of elements that have been already moved out.
         unsafe {
-            if needs_drop::<(K, V)>() { // avoid linear runtime for types that don't need drop
+            if needs_drop::<(K, V)>() {
+                // avoid linear runtime for types that don't need drop
                 for _ in self.rev_move_buckets() {}
             }
         }
@@ -1048,10 +1057,12 @@ impl<K, V> Drop for RawTable<K, V> {
         let hashes_size = self.capacity * size_of::<u64>();
         let keys_size = self.capacity * size_of::<K>();
         let vals_size = self.capacity * size_of::<V>();
-        let (align, _, size, oflo) =
-            calculate_allocation(hashes_size, align_of::<u64>(),
-                                 keys_size, align_of::<K>(),
-                                 vals_size, align_of::<V>());
+        let (align, _, size, oflo) = calculate_allocation(hashes_size,
+                                                          align_of::<u64>(),
+                                                          keys_size,
+                                                          align_of::<K>(),
+                                                          vals_size,
+                                                          align_of::<V>());
 
         debug_assert!(!oflo, "should be impossible");
 
index 44613d776712f82e5fcc56e60f02912c49cda0ef..9b13d54230078a8de6006c59864c5ee7f54f0143 100644 (file)
@@ -429,14 +429,16 @@ mod hash;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub mod hash_map {
-    //! A hashmap
+    //! A hash map implementation which uses linear probing with Robin
+    //! Hood bucket stealing.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use super::hash::map::*;
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub mod hash_set {
-    //! A hashset
+    //! An implementation of a hash set using the underlying representation of a
+    //! HashMap where the value is ().
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use super::hash::set::*;
 }
index d49d97649467ae1d61a726350aaf396628d244a3..2a2d41112ffae17fb0e156c3d9e4111ccc621181 100644 (file)
@@ -212,6 +212,13 @@ impl<T: Error> Error for Box<T> {
     }
 }
 
+#[stable(feature = "fmt_error", since = "1.11.0")]
+impl Error for fmt::Error {
+    fn description(&self) -> &str {
+        "an error occurred when formatting an argument"
+    }
+}
+
 // copied from any.rs
 impl Error + 'static {
     /// Returns true if the boxed type is the same as `T`
index 2bc7585f5fba913ef113f6fa9d5b390c317e54c8..0d3e18f9b966a2848781cafd10f2944d14ec2156 100644 (file)
@@ -509,6 +509,38 @@ impl CStr {
     /// The returned pointer will be valid for as long as `self` is and points
     /// to a contiguous region of memory terminated with a 0 byte to represent
     /// the end of the string.
+    ///
+    /// **WARNING**
+    ///
+    /// It is your responsibility to make sure that the underlying memory is not
+    /// freed too early. For example, the following code will cause undefined
+    /// behaviour when `ptr` is used inside the `unsafe` block:
+    ///
+    /// ```no_run
+    /// use std::ffi::{CString};
+    ///
+    /// let ptr = CString::new("Hello").unwrap().as_ptr();
+    /// unsafe {
+    ///     // `ptr` is dangling
+    ///     *ptr;
+    /// }
+    /// ```
+    ///
+    /// This happens because the pointer returned by `as_ptr` does not carry any
+    /// lifetime information and the string is deallocated immediately after
+    /// the `CString::new("Hello").unwrap().as_ptr()` expression is evaluated.
+    /// To fix the problem, bind the string to a local variable:
+    ///
+    /// ```no_run
+    /// use std::ffi::{CString};
+    ///
+    /// let hello = CString::new("Hello").unwrap();
+    /// let ptr = hello.as_ptr();
+    /// unsafe {
+    ///     // `ptr` is valid because `hello` is in scope
+    ///     *ptr;
+    /// }
+    /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn as_ptr(&self) -> *const c_char {
         self.inner.as_ptr()
index 125170bd47bc97c97cb6d1f24ce7f6b512ed990e..668fa1fb303601cefa74c819c7a9d2cf974fb5f0 100644 (file)
@@ -32,6 +32,8 @@ use time::SystemTime;
 /// it was opened with. Files also implement `Seek` to alter the logical cursor
 /// that the file contains internally.
 ///
+/// Files are automatically closed when they go out of scope.
+///
 /// # Examples
 ///
 /// ```no_run
@@ -510,7 +512,7 @@ impl OpenOptions {
     /// No file is allowed to exist at the target location, also no (dangling)
     /// symlink.
     ///
-    /// This option is useful because it as atomic. Otherwise between checking
+    /// This option is useful because it is atomic. Otherwise between checking
     /// whether a file exists and creating a new one, the file may have been
     /// created by another process (a TOCTOU race condition / attack).
     ///
@@ -1338,11 +1340,12 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
 ///
 /// // one possible implementation of walking a directory only visiting files
 /// fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> {
-///     if try!(fs::metadata(dir)).is_dir() {
+///     if dir.is_dir() {
 ///         for entry in try!(fs::read_dir(dir)) {
 ///             let entry = try!(entry);
-///             if try!(fs::metadata(entry.path())).is_dir() {
-///                 try!(visit_dirs(&entry.path(), cb));
+///             let path = entry.path();
+///             if path.is_dir() {
+///                 try!(visit_dirs(&path, cb));
 ///             } else {
 ///                 cb(&entry);
 ///             }
@@ -1767,6 +1770,15 @@ mod tests {
         check!(fs::remove_dir(dir));
     }
 
+    #[test]
+    fn file_create_new_already_exists_error() {
+        let tmpdir = tmpdir();
+        let file = &tmpdir.join("file_create_new_error_exists");
+        check!(fs::File::create(file));
+        let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err();
+        assert_eq!(e.kind(), ErrorKind::AlreadyExists);
+    }
+
     #[test]
     fn mkdir_path_already_exists_error() {
         let tmpdir = tmpdir();
index a1002fdb645fcb6119b9ff0d9b845a2ff3ab52fd..2d780559db1229b5382bdf831ec4b117f93ff43d 100644 (file)
@@ -230,6 +230,7 @@ impl<T> BufRead for Cursor<T> where T: AsRef<[u8]> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a> Write for Cursor<&'a mut [u8]> {
+    #[inline]
     fn write(&mut self, data: &[u8]) -> io::Result<usize> {
         let pos = cmp::min(self.pos, self.inner.len() as u64);
         let amt = (&mut self.inner[(pos as usize)..]).write(data)?;
@@ -269,6 +270,7 @@ impl Write for Cursor<Vec<u8>> {
 
 #[stable(feature = "cursor_box_slice", since = "1.5.0")]
 impl Write for Cursor<Box<[u8]>> {
+    #[inline]
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         let pos = cmp::min(self.pos, self.inner.len() as u64);
         let amt = (&mut self.inner[(pos as usize)..]).write(buf)?;
index 2815c0163d68a86e448dc50f459b0c00663c4e21..07f43f72ff55ab1d987b60c70c37b909695f27a3 100644 (file)
@@ -78,14 +78,11 @@ pub struct Empty { _priv: () }
 /// A slightly sad example of not reading anything into a buffer:
 ///
 /// ```
-/// use std::io;
-/// use std::io::Read;
+/// use std::io::{self, Read};
 ///
-/// # fn foo() -> io::Result<String> {
 /// let mut buffer = String::new();
-/// try!(io::empty().read_to_string(&mut buffer));
-/// # Ok(buffer)
-/// # }
+/// io::empty().read_to_string(&mut buffer).unwrap();
+/// assert!(buffer.is_empty());
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn empty() -> Empty { Empty { _priv: () } }
@@ -113,6 +110,16 @@ pub struct Repeat { byte: u8 }
 ///
 /// All reads from this reader will succeed by filling the specified buffer with
 /// the given byte.
+///
+/// # Examples
+///
+/// ```
+/// use std::io::{self, Read};
+///
+/// let mut buffer = [0; 3];
+/// io::repeat(0b101).read_exact(&mut buffer).unwrap();
+/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn repeat(byte: u8) -> Repeat { Repeat { byte: byte } }
 
@@ -139,6 +146,16 @@ pub struct Sink { _priv: () }
 ///
 /// All calls to `write` on the returned instance will return `Ok(buf.len())`
 /// and the contents of the buffer will not be inspected.
+///
+/// # Examples
+///
+/// ```rust
+/// use std::io::{self, Write};
+///
+/// let mut buffer = vec![1, 2, 3, 5, 8];
+/// let num_bytes = io::sink().write(&mut buffer).unwrap();
+/// assert_eq!(num_bytes, 5);
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn sink() -> Sink { Sink { _priv: () } }
 
index 8f41bdf39e97a0c054f1b1645489781e908c95c8..a396c7be09ad16db2effaf51845b45085bd80d47 100644 (file)
        test(no_crate_inject, attr(deny(warnings))),
        test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))]
 
+#![needs_panic_runtime]
+
 #![feature(alloc)]
 #![feature(allow_internal_unstable)]
 #![feature(asm)]
 #![feature(reflect_marker)]
 #![feature(rustc_attrs)]
 #![feature(shared)]
+#![feature(sip_hash_13)]
 #![feature(slice_bytes)]
 #![feature(slice_concat_ext)]
 #![feature(slice_patterns)]
 #![feature(zero_one)]
 #![feature(question_mark)]
 #![feature(try_from)]
+#![feature(needs_panic_runtime)]
 
 // Issue# 30592: Systematically use alloc_system during stage0 since jemalloc
 // might be unavailable or disabled
 #![allow(unused_features)] // std may use features in a platform-specific way
 #![cfg_attr(not(stage0), deny(warnings))]
 
-// FIXME(stage0): after a snapshot, move needs_panic_runtime up above and remove
-//                this `extern crate` declaration and feature(panic_unwind)
-#![cfg_attr(not(stage0), needs_panic_runtime)]
-#![cfg_attr(not(stage0), feature(needs_panic_runtime))]
-#[cfg(stage0)]
-extern crate panic_unwind as __please_just_link_me_dont_reference_me;
-
 #[cfg(test)] extern crate test;
 
 // We want to reexport a few macros from core but libcore has already been
@@ -471,3 +468,15 @@ pub mod __rand {
 // the rustdoc documentation for primitive types. Using `include!`
 // because rustdoc only looks for these modules at the crate level.
 include!("primitive_docs.rs");
+
+// FIXME(stage0): remove this after a snapshot
+// HACK: this is needed because the interpretation of slice
+// patterns changed between stage0 and now.
+#[cfg(stage0)]
+fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'a &'b [T] {
+    t
+}
+#[cfg(not(stage0))]
+fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'b [T] {
+    *t
+}
index d69789cedaf2c0e39454c999c28715c8d31b784c..26cf8a3199d1bb5cff9438b94fc7946ebf3c7571 100644 (file)
@@ -276,7 +276,7 @@ pub mod builtin {
     /// // fn concat_idents!(new, fun, name) { } // not usable in this way!
     /// # }
     /// ```
-    #[stable(feature = "rust1", since = "1.0.0")]
+    #[unstable(feature = "concat_idents", issue = "29599")]
     #[macro_export]
     macro_rules! concat_idents {
         ($($e:ident),*) => ({ /* compiler built-in */ })
index d510339f1c5b492d29941a9cbfe10bc7e4e926d5..b93ca8277e636bb7b3b8ff1ed30cd84db1a20ef1 100644 (file)
@@ -344,6 +344,9 @@ impl hash::Hash for SocketAddrV6 {
 /// some other type (e.g. a string) just for it to be converted back to
 /// `SocketAddr` in constructor methods is pointless.
 ///
+/// Addresses returned by the operating system that are not IP addresses are
+/// silently ignored.
+///
 /// Some examples:
 ///
 /// ```no_run
@@ -448,12 +451,7 @@ impl ToSocketAddrs for (Ipv6Addr, u16) {
 
 fn resolve_socket_addr(s: &str, p: u16) -> io::Result<vec::IntoIter<SocketAddr>> {
     let ips = lookup_host(s)?;
-    let v: Vec<_> = ips.map(|a| {
-        a.map(|mut a| {
-            a.set_port(p);
-            a
-        })
-    }).collect()?;
+    let v: Vec<_> = ips.map(|mut a| { a.set_port(p); a }).collect();
     Ok(v.into_iter())
 }
 
index 45b85d600a65639e057e14de5470b06992014f08..57d75441bff3dd50991b0192d8eb2ce8c36d76b9 100644 (file)
@@ -82,14 +82,15 @@ impl Ipv4Addr {
         [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8]
     }
 
-    /// Returns true for the special 'unspecified' address 0.0.0.0.
+    /// Returns true for the special 'unspecified' address (0.0.0.0).
     pub fn is_unspecified(&self) -> bool {
         self.inner.s_addr == 0
     }
 
     /// Returns true if this is a loopback address (127.0.0.0/8).
     ///
-    /// This property is defined by RFC 6890.
+    /// This property is defined by [RFC 1122].
+    /// [RFC 1122]: https://tools.ietf.org/html/rfc1122
     #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_loopback(&self) -> bool {
         self.octets()[0] == 127
@@ -97,7 +98,8 @@ impl Ipv4Addr {
 
     /// Returns true if this is a private address.
     ///
-    /// The private address ranges are defined in RFC 1918 and include:
+    /// The private address ranges are defined in [RFC 1918] and include:
+    /// [RFC 1918]: https://tools.ietf.org/html/rfc1918
     ///
     ///  - 10.0.0.0/8
     ///  - 172.16.0.0/12
@@ -114,7 +116,8 @@ impl Ipv4Addr {
 
     /// Returns true if the address is link-local (169.254.0.0/16).
     ///
-    /// This property is defined by RFC 6890.
+    /// This property is defined by [RFC 3927].
+    /// [RFC 3927]: https://tools.ietf.org/html/rfc3927
     #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_link_local(&self) -> bool {
         self.octets()[0] == 169 && self.octets()[1] == 254
@@ -137,18 +140,20 @@ impl Ipv4Addr {
         !self.is_broadcast() && !self.is_documentation() && !self.is_unspecified()
     }
 
-    /// Returns true if this is a multicast address.
+    /// Returns true if this is a multicast address (224.0.0.0/4).
     ///
     /// Multicast addresses have a most significant octet between 224 and 239,
-    /// and is defined by RFC 5771.
+    /// and is defined by [RFC 5771].
+    /// [RFC 5771]: https://tools.ietf.org/html/rfc5771
     #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_multicast(&self) -> bool {
         self.octets()[0] >= 224 && self.octets()[0] <= 239
     }
 
-    /// Returns true if this is a broadcast address.
+    /// Returns true if this is a broadcast address (255.255.255.255).
     ///
-    /// A broadcast address has all octets set to 255 as defined in RFC 919.
+    /// A broadcast address has all octets set to 255 as defined in [RFC 919].
+    /// [RFC 919]: https://tools.ietf.org/html/rfc919
     #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_broadcast(&self) -> bool {
         self.octets()[0] == 255 && self.octets()[1] == 255 &&
@@ -157,7 +162,8 @@ impl Ipv4Addr {
 
     /// Returns true if this address is in a range designated for documentation.
     ///
-    /// This is defined in RFC 5737:
+    /// This is defined in [RFC 5737]:
+    /// [RFC 5737]: https://tools.ietf.org/html/rfc5737
     ///
     /// - 192.0.2.0/24 (TEST-NET-1)
     /// - 198.51.100.0/24 (TEST-NET-2)
@@ -251,7 +257,7 @@ impl PartialOrd for Ipv4Addr {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Ord for Ipv4Addr {
     fn cmp(&self, other: &Ipv4Addr) -> Ordering {
-        self.octets().cmp(&other.octets())
+        ntoh(self.inner.s_addr).cmp(&ntoh(other.inner.s_addr))
     }
 }
 
@@ -321,9 +327,10 @@ impl Ipv6Addr {
         ]
     }
 
-    /// Returns true for the special 'unspecified' address ::.
+    /// Returns true for the special 'unspecified' address (::).
     ///
-    /// This property is defined in RFC 6890.
+    /// This property is defined in [RFC 4291].
+    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
     #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_unspecified(&self) -> bool {
         self.segments() == [0, 0, 0, 0, 0, 0, 0, 0]
@@ -331,7 +338,8 @@ impl Ipv6Addr {
 
     /// Returns true if this is a loopback address (::1).
     ///
-    /// This property is defined in RFC 6890.
+    /// This property is defined in [RFC 4291].
+    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
     #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_loopback(&self) -> bool {
         self.segments() == [0, 0, 0, 0, 0, 0, 0, 1]
@@ -352,26 +360,33 @@ impl Ipv6Addr {
         }
     }
 
-    /// Returns true if this is a unique local address (IPv6).
+    /// Returns true if this is a unique local address (fc00::/7).
     ///
-    /// Unique local addresses are defined in RFC 4193 and have the form fc00::/7.
+    /// This property is defined in [RFC 4193].
+    /// [RFC 4193]: https://tools.ietf.org/html/rfc4193
     pub fn is_unique_local(&self) -> bool {
         (self.segments()[0] & 0xfe00) == 0xfc00
     }
 
     /// Returns true if the address is unicast and link-local (fe80::/10).
+    ///
+    /// This property is defined in [RFC 4291].
+    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
     pub fn is_unicast_link_local(&self) -> bool {
         (self.segments()[0] & 0xffc0) == 0xfe80
     }
 
-    /// Returns true if this is a deprecated unicast site-local address (IPv6
-    /// fec0::/10).
+    /// Returns true if this is a deprecated unicast site-local address
+    /// (fec0::/10).
     pub fn is_unicast_site_local(&self) -> bool {
         (self.segments()[0] & 0xffc0) == 0xfec0
     }
 
     /// Returns true if this is an address reserved for documentation
-    /// This is defined to be 2001:db8::/32 in RFC 3849.
+    /// (2001:db8::/32).
+    ///
+    /// This property is defined in [RFC 3849].
+    /// [RFC 3849]: https://tools.ietf.org/html/rfc3849
     pub fn is_documentation(&self) -> bool {
         (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8)
     }
@@ -411,10 +426,10 @@ impl Ipv6Addr {
         }
     }
 
-    /// Returns true if this is a multicast address.
+    /// Returns true if this is a multicast address (ff00::/8).
     ///
-    /// Multicast addresses have the form ff00::/8, and this property is defined
-    /// by RFC 3956.
+    /// This property is defined by [RFC 4291].
+    /// [RFC 4291]: https://tools.ietf.org/html/rfc4291
     #[stable(since = "1.7.0", feature = "ip_17")]
     pub fn is_multicast(&self) -> bool {
         (self.segments()[0] & 0xff00) == 0xff00
index 45070460282710f84e0de8fcd37e179c573f8421..ac13b23ebee50256b70f780917b182f77ffce26b 100644 (file)
@@ -98,8 +98,8 @@ pub struct LookupHost(net_imp::LookupHost);
                                               addresses",
            issue = "27705")]
 impl Iterator for LookupHost {
-    type Item = io::Result<SocketAddr>;
-    fn next(&mut self) -> Option<io::Result<SocketAddr>> { self.0.next() }
+    type Item = SocketAddr;
+    fn next(&mut self) -> Option<SocketAddr> { self.0.next() }
 }
 
 /// Resolve the host specified by `host` as a number of `SocketAddr` instances.
@@ -107,6 +107,9 @@ impl Iterator for LookupHost {
 /// This method may perform a DNS query to resolve `host` and may also inspect
 /// system configuration to resolve the specified hostname.
 ///
+/// The returned iterator will skip over any unknown addresses returned by the
+/// operating system.
+///
 /// # Examples
 ///
 /// ```no_run
@@ -116,7 +119,7 @@ impl Iterator for LookupHost {
 ///
 /// # fn foo() -> std::io::Result<()> {
 /// for host in try!(net::lookup_host("rust-lang.org")) {
-///     println!("found address: {}", try!(host));
+///     println!("found address: {}", host);
 /// }
 /// # Ok(())
 /// # }
index 94aa3d6b513ef4d24489f34bde91cf63091b5eee..7a676c041ad89084e72b2ea5325923f0e0fabf4b 100644 (file)
@@ -110,6 +110,7 @@ mod cmath {
         }
 
         #[inline]
+        #[allow(deprecated)]
         pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float {
             let (a, b) = f64::frexp(x as f64);
             *value = b as c_int;
@@ -117,6 +118,7 @@ mod cmath {
         }
 
         #[inline]
+        #[allow(deprecated)]
         pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float {
             f64::ldexp(x as f64, n as isize) as c_float
         }
@@ -217,7 +219,7 @@ impl f32 {
     /// // Values between `0` and `min` are Subnormal.
     /// assert!(!lower_than_min.is_normal());
     /// ```
-    /// [subnormal]: http://en.wikipedia.org/wiki/Denormal_number
+    /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn is_normal(self) -> bool { num::Float::is_normal(self) }
@@ -265,7 +267,11 @@ impl f32 {
     /// [floating-point]: ../reference.html#machine-types
     #[unstable(feature = "float_extras", reason = "signature is undecided",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     #[inline]
+    #[allow(deprecated)]
     pub fn integer_decode(self) -> (u64, i16, i8) {
         num::Float::integer_decode(self)
     }
@@ -718,6 +724,9 @@ impl f32 {
     #[unstable(feature = "float_extras",
                reason = "pending integer conventions",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     #[inline]
     pub fn ldexp(x: f32, exp: isize) -> f32 {
         unsafe { cmath::ldexpf(x, exp as c_int) }
@@ -747,6 +756,9 @@ impl f32 {
     #[unstable(feature = "float_extras",
                reason = "pending integer conventions",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     #[inline]
     pub fn frexp(self) -> (f32, isize) {
         unsafe {
@@ -773,6 +785,9 @@ impl f32 {
     #[unstable(feature = "float_extras",
                reason = "unsure about its place in the world",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     #[inline]
     pub fn next_after(self, other: f32) -> f32 {
         unsafe { cmath::nextafterf(self, other) }
@@ -829,6 +844,13 @@ impl f32 {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
+    #[rustc_deprecated(since = "1.10.0",
+                       reason = "you probably meant `(self - other).abs()`: \
+                                 this operation is `(self - other).max(0.0)` (also \
+                                 known as `fdimf` in C). If you truly need the positive \
+                                 difference, consider using that expression or the C function \
+                                 `fdimf`, depending on how you wish to handle NaN (please consider \
+                                 filing an issue describing your use-case too).")]
     pub fn abs_sub(self, other: f32) -> f32 {
         unsafe { cmath::fdimf(self, other) }
     }
@@ -916,12 +938,12 @@ impl f32 {
     /// Computes the tangent of a number (in radians).
     ///
     /// ```
-    /// use std::f64;
+    /// use std::f32;
     ///
-    /// let x = f64::consts::PI/4.0;
+    /// let x = f32::consts::PI / 4.0;
     /// let abs_difference = (x.tan() - 1.0).abs();
     ///
-    /// assert!(abs_difference < 1e-10);
+    /// assert!(abs_difference <= f32::EPSILON);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
@@ -939,7 +961,7 @@ impl f32 {
     /// let f = f32::consts::PI / 2.0;
     ///
     /// // asin(sin(pi/2))
-    /// let abs_difference = f.sin().asin().abs_sub(f32::consts::PI / 2.0);
+    /// let abs_difference = (f.sin().asin() - f32::consts::PI / 2.0).abs();
     ///
     /// assert!(abs_difference <= f32::EPSILON);
     /// ```
@@ -959,7 +981,7 @@ impl f32 {
     /// let f = f32::consts::PI / 4.0;
     ///
     /// // acos(cos(pi/4))
-    /// let abs_difference = f.cos().acos().abs_sub(f32::consts::PI / 4.0);
+    /// let abs_difference = (f.cos().acos() - f32::consts::PI / 4.0).abs();
     ///
     /// assert!(abs_difference <= f32::EPSILON);
     /// ```
@@ -978,7 +1000,7 @@ impl f32 {
     /// let f = 1.0f32;
     ///
     /// // atan(tan(1))
-    /// let abs_difference = f.tan().atan().abs_sub(1.0);
+    /// let abs_difference = (f.tan().atan() - 1.0).abs();
     ///
     /// assert!(abs_difference <= f32::EPSILON);
     /// ```
@@ -1045,12 +1067,14 @@ impl f32 {
     /// number is close to zero.
     ///
     /// ```
-    /// let x = 7.0f64;
+    /// use std::f32;
+    ///
+    /// let x = 6.0f32;
     ///
-    /// // e^(ln(7)) - 1
-    /// let abs_difference = x.ln().exp_m1().abs_sub(6.0);
+    /// // e^(ln(6)) - 1
+    /// let abs_difference = (x.ln().exp_m1() - 5.0).abs();
     ///
-    /// assert!(abs_difference < 1e-10);
+    /// assert!(abs_difference <= f32::EPSILON);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
@@ -1108,7 +1132,7 @@ impl f32 {
     /// let f = x.cosh();
     /// // Solving cosh() at 1 gives this result
     /// let g = (e*e + 1.0)/(2.0*e);
-    /// let abs_difference = f.abs_sub(g);
+    /// let abs_difference = (f - g).abs();
     ///
     /// // Same result
     /// assert!(abs_difference <= f32::EPSILON);
@@ -1191,9 +1215,9 @@ impl f32 {
     /// let e = f32::consts::E;
     /// let f = e.tanh().atanh();
     ///
-    /// let abs_difference = f.abs_sub(e);
+    /// let abs_difference = (f - e).abs();
     ///
-    /// assert!(abs_difference <= f32::EPSILON);
+    /// assert!(abs_difference <= 1e-5);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
@@ -1375,7 +1399,7 @@ mod tests {
     }
 
     #[test]
-    #[rustc_no_mir] // FIXME #27840 MIR NAN ends up negative.
+    #[allow(deprecated)]
     fn test_integer_decode() {
         assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1));
         assert_eq!((-8573.5918555f32).integer_decode(), (8779358, -10, -1));
@@ -1384,7 +1408,11 @@ mod tests {
         assert_eq!((-0f32).integer_decode(), (0, -150, -1));
         assert_eq!(INFINITY.integer_decode(), (8388608, 105, 1));
         assert_eq!(NEG_INFINITY.integer_decode(), (8388608, 105, -1));
-        assert_eq!(NAN.integer_decode(), (12582912, 105, 1));
+
+        // Ignore the "sign" (quiet / signalling flag) of NAN.
+        // It can vary between runtime operations and LLVM folding.
+        let (nan_m, nan_e, _nan_s) = NAN.integer_decode();
+        assert_eq!((nan_m, nan_e), (12582912, 105));
     }
 
     #[test]
@@ -1699,6 +1727,7 @@ mod tests {
     }
 
     #[test]
+    #[allow(deprecated)]
     fn test_ldexp() {
         let f1 = 2.0f32.powi(-123);
         let f2 = 2.0f32.powi(-111);
@@ -1719,6 +1748,7 @@ mod tests {
     }
 
     #[test]
+    #[allow(deprecated)]
     fn test_frexp() {
         let f1 = 2.0f32.powi(-123);
         let f2 = 2.0f32.powi(-111);
@@ -1738,6 +1768,7 @@ mod tests {
     }
 
     #[test] #[cfg_attr(windows, ignore)] // FIXME #8755
+    #[allow(deprecated)]
     fn test_frexp_nowin() {
         let inf: f32 = f32::INFINITY;
         let neg_inf: f32 = f32::NEG_INFINITY;
@@ -1747,24 +1778,6 @@ mod tests {
         assert!(match nan.frexp() { (x, _) => x.is_nan() })
     }
 
-    #[test]
-    fn test_abs_sub() {
-        assert_eq!((-1f32).abs_sub(1f32), 0f32);
-        assert_eq!(1f32.abs_sub(1f32), 0f32);
-        assert_eq!(1f32.abs_sub(0f32), 1f32);
-        assert_eq!(1f32.abs_sub(-1f32), 2f32);
-        assert_eq!(NEG_INFINITY.abs_sub(0f32), 0f32);
-        assert_eq!(INFINITY.abs_sub(1f32), INFINITY);
-        assert_eq!(0f32.abs_sub(NEG_INFINITY), INFINITY);
-        assert_eq!(0f32.abs_sub(INFINITY), 0f32);
-    }
-
-    #[test]
-    fn test_abs_sub_nowin() {
-        assert!(NAN.abs_sub(-1f32).is_nan());
-        assert!(1f32.abs_sub(NAN).is_nan());
-    }
-
     #[test]
     fn test_asinh() {
         assert_eq!(0.0f32.asinh(), 0.0f32);
index 2beffb64d3dc44ecb0a75d6d1cd8a120c6aa0524..67a1c302483d20644bdd157def4bed445c47e6fa 100644 (file)
@@ -147,23 +147,23 @@ impl f64 {
     /// [subnormal][subnormal], or `NaN`.
     ///
     /// ```
-    /// use std::f32;
+    /// use std::f64;
     ///
-    /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f64
-    /// let max = f32::MAX;
-    /// let lower_than_min = 1.0e-40_f32;
-    /// let zero = 0.0f32;
+    /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308f64
+    /// let max = f64::MAX;
+    /// let lower_than_min = 1.0e-308_f64;
+    /// let zero = 0.0f64;
     ///
     /// assert!(min.is_normal());
     /// assert!(max.is_normal());
     ///
     /// assert!(!zero.is_normal());
-    /// assert!(!f32::NAN.is_normal());
-    /// assert!(!f32::INFINITY.is_normal());
+    /// assert!(!f64::NAN.is_normal());
+    /// assert!(!f64::INFINITY.is_normal());
     /// // Values between `0` and `min` are Subnormal.
     /// assert!(!lower_than_min.is_normal());
     /// ```
-    /// [subnormal]: http://en.wikipedia.org/wiki/Denormal_number
+    /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn is_normal(self) -> bool { num::Float::is_normal(self) }
@@ -209,7 +209,11 @@ impl f64 {
     /// [floating-point]: ../reference.html#machine-types
     #[unstable(feature = "float_extras", reason = "signature is undecided",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     #[inline]
+    #[allow(deprecated)]
     pub fn integer_decode(self) -> (u64, i16, i8) { num::Float::integer_decode(self) }
 
     /// Returns the largest integer less than or equal to a number.
@@ -613,6 +617,9 @@ impl f64 {
     #[unstable(feature = "float_extras",
                reason = "pending integer conventions",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     #[inline]
     pub fn ldexp(x: f64, exp: isize) -> f64 {
         unsafe { cmath::ldexp(x, exp as c_int) }
@@ -640,6 +647,9 @@ impl f64 {
     #[unstable(feature = "float_extras",
                reason = "pending integer conventions",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     #[inline]
     pub fn frexp(self) -> (f64, isize) {
         unsafe {
@@ -655,15 +665,18 @@ impl f64 {
     /// ```
     /// #![feature(float_extras)]
     ///
-    /// let x = 1.0f32;
+    /// let x = 1.0f64;
     ///
-    /// let abs_diff = (x.next_after(2.0) - 1.00000011920928955078125_f32).abs();
+    /// let abs_diff = (x.next_after(2.0) - 1.0000000000000002220446049250313_f64).abs();
     ///
     /// assert!(abs_diff < 1e-10);
     /// ```
     #[unstable(feature = "float_extras",
                reason = "unsure about its place in the world",
                issue = "27752")]
+    #[rustc_deprecated(since = "1.11.0",
+                       reason = "never really came to fruition and easily \
+                                 implementable outside the standard library")]
     #[inline]
     pub fn next_after(self, other: f64) -> f64 {
         unsafe { cmath::nextafter(self, other) }
@@ -718,9 +731,16 @@ impl f64 {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
-    pub fn abs_sub(self, other: f64) -> f64 {
-        unsafe { cmath::fdim(self, other) }
-    }
+    #[rustc_deprecated(since = "1.10.0",
+                       reason = "you probably meant `(self - other).abs()`: \
+                                 this operation is `(self - other).max(0.0)` (also \
+                                 known as `fdim` in C). If you truly need the positive \
+                                 difference, consider using that expression or the C function \
+                                 `fdim`, depending on how you wish to handle NaN (please consider \
+                                 filing an issue describing your use-case too).")]
+     pub fn abs_sub(self, other: f64) -> f64 {
+         unsafe { cmath::fdim(self, other) }
+     }
 
     /// Takes the cubic root of a number.
     ///
@@ -1270,7 +1290,7 @@ mod tests {
     }
 
     #[test]
-    #[rustc_no_mir] // FIXME #27840 MIR NAN ends up negative.
+    #[allow(deprecated)]
     fn test_integer_decode() {
         assert_eq!(3.14159265359f64.integer_decode(), (7074237752028906, -51, 1));
         assert_eq!((-8573.5918555f64).integer_decode(), (4713381968463931, -39, -1));
@@ -1279,7 +1299,11 @@ mod tests {
         assert_eq!((-0f64).integer_decode(), (0, -1075, -1));
         assert_eq!(INFINITY.integer_decode(), (4503599627370496, 972, 1));
         assert_eq!(NEG_INFINITY.integer_decode(), (4503599627370496, 972, -1));
-        assert_eq!(NAN.integer_decode(), (6755399441055744, 972, 1));
+
+        // Ignore the "sign" (quiet / signalling flag) of NAN.
+        // It can vary between runtime operations and LLVM folding.
+        let (nan_m, nan_e, _nan_s) = NAN.integer_decode();
+        assert_eq!((nan_m, nan_e), (6755399441055744, 972));
     }
 
     #[test]
@@ -1594,6 +1618,7 @@ mod tests {
     }
 
     #[test]
+    #[allow(deprecated)]
     fn test_ldexp() {
         let f1 = 2.0f64.powi(-123);
         let f2 = 2.0f64.powi(-111);
@@ -1614,6 +1639,7 @@ mod tests {
     }
 
     #[test]
+    #[allow(deprecated)]
     fn test_frexp() {
         let f1 = 2.0f64.powi(-123);
         let f2 = 2.0f64.powi(-111);
@@ -1633,6 +1659,7 @@ mod tests {
     }
 
     #[test] #[cfg_attr(windows, ignore)] // FIXME #8755
+    #[allow(deprecated)]
     fn test_frexp_nowin() {
         let inf: f64 = INFINITY;
         let neg_inf: f64 = NEG_INFINITY;
@@ -1642,24 +1669,6 @@ mod tests {
         assert!(match nan.frexp() { (x, _) => x.is_nan() })
     }
 
-    #[test]
-    fn test_abs_sub() {
-        assert_eq!((-1f64).abs_sub(1f64), 0f64);
-        assert_eq!(1f64.abs_sub(1f64), 0f64);
-        assert_eq!(1f64.abs_sub(0f64), 1f64);
-        assert_eq!(1f64.abs_sub(-1f64), 2f64);
-        assert_eq!(NEG_INFINITY.abs_sub(0f64), 0f64);
-        assert_eq!(INFINITY.abs_sub(1f64), INFINITY);
-        assert_eq!(0f64.abs_sub(NEG_INFINITY), INFINITY);
-        assert_eq!(0f64.abs_sub(INFINITY), 0f64);
-    }
-
-    #[test]
-    fn test_abs_sub_nowin() {
-        assert!(NAN.abs_sub(-1f64).is_nan());
-        assert!(1f64.abs_sub(NAN).is_nan());
-    }
-
     #[test]
     fn test_asinh() {
         assert_eq!(0.0f64.asinh(), 0.0f64);
index d33df05acf2248fcea578cdc2672176aff1089dd..20804d62dfab663401f07293496b3c4d1066973d 100644 (file)
@@ -17,6 +17,7 @@
 #![allow(missing_docs)]
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[allow(deprecated)]
 pub use core::num::{Zero, One};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::num::{FpCategory, ParseIntError, ParseFloatError, TryFromIntError};
@@ -46,7 +47,6 @@ pub fn test_num<T>(ten: T, two: T) where
 
 #[cfg(test)]
 mod tests {
-    use super::*;
     use u8;
     use u16;
     use u32;
@@ -198,15 +198,14 @@ mod tests {
 
     #[test]
     fn test_pow() {
-        fn naive_pow<T: Mul<Output=T> + One + Copy>(base: T, exp: usize) -> T {
-            let one: T = T::one();
+        fn naive_pow<T: Mul<Output=T> + Copy>(one: T, base: T, exp: usize) -> T {
             (0..exp).fold(one, |acc, _| acc * base)
         }
         macro_rules! assert_pow {
             (($num:expr, $exp:expr) => $expected:expr) => {{
                 let result = $num.pow($exp);
                 assert_eq!(result, $expected);
-                assert_eq!(result, naive_pow($num, $exp));
+                assert_eq!(result, naive_pow(1, $num, $exp));
             }}
         }
         assert_pow!((3u32,     0 ) => 1);
index ce6e810592c6c62e4ce71b600f382d66678c9bcc..5e473a933a67671c8374ed7b79fe8c3a72a78422 100644 (file)
@@ -20,7 +20,8 @@
 
 use os::raw::c_long;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = c_long;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = c_long;
 
 #[doc(inline)]
 #[stable(feature = "raw_ext", since = "1.1.0")]
index 3fc3c5937f4da7fe9758b3179320eb355185dcf8..28958575eb229fc00529afa1a5f32195f171dc7b 100644 (file)
@@ -31,7 +31,8 @@ use os::unix::raw::{uid_t, gid_t};
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
 
 #[repr(C)]
 #[derive(Clone)]
index 83e0be0d158ec3b8747e5b99d0a7b00fcb2f2336..5da2540ceee6702bded065b32d888be984a69428 100644 (file)
@@ -30,7 +30,8 @@ use os::raw::c_long;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
 
 #[repr(C)]
 #[derive(Clone)]
index 9da400a69131b58c155c87eb4e32418cc1f9743f..bcd85f8e29af9eac0491f7d18e76c70b2607bc2c 100644 (file)
@@ -25,7 +25,8 @@ use os::raw::{c_long, c_short, c_uint, c_ulong};
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = c_ulong;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = c_ulong;
 
 #[doc(inline)]
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
index 989eef63d82cf27b38e2e276605a5898ad37f6c0..c755e5af8d94966baf771cf2ccb4b828a4683c0d 100644 (file)
@@ -30,7 +30,8 @@ use os::raw::c_long;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
 
 #[repr(C)]
 #[derive(Clone)]
index 5a2de14b28b0c4be4c2eb3a138ed02aa9f8ccd45..8b34a932a170a773ec36dc7cb7ec20cf2979b314 100644 (file)
@@ -29,7 +29,8 @@ use os::raw::c_long;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
 
 #[repr(C)]
 #[derive(Clone)]
index 4113966841b24bdcd0d98ef74ac0908d17b701ec..1be76961fea9efdfb9c04080ddbeb7422e306359 100644 (file)
@@ -23,7 +23,8 @@ use os::raw::c_ulong;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = c_ulong;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = c_ulong;
 
 #[doc(inline)]
 #[stable(feature = "raw_ext", since = "1.1.0")]
index 2148670ccbd2a5ec5b580f89870df6df627a3b57..8f9b29462c4f938bc584133bd4a064774940c366 100644 (file)
@@ -29,7 +29,8 @@ use os::raw::c_long;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
 
 #[repr(C)]
 #[derive(Clone)]
index 9a10fbcc30b72dac612a3f800eb58fa45e7bd359..3c3d4410a2a16bc89dc93499fb10eff32d5da164 100644 (file)
@@ -30,7 +30,8 @@
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
 
 #[repr(C)]
 #[derive(Copy, Clone)]
index bc30c1a7f48eb6c46b13f55e374bd2fc7da42c32..9b2e037e59a0bc7166779f236db1a6fcec3cb307 100644 (file)
@@ -31,7 +31,8 @@ use os::unix::raw::{uid_t, gid_t};
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
 
 #[repr(C)]
 #[derive(Clone)]
index 0e9a2128bc2a3ad84a0a82f2c665c181a45c2d98..6142f03621813b44ebb1b61206a155b1ac71016c 100644 (file)
@@ -30,7 +30,8 @@ use os::raw::c_long;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
 
 #[repr(C)]
 #[derive(Clone)]
index 6a75b36bd6bdea776308356e268e9bb32fef48d1..b84fdba9ca25cdb024989fa05ffce38ada0d3935 100644 (file)
@@ -31,7 +31,8 @@ use os::unix::raw::{uid_t, gid_t};
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
 
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
 
 #[repr(C)]
 #[derive(Clone)]
index 70e6f90293222270b9c9ccc63109c9a9cf67dc4e..d8cadf09cb2a4c3a3c8ae9ea78b6ab0059627b04 100644 (file)
@@ -25,28 +25,14 @@ use thread::Result;
 #[stable(feature = "panic_hooks", since = "1.10.0")]
 pub use panicking::{take_hook, set_hook, PanicInfo, Location};
 
-///
-#[rustc_deprecated(since = "1.9.0", reason = "renamed to set_hook")]
-#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
-pub fn set_handler<F>(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send {
-    set_hook(Box::new(handler))
-}
-
-///
-#[rustc_deprecated(since = "1.9.0", reason = "renamed to take_hook")]
-#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
-pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
-    take_hook()
-}
-
 /// A marker trait which represents "panic safe" types in Rust.
 ///
 /// This trait is implemented by default for many types and behaves similarly in
 /// terms of inference of implementation to the `Send` and `Sync` traits. The
-/// purpose of this trait is to encode what types are safe to cross a `recover`
-/// boundary with no fear of panic safety.
+/// purpose of this trait is to encode what types are safe to cross a `catch_unwind`
+/// boundary with no fear of unwind safety.
 ///
-/// ## What is panic safety?
+/// ## What is unwind safety?
 ///
 /// In Rust a function can "return" early if it either panics or calls a
 /// function which transitively panics. This sort of control flow is not always
@@ -59,74 +45,66 @@ pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
 ///
 /// Typically in Rust, it is difficult to perform step (2) because catching a
 /// panic involves either spawning a thread (which in turns makes it difficult
-/// to later witness broken invariants) or using the `recover` function in this
+/// to later witness broken invariants) or using the `catch_unwind` function in this
 /// module. Additionally, even if an invariant is witnessed, it typically isn't a
-/// problem in Rust because there's no uninitialized values (like in C or C++).
+/// problem in Rust because there are no uninitialized values (like in C or C++).
 ///
 /// It is possible, however, for **logical** invariants to be broken in Rust,
-/// which can end up causing behavioral bugs. Another key aspect of panic safety
+/// which can end up causing behavioral bugs. Another key aspect of unwind safety
 /// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to
 /// memory unsafety.
 ///
-/// That was a bit of a whirlwind tour of panic safety, but for more information
-/// about panic safety and how it applies to Rust, see an [associated RFC][rfc].
+/// That was a bit of a whirlwind tour of unwind safety, but for more information
+/// about unwind safety and how it applies to Rust, see an [associated RFC][rfc].
 ///
 /// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
 ///
 /// ## What is `UnwindSafe`?
 ///
-/// Now that we've got an idea of what panic safety is in Rust, it's also
+/// Now that we've got an idea of what unwind safety is in Rust, it's also
 /// important to understand what this trait represents. As mentioned above, one
-/// way to witness broken invariants is through the `recover` function in this
+/// way to witness broken invariants is through the `catch_unwind` function in this
 /// module as it allows catching a panic and then re-using the environment of
 /// the closure.
 ///
 /// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow
-/// witnessing a broken invariant through the use of `recover` (catching a
+/// witnessing a broken invariant through the use of `catch_unwind` (catching a
 /// panic). This trait is a marker trait, so it is automatically implemented for
-/// many types, and it is also structurally composed (e.g. a struct is recover
-/// safe if all of its components are recover safe).
+/// many types, and it is also structurally composed (e.g. a struct is unwind
+/// safe if all of its components are unwind safe).
 ///
 /// Note, however, that this is not an unsafe trait, so there is not a succinct
 /// contract that this trait is providing. Instead it is intended as more of a
-/// "speed bump" to alert users of `recover` that broken invariants may be
+/// "speed bump" to alert users of `catch_unwind` that broken invariants may be
 /// witnessed and may need to be accounted for.
 ///
 /// ## Who implements `UnwindSafe`?
 ///
 /// Types such as `&mut T` and `&RefCell<T>` are examples which are **not**
-/// recover safe. The general idea is that any mutable state which can be shared
-/// across `recover` is not recover safe by default. This is because it is very
-/// easy to witness a broken invariant outside of `recover` as the data is
+/// unwind safe. The general idea is that any mutable state which can be shared
+/// across `catch_unwind` is not unwind safe by default. This is because it is very
+/// easy to witness a broken invariant outside of `catch_unwind` as the data is
 /// simply accessed as usual.
 ///
-/// Types like `&Mutex<T>`, however, are recover safe because they implement
+/// Types like `&Mutex<T>`, however, are unwind safe because they implement
 /// poisoning by default. They still allow witnessing a broken invariant, but
 /// they already provide their own "speed bumps" to do so.
 ///
 /// ## When should `UnwindSafe` be used?
 ///
 /// Is not intended that most types or functions need to worry about this trait.
-/// It is only used as a bound on the `recover` function and as mentioned above,
+/// It is only used as a bound on the `catch_unwind` function and as mentioned above,
 /// the lack of `unsafe` means it is mostly an advisory. The `AssertUnwindSafe`
 /// wrapper struct in this module can be used to force this trait to be
-/// implemented for any closed over variables passed to the `recover` function
+/// implemented for any closed over variables passed to the `catch_unwind` function
 /// (more on this below).
 #[stable(feature = "catch_unwind", since = "1.9.0")]
 #[rustc_on_unimplemented = "the type {Self} may not be safely transferred \
-                            across a recover boundary"]
+                            across an unwind boundary"]
 pub trait UnwindSafe {}
 
-/// Deprecated, renamed to UnwindSafe
-#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
-#[rustc_deprecated(reason = "renamed to `UnwindSafe`", since = "1.9.0")]
-pub trait RecoverSafe {}
-#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
-#[allow(deprecated)]
-impl<T: UnwindSafe> RecoverSafe for T {}
-
 /// A marker trait representing types where a shared reference is considered
-/// recover safe.
+/// unwind safe.
 ///
 /// This trait is namely not implemented by `UnsafeCell`, the root of all
 /// interior mutability.
@@ -136,23 +114,23 @@ impl<T: UnwindSafe> RecoverSafe for T {}
 #[stable(feature = "catch_unwind", since = "1.9.0")]
 #[rustc_on_unimplemented = "the type {Self} contains interior mutability \
                             and a reference may not be safely transferrable \
-                            across a recover boundary"]
+                            across a catch_unwind boundary"]
 pub trait RefUnwindSafe {}
 
-/// A simple wrapper around a type to assert that it is panic safe.
+/// A simple wrapper around a type to assert that it is unwind safe.
 ///
-/// When using `recover` it may be the case that some of the closed over
-/// variables are not panic safe. For example if `&mut T` is captured the
-/// compiler will generate a warning indicating that it is not panic safe. It
+/// When using `catch_unwind` it may be the case that some of the closed over
+/// variables are not unwind safe. For example if `&mut T` is captured the
+/// compiler will generate a warning indicating that it is not unwind safe. It
 /// may not be the case, however, that this is actually a problem due to the
-/// specific usage of `recover` if panic safety is specifically taken into
+/// specific usage of `catch_unwind` if unwind safety is specifically taken into
 /// account. This wrapper struct is useful for a quick and lightweight
-/// annotation that a variable is indeed panic safe.
+/// annotation that a variable is indeed unwind safe.
 ///
 /// # Examples
 ///
 /// One way to use `AssertUnwindSafe` is to assert that the entire closure
-/// itself is recover safe, bypassing all checks for all variables:
+/// itself is unwind safe, bypassing all checks for all variables:
 ///
 /// ```
 /// use std::panic::{self, AssertUnwindSafe};
@@ -160,7 +138,7 @@ pub trait RefUnwindSafe {}
 /// let mut variable = 4;
 ///
 /// // This code will not compile because the closure captures `&mut variable`
-/// // which is not considered panic safe by default.
+/// // which is not considered unwind safe by default.
 ///
 /// // panic::catch_unwind(|| {
 /// //     variable += 3;
@@ -202,11 +180,6 @@ pub struct AssertUnwindSafe<T>(
     pub T
 );
 
-/// Deprecated, renamed to `AssertUnwindSafe`
-#[unstable(feature = "recover", issue = "27719")]
-#[rustc_deprecated(reason = "renamed to `AssertUnwindSafe`", since = "1.9.0")]
-pub struct AssertRecoverSafe<T>(pub T);
-
 // Implementations of the `UnwindSafe` trait:
 //
 // * By default everything is unwind safe
@@ -234,12 +207,9 @@ impl<T: ?Sized> UnwindSafe for Mutex<T> {}
 impl<T: ?Sized> UnwindSafe for RwLock<T> {}
 #[stable(feature = "catch_unwind", since = "1.9.0")]
 impl<T> UnwindSafe for AssertUnwindSafe<T> {}
-#[unstable(feature = "recover", issue = "27719")]
-#[allow(deprecated)]
-impl<T> UnwindSafe for AssertRecoverSafe<T> {}
 
 // not covered via the Shared impl above b/c the inner contents use
-// Cell/AtomicUsize, but the usage here is recover safe so we can lift the
+// Cell/AtomicUsize, but the usage here is unwind safe so we can lift the
 // impl up one level to Arc/Rc itself
 #[stable(feature = "catch_unwind", since = "1.9.0")]
 impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Rc<T> {}
@@ -256,9 +226,6 @@ impl RefUnwindSafe for .. {}
 impl<T: ?Sized> !RefUnwindSafe for UnsafeCell<T> {}
 #[stable(feature = "catch_unwind", since = "1.9.0")]
 impl<T> RefUnwindSafe for AssertUnwindSafe<T> {}
-#[unstable(feature = "recover", issue = "27719")]
-#[allow(deprecated)]
-impl<T> RefUnwindSafe for AssertRecoverSafe<T> {}
 
 #[stable(feature = "catch_unwind", since = "1.9.0")]
 impl<T> Deref for AssertUnwindSafe<T> {
@@ -285,53 +252,6 @@ impl<R, F: FnOnce() -> R> FnOnce<()> for AssertUnwindSafe<F> {
     }
 }
 
-#[allow(deprecated)]
-impl<T> AssertRecoverSafe<T> {
-    /// Creates a new `AssertRecoverSafe` wrapper around the provided type.
-    #[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
-    #[rustc_deprecated(reason = "the type's field is now public, construct it directly",
-                       since = "1.9.0")]
-    pub fn new(t: T) -> AssertRecoverSafe<T> {
-        AssertRecoverSafe(t)
-    }
-
-    /// Consumes the `AssertRecoverSafe`, returning the wrapped value.
-    #[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
-    #[rustc_deprecated(reason = "the type's field is now public, access it directly",
-                       since = "1.9.0")]
-    pub fn into_inner(self) -> T {
-        self.0
-    }
-}
-
-#[unstable(feature = "recover", issue = "27719")]
-#[allow(deprecated)]
-impl<T> Deref for AssertRecoverSafe<T> {
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        &self.0
-    }
-}
-
-#[unstable(feature = "recover", issue = "27719")]
-#[allow(deprecated)]
-impl<T> DerefMut for AssertRecoverSafe<T> {
-    fn deref_mut(&mut self) -> &mut T {
-        &mut self.0
-    }
-}
-
-#[unstable(feature = "recover", issue = "27719")]
-#[allow(deprecated)]
-impl<R, F: FnOnce() -> R> FnOnce<()> for AssertRecoverSafe<F> {
-    type Output = R;
-
-    extern "rust-call" fn call_once(self, _args: ()) -> R {
-        (self.0)()
-    }
-}
-
 /// Invokes a closure, capturing the cause of an unwinding panic if one occurs.
 ///
 /// This function will return `Ok` with the closure's result if the closure
@@ -352,9 +272,9 @@ impl<R, F: FnOnce() -> R> FnOnce<()> for AssertRecoverSafe<F> {
 /// that all captured variables are safe to cross this boundary. The purpose of
 /// this bound is to encode the concept of [exception safety][rfc] in the type
 /// system. Most usage of this function should not need to worry about this
-/// bound as programs are naturally panic safe without `unsafe` code. If it
+/// bound as programs are naturally unwind safe without `unsafe` code. If it
 /// becomes a problem the associated `AssertUnwindSafe` wrapper type in this
-/// module can be used to quickly assert that the usage here is indeed exception
+/// module can be used to quickly assert that the usage here is indeed unwind
 /// safe.
 ///
 /// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
@@ -388,13 +308,6 @@ pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
     }
 }
 
-/// Deprecated, renamed to `catch_unwind`
-#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
-#[rustc_deprecated(reason = "renamed to `catch_unwind`", since = "1.9.0")]
-pub fn recover<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
-    catch_unwind(f)
-}
-
 /// Triggers a panic without invoking the panic hook.
 ///
 /// This is designed to be used in conjunction with `catch_unwind` to, for
@@ -424,10 +337,3 @@ pub fn recover<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
 pub fn resume_unwind(payload: Box<Any + Send>) -> ! {
     panicking::rust_panic(payload)
 }
-
-/// Deprecated, use resume_unwind instead
-#[unstable(feature = "panic_propagate", reason = "awaiting feedback", issue = "30752")]
-#[rustc_deprecated(reason = "renamed to `resume_unwind`", since = "1.9.0")]
-pub fn propagate(payload: Box<Any + Send>) -> ! {
-    resume_unwind(payload)
-}
index f413bed86a8534d3c67844d380629ea49cf756bc..ad4cdef615847719d2f63ff078b8f36346a302e9 100644 (file)
@@ -525,6 +525,26 @@ impl<'a> Hash for PrefixComponent<'a> {
 ///
 /// See the module documentation for an in-depth explanation of components and
 /// their role in the API.
+///
+/// This `enum` is created from iterating over the [`path::Components`]
+/// `struct`.
+///
+/// # Examples
+///
+/// ```rust
+/// use std::path::{Component, Path};
+///
+/// let path = Path::new("/tmp/foo/bar.txt");
+/// let components = path.components().collect::<Vec<_>>();
+/// assert_eq!(&components, &[
+///     Component::RootDir,
+///     Component::Normal("tmp".as_ref()),
+///     Component::Normal("foo".as_ref()),
+///     Component::Normal("bar.txt".as_ref()),
+/// ]);
+/// ```
+///
+/// [`path::Components`]: struct.Components.html
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub enum Component<'a> {
@@ -579,6 +599,8 @@ impl<'a> AsRef<OsStr> for Component<'a> {
 /// See the module documentation for an in-depth explanation of components and
 /// their role in the API.
 ///
+/// This `struct` is created by the [`path::Path::components`] method.
+///
 /// # Examples
 ///
 /// ```
@@ -590,6 +612,8 @@ impl<'a> AsRef<OsStr> for Component<'a> {
 ///     println!("{:?}", component);
 /// }
 /// ```
+///
+/// [`path::Path::components`]: struct.Path.html#method.components
 #[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Components<'a> {
@@ -1033,7 +1057,6 @@ impl PathBuf {
         self._push(path.as_ref())
     }
 
-    #[allow(deprecated)]
     fn _push(&mut self, path: &Path) {
         // in general, a separator is needed if the rightmost byte is not a separator
         let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false);
@@ -1419,8 +1442,7 @@ impl Path {
     /// `is_absolute` and `has_root` are equivalent.
     ///
     /// * On Windows, a path is absolute if it has a prefix and starts with the
-    /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. In
-    /// other words, `path.is_absolute() == path.prefix().is_some() && path.has_root()`.
+    /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not.
     ///
     /// # Examples
     ///
@@ -1781,7 +1803,9 @@ impl Path {
     /// This function will traverse symbolic links to query information about the
     /// destination file.
     ///
-    /// This is an alias to `fs::metadata`.
+    /// This is an alias to [`fs::metadata`].
+    ///
+    /// [`fs::metadata`]: ../fs/fn.metadata.html
     #[stable(feature = "path_ext", since = "1.5.0")]
     pub fn metadata(&self) -> io::Result<fs::Metadata> {
         fs::metadata(self)
@@ -1789,7 +1813,9 @@ impl Path {
 
     /// Query the metadata about a file without following symlinks.
     ///
-    /// This is an alias to `fs::symlink_metadata`.
+    /// This is an alias to [`fs::symlink_metadata`].
+    ///
+    /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html
     #[stable(feature = "path_ext", since = "1.5.0")]
     pub fn symlink_metadata(&self) -> io::Result<fs::Metadata> {
         fs::symlink_metadata(self)
@@ -1798,7 +1824,9 @@ impl Path {
     /// Returns the canonical form of the path with all intermediate components
     /// normalized and symbolic links resolved.
     ///
-    /// This is an alias to `fs::canonicalize`.
+    /// This is an alias to [`fs::canonicalize`].
+    ///
+    /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html
     #[stable(feature = "path_ext", since = "1.5.0")]
     pub fn canonicalize(&self) -> io::Result<PathBuf> {
         fs::canonicalize(self)
@@ -1806,7 +1834,9 @@ impl Path {
 
     /// Reads a symbolic link, returning the file that the link points to.
     ///
-    /// This is an alias to `fs::read_link`.
+    /// This is an alias to [`fs::read_link`].
+    ///
+    /// [`fs::read_link`]: ../fs/fn.read_link.html
     #[stable(feature = "path_ext", since = "1.5.0")]
     pub fn read_link(&self) -> io::Result<PathBuf> {
         fs::read_link(self)
@@ -1817,7 +1847,9 @@ impl Path {
     /// The iterator will yield instances of `io::Result<DirEntry>`. New errors may
     /// be encountered after an iterator is initially constructed.
     ///
-    /// This is an alias to `fs::read_dir`.
+    /// This is an alias to [`fs::read_dir`].
+    ///
+    /// [`fs::read_dir`]: ../fs/fn.read_dir.html
     #[stable(feature = "path_ext", since = "1.5.0")]
     pub fn read_dir(&self) -> io::Result<fs::ReadDir> {
         fs::read_dir(self)
index e083605a2acd5bf1ed38d6b1d7b29023b38cd296..be9cd6a688858ff2fc6b8da382babd1d0c496892 100644 (file)
@@ -28,7 +28,7 @@
 /// ```
 ///
 /// [`assert!`]: macro.assert!.html
-/// [`if` conditionals]: ../book/if.html
+/// [`if`]: ../book/if.html
 /// [`BitAnd`]: ops/trait.BitAnd.html
 /// [`BitOr`]: ops/trait.BitOr.html
 /// [`Not`]: ops/trait.Not.html
@@ -490,9 +490,6 @@ mod prim_tuple { }
 ///
 /// *[See also the `std::f32` module](f32/index.html).*
 ///
-/// However, please note that examples are shared between the `f64` and `f32`
-/// primitive types. So it's normal if you see usage of `f64` in there.
-///
 mod prim_f32 { }
 
 #[doc(primitive = "f64")]
@@ -501,9 +498,6 @@ mod prim_f32 { }
 ///
 /// *[See also the `std::f64` module](f64/index.html).*
 ///
-/// However, please note that examples are shared between the `f64` and `f32`
-/// primitive types. So it's normal if you see usage of `f32` in there.
-///
 mod prim_f64 { }
 
 #[doc(primitive = "i8")]
index 1b6f6c3e875c98f12267fd362054b326d32046b3..2edd1a3063810712f56937201c70b398143d8ddc 100644 (file)
@@ -182,8 +182,10 @@ impl FromInner<AnonPipe> for ChildStderr {
     }
 }
 
-/// The `Command` type acts as a process builder, providing fine-grained control
-/// over how a new process should be spawned. A default configuration can be
+/// A process builder, providing fine-grained control
+/// over how a new process should be spawned.
+///
+/// A default configuration can be
 /// generated using `Command::new(program)`, where `program` gives a path to the
 /// program to be executed. Additional builder methods allow the configuration
 /// to be changed (for example, by adding arguments) prior to spawning:
@@ -195,7 +197,7 @@ impl FromInner<AnonPipe> for ChildStderr {
 ///                      .arg("-c")
 ///                      .arg("echo hello")
 ///                      .output()
-///                      .expect("failed to execute proces");
+///                      .expect("failed to execute process");
 ///
 /// let hello = output.stdout;
 /// ```
@@ -711,16 +713,17 @@ impl Child {
     /// ```should_panic
     /// use std::process::{Command, Stdio};
     ///
-    /// let mut child = Command::new("/bin/cat")
-    ///                         .arg("file.txt")
-    ///                         .stdout(Stdio::piped())
-    ///                         .spawn()
-    ///                         .expect("failed to execute child");
+    /// let child = Command::new("/bin/cat")
+    ///     .arg("file.txt")
+    ///     .stdout(Stdio::piped())
+    ///     .spawn()
+    ///     .expect("failed to execute child");
     ///
-    /// let ecode = child.wait_with_output()
-    ///                  .expect("failed to wait on child");
+    /// let output = child
+    ///     .wait_with_output()
+    ///     .expect("failed to wait on child");
     ///
-    /// assert!(ecode.status.success());
+    /// assert!(output.status.success());
     /// ```
     ///
     #[stable(feature = "process", since = "1.0.0")]
index 6eee4ee9bbe5f66edc86d89c8fe451984025356e..5a7c0fe4816c981334849c6fd2d5a91602fe08f2 100644 (file)
@@ -27,9 +27,6 @@
 // Reexport some of our utilities which are expected by other crates.
 pub use panicking::{begin_panic, begin_panic_fmt};
 
-#[cfg(stage0)]
-pub use panicking::begin_panic as begin_unwind;
-
 #[cfg(not(test))]
 #[lang = "start"]
 fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
@@ -51,7 +48,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
         // created. Note that this isn't necessary in general for new threads,
         // but we just do this to name the main thread and to give it correct
         // info about the stack bounds.
-        let thread: Thread = NewThread::new(Some("<main>".to_owned()));
+        let thread: Thread = NewThread::new(Some("main".to_owned()));
         thread_info::set(main_guard, thread);
 
         // Store our args if necessary in a squirreled away location
index 0e5a98591168b5d6864a092f6a3b0a55d9ce5c6a..4a70de0e7d8fccf7bba8b7de64fc53339c1e5ef2 100644 (file)
@@ -16,6 +16,7 @@ use sync::Arc;
 use marker::{Sync, Send};
 use mem;
 use clone::Clone;
+use time::Instant;
 
 struct Inner {
     thread: Thread,
@@ -74,7 +75,6 @@ impl SignalToken {
     pub unsafe fn cast_from_usize(signal_ptr: usize) -> SignalToken {
         SignalToken { inner: mem::transmute(signal_ptr) }
     }
-
 }
 
 impl WaitToken {
@@ -83,4 +83,16 @@ impl WaitToken {
             thread::park()
         }
     }
+
+    /// Returns true if we wake up normally, false otherwise.
+    pub fn wait_max_until(self, end: Instant) -> bool {
+        while !self.inner.woken.load(Ordering::SeqCst) {
+            let now = Instant::now();
+            if now >= end {
+                return false;
+            }
+            thread::park_timeout(end - now)
+        }
+        true
+    }
 }
index 63b659d8db3b7b2aa85ada196f3c1cfb52b7f1f5..34bc210b3c8239eb3bfef6261653a5a35a610a82 100644 (file)
 // senders. Under the hood, however, there are actually three flavors of
 // channels in play.
 //
-// * Flavor::Oneshots - these channels are highly optimized for the one-send use case.
-//              They contain as few atomics as possible and involve one and
-//              exactly one allocation.
+// * Flavor::Oneshots - these channels are highly optimized for the one-send use
+//                      case. They contain as few atomics as possible and
+//                      involve one and exactly one allocation.
 // * Streams - these channels are optimized for the non-shared use case. They
 //             use a different concurrent queue that is more tailored for this
 //             use case. The initial allocation of this flavor of channel is not
 //
 // ## Concurrent queues
 //
-// The basic idea of Rust's Sender/Receiver types is that send() never blocks, but
-// recv() obviously blocks. This means that under the hood there must be some
-// shared and concurrent queue holding all of the actual data.
+// The basic idea of Rust's Sender/Receiver types is that send() never blocks,
+// but recv() obviously blocks. This means that under the hood there must be
+// some shared and concurrent queue holding all of the actual data.
 //
 // With two flavors of channels, two flavors of queues are also used. We have
 // chosen to use queues from a well-known author that are abbreviated as SPSC
@@ -271,6 +271,7 @@ use fmt;
 use mem;
 use cell::UnsafeCell;
 use marker::Reflect;
+use time::{Duration, Instant};
 
 #[unstable(feature = "mpsc_select", issue = "27800")]
 pub use self::select::{Select, Handle};
@@ -379,6 +380,19 @@ pub enum TryRecvError {
     Disconnected,
 }
 
+/// This enumeration is the list of possible errors that `recv_timeout` could
+/// not return data when called.
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+#[unstable(feature = "mpsc_recv_timeout", issue = "34029")]
+pub enum RecvTimeoutError {
+    /// This channel is currently empty, but the sender(s) have not yet
+    /// disconnected, so data may yet become available.
+    Timeout,
+    /// This channel's sending half has become disconnected, and there will
+    /// never be any more data received on this channel
+    Disconnected,
+}
+
 /// This enumeration is the list of the possible error outcomes for the
 /// `SyncSender::try_send` method.
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -838,30 +852,30 @@ impl<T> Receiver<T> {
         loop {
             let new_port = match *unsafe { self.inner() } {
                 Flavor::Oneshot(ref p) => {
-                    match unsafe { (*p.get()).recv() } {
+                    match unsafe { (*p.get()).recv(None) } {
                         Ok(t) => return Ok(t),
-                        Err(oneshot::Empty) => return unreachable!(),
                         Err(oneshot::Disconnected) => return Err(RecvError),
                         Err(oneshot::Upgraded(rx)) => rx,
+                        Err(oneshot::Empty) => unreachable!(),
                     }
                 }
                 Flavor::Stream(ref p) => {
-                    match unsafe { (*p.get()).recv() } {
+                    match unsafe { (*p.get()).recv(None) } {
                         Ok(t) => return Ok(t),
-                        Err(stream::Empty) => return unreachable!(),
                         Err(stream::Disconnected) => return Err(RecvError),
                         Err(stream::Upgraded(rx)) => rx,
+                        Err(stream::Empty) => unreachable!(),
                     }
                 }
                 Flavor::Shared(ref p) => {
-                    match unsafe { (*p.get()).recv() } {
+                    match unsafe { (*p.get()).recv(None) } {
                         Ok(t) => return Ok(t),
-                        Err(shared::Empty) => return unreachable!(),
                         Err(shared::Disconnected) => return Err(RecvError),
+                        Err(shared::Empty) => unreachable!(),
                     }
                 }
                 Flavor::Sync(ref p) => return unsafe {
-                    (*p.get()).recv().map_err(|()| RecvError)
+                    (*p.get()).recv(None).map_err(|_| RecvError)
                 }
             };
             unsafe {
@@ -870,6 +884,98 @@ impl<T> Receiver<T> {
         }
     }
 
+    /// Attempts to wait for a value on this receiver, returning an error if the
+    /// corresponding channel has hung up, or if it waits more than `timeout`.
+    ///
+    /// This function will always block the current thread if there is no data
+    /// available and it's possible for more data to be sent. Once a message is
+    /// sent to the corresponding `Sender`, then this receiver will wake up and
+    /// return that message.
+    ///
+    /// If the corresponding `Sender` has disconnected, or it disconnects while
+    /// this call is blocking, this call will wake up and return `Err` to
+    /// indicate that no more messages can ever be received on this channel.
+    /// However, since channels are buffered, messages sent before the disconnect
+    /// will still be properly received.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(mpsc_recv_timeout)]
+    ///
+    /// use std::sync::mpsc::{self, RecvTimeoutError};
+    /// use std::time::Duration;
+    ///
+    /// let (send, recv) = mpsc::channel::<()>();
+    ///
+    /// let timeout = Duration::from_millis(100);
+    /// assert_eq!(Err(RecvTimeoutError::Timeout), recv.recv_timeout(timeout));
+    /// ```
+    #[unstable(feature = "mpsc_recv_timeout", issue = "34029")]
+    pub fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError> {
+        // Do an optimistic try_recv to avoid the performance impact of
+        // Instant::now() in the full-channel case.
+        match self.try_recv() {
+            Ok(result)
+                => Ok(result),
+            Err(TryRecvError::Disconnected)
+                => Err(RecvTimeoutError::Disconnected),
+            Err(TryRecvError::Empty)
+                => self.recv_max_until(Instant::now() + timeout)
+        }
+    }
+
+    fn recv_max_until(&self, deadline: Instant) -> Result<T, RecvTimeoutError> {
+        use self::RecvTimeoutError::*;
+
+        loop {
+            let port_or_empty = match *unsafe { self.inner() } {
+                Flavor::Oneshot(ref p) => {
+                    match unsafe { (*p.get()).recv(Some(deadline)) } {
+                        Ok(t) => return Ok(t),
+                        Err(oneshot::Disconnected) => return Err(Disconnected),
+                        Err(oneshot::Upgraded(rx)) => Some(rx),
+                        Err(oneshot::Empty) => None,
+                    }
+                }
+                Flavor::Stream(ref p) => {
+                    match unsafe { (*p.get()).recv(Some(deadline)) } {
+                        Ok(t) => return Ok(t),
+                        Err(stream::Disconnected) => return Err(Disconnected),
+                        Err(stream::Upgraded(rx)) => Some(rx),
+                        Err(stream::Empty) => None,
+                    }
+                }
+                Flavor::Shared(ref p) => {
+                    match unsafe { (*p.get()).recv(Some(deadline)) } {
+                        Ok(t) => return Ok(t),
+                        Err(shared::Disconnected) => return Err(Disconnected),
+                        Err(shared::Empty) => None,
+                    }
+                }
+                Flavor::Sync(ref p) => {
+                    match unsafe { (*p.get()).recv(Some(deadline)) } {
+                        Ok(t) => return Ok(t),
+                        Err(sync::Disconnected) => return Err(Disconnected),
+                        Err(sync::Empty) => None,
+                    }
+                }
+            };
+
+            if let Some(new_port) = port_or_empty {
+                unsafe {
+                    mem::swap(self.inner_mut(), new_port.inner_mut());
+                }
+            }
+
+            // If we're already passed the deadline, and we're here without
+            // data, return a timeout, else try again.
+            if Instant::now() >= deadline {
+                return Err(Timeout);
+            }
+        }
+    }
+
     /// Returns an iterator that will block waiting for messages, but never
     /// `panic!`. It will return `None` when the channel has hung up.
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -1141,6 +1247,7 @@ mod tests {
     use env;
     use super::*;
     use thread;
+    use time::{Duration, Instant};
 
     pub fn stress_factor() -> usize {
         match env::var("RUST_TEST_STRESS") {
@@ -1539,6 +1646,87 @@ mod tests {
         }
     }
 
+    #[test]
+    fn oneshot_single_thread_recv_timeout() {
+        let (tx, rx) = channel();
+        tx.send(()).unwrap();
+        assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
+        assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
+        tx.send(()).unwrap();
+        assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
+    }
+
+    #[test]
+    fn stress_recv_timeout_two_threads() {
+        let (tx, rx) = channel();
+        let stress = stress_factor() + 100;
+        let timeout = Duration::from_millis(100);
+
+        thread::spawn(move || {
+            for i in 0..stress {
+                if i % 2 == 0 {
+                    thread::sleep(timeout * 2);
+                }
+                tx.send(1usize).unwrap();
+            }
+        });
+
+        let mut recv_count = 0;
+        loop {
+            match rx.recv_timeout(timeout) {
+                Ok(n) => {
+                    assert_eq!(n, 1usize);
+                    recv_count += 1;
+                }
+                Err(RecvTimeoutError::Timeout) => continue,
+                Err(RecvTimeoutError::Disconnected) => break,
+            }
+        }
+
+        assert_eq!(recv_count, stress);
+    }
+
+    #[test]
+    fn recv_timeout_upgrade() {
+        let (tx, rx) = channel::<()>();
+        let timeout = Duration::from_millis(1);
+        let _tx_clone = tx.clone();
+
+        let start = Instant::now();
+        assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout));
+        assert!(Instant::now() >= start + timeout);
+    }
+
+    #[test]
+    fn stress_recv_timeout_shared() {
+        let (tx, rx) = channel();
+        let stress = stress_factor() + 100;
+
+        for i in 0..stress {
+            let tx = tx.clone();
+            thread::spawn(move || {
+                thread::sleep(Duration::from_millis(i as u64 * 10));
+                tx.send(1usize).unwrap();
+            });
+        }
+
+        drop(tx);
+
+        let mut recv_count = 0;
+        loop {
+            match rx.recv_timeout(Duration::from_millis(10)) {
+                Ok(n) => {
+                    assert_eq!(n, 1usize);
+                    recv_count += 1;
+                }
+                Err(RecvTimeoutError::Timeout) => continue,
+                Err(RecvTimeoutError::Disconnected) => break,
+            }
+        }
+
+        assert_eq!(recv_count, stress);
+    }
+
     #[test]
     fn recv_a_lot() {
         // Regression test that we don't run out of stack in scheduler context
@@ -1547,6 +1735,24 @@ mod tests {
         for _ in 0..10000 { rx.recv().unwrap(); }
     }
 
+    #[test]
+    fn shared_recv_timeout() {
+        let (tx, rx) = channel();
+        let total = 5;
+        for _ in 0..total {
+            let tx = tx.clone();
+            thread::spawn(move|| {
+                tx.send(()).unwrap();
+            });
+        }
+
+        for _ in 0..total { rx.recv().unwrap(); }
+
+        assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
+        tx.send(()).unwrap();
+        assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
+    }
+
     #[test]
     fn shared_chan_stress() {
         let (tx, rx) = channel();
@@ -1689,6 +1895,7 @@ mod sync_tests {
     use env;
     use thread;
     use super::*;
+    use time::Duration;
 
     pub fn stress_factor() -> usize {
         match env::var("RUST_TEST_STRESS") {
@@ -1720,6 +1927,14 @@ mod sync_tests {
         assert_eq!(rx.recv().unwrap(), 1);
     }
 
+    #[test]
+    fn recv_timeout() {
+        let (tx, rx) = sync_channel::<i32>(1);
+        assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
+        tx.send(1).unwrap();
+        assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1));
+    }
+
     #[test]
     fn smoke_threads() {
         let (tx, rx) = sync_channel::<i32>(0);
@@ -1801,6 +2016,67 @@ mod sync_tests {
         }
     }
 
+    #[test]
+    fn stress_recv_timeout_two_threads() {
+        let (tx, rx) = sync_channel::<i32>(0);
+
+        thread::spawn(move|| {
+            for _ in 0..10000 { tx.send(1).unwrap(); }
+        });
+
+        let mut recv_count = 0;
+        loop {
+            match rx.recv_timeout(Duration::from_millis(1)) {
+                Ok(v) => {
+                    assert_eq!(v, 1);
+                    recv_count += 1;
+                },
+                Err(RecvTimeoutError::Timeout) => continue,
+                Err(RecvTimeoutError::Disconnected) => break,
+            }
+        }
+
+        assert_eq!(recv_count, 10000);
+    }
+
+    #[test]
+    fn stress_recv_timeout_shared() {
+        const AMT: u32 = 1000;
+        const NTHREADS: u32 = 8;
+        let (tx, rx) = sync_channel::<i32>(0);
+        let (dtx, drx) = sync_channel::<()>(0);
+
+        thread::spawn(move|| {
+            let mut recv_count = 0;
+            loop {
+                match rx.recv_timeout(Duration::from_millis(10)) {
+                    Ok(v) => {
+                        assert_eq!(v, 1);
+                        recv_count += 1;
+                    },
+                    Err(RecvTimeoutError::Timeout) => continue,
+                    Err(RecvTimeoutError::Disconnected) => break,
+                }
+            }
+
+            assert_eq!(recv_count, AMT * NTHREADS);
+            assert!(rx.try_recv().is_err());
+
+            dtx.send(()).unwrap();
+        });
+
+        for _ in 0..NTHREADS {
+            let tx = tx.clone();
+            thread::spawn(move|| {
+                for _ in 0..AMT { tx.send(1).unwrap(); }
+            });
+        }
+
+        drop(tx);
+
+        drx.recv().unwrap();
+    }
+
     #[test]
     fn stress_shared() {
         const AMT: u32 = 1000;
index cb930280964b3260fbd7cb6fbc922ac3db2cb256..7a35ea6bbaaa20c184116b5f1b58be4c17090acf 100644 (file)
@@ -41,6 +41,7 @@ use sync::mpsc::Receiver;
 use sync::mpsc::blocking::{self, SignalToken};
 use core::mem;
 use sync::atomic::{AtomicUsize, Ordering};
+use time::Instant;
 
 // Various states you can find a port in.
 const EMPTY: usize = 0;          // initial state: no data, no blocked receiver
@@ -136,7 +137,7 @@ impl<T> Packet<T> {
         }
     }
 
-    pub fn recv(&mut self) -> Result<T, Failure<T>> {
+    pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure<T>> {
         // Attempt to not block the thread (it's a little expensive). If it looks
         // like we're not empty, then immediately go through to `try_recv`.
         if self.state.load(Ordering::SeqCst) == EMPTY {
@@ -145,8 +146,16 @@ impl<T> Packet<T> {
 
             // race with senders to enter the blocking state
             if self.state.compare_and_swap(EMPTY, ptr, Ordering::SeqCst) == EMPTY {
-                wait_token.wait();
-                debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY);
+                if let Some(deadline) = deadline {
+                    let timed_out = !wait_token.wait_max_until(deadline);
+                    // Try to reset the state
+                    if timed_out {
+                        try!(self.abort_selection().map_err(Upgraded));
+                    }
+                } else {
+                    wait_token.wait();
+                    debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY);
+                }
             } else {
                 // drop the signal token, since we never blocked
                 drop(unsafe { SignalToken::cast_from_usize(ptr) });
index a3779931c7bd294291a8d75d43e487a5ca24f89b..baa4db7e5c0fa3e8e08b94395ee7c572fe4e2ac4 100644 (file)
@@ -30,6 +30,7 @@ use sync::mpsc::select::StartResult::*;
 use sync::mpsc::select::StartResult;
 use sync::{Mutex, MutexGuard};
 use thread;
+use time::Instant;
 
 const DISCONNECTED: isize = isize::MIN;
 const FUDGE: isize = 1024;
@@ -66,7 +67,7 @@ impl<T> Packet<T> {
     // Creation of a packet *must* be followed by a call to postinit_lock
     // and later by inherit_blocker
     pub fn new() -> Packet<T> {
-        let p = Packet {
+        Packet {
             queue: mpsc::Queue::new(),
             cnt: AtomicIsize::new(0),
             steals: 0,
@@ -75,8 +76,7 @@ impl<T> Packet<T> {
             port_dropped: AtomicBool::new(false),
             sender_drain: AtomicIsize::new(0),
             select_lock: Mutex::new(()),
-        };
-        return p;
+        }
     }
 
     // This function should be used after newly created Packet
@@ -216,7 +216,7 @@ impl<T> Packet<T> {
         Ok(())
     }
 
-    pub fn recv(&mut self) -> Result<T, Failure> {
+    pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure> {
         // This code is essentially the exact same as that found in the stream
         // case (see stream.rs)
         match self.try_recv() {
@@ -226,7 +226,14 @@ impl<T> Packet<T> {
 
         let (wait_token, signal_token) = blocking::tokens();
         if self.decrement(signal_token) == Installed {
-            wait_token.wait()
+            if let Some(deadline) = deadline {
+                let timed_out = !wait_token.wait_max_until(deadline);
+                if timed_out {
+                    self.abort_selection(false);
+                }
+            } else {
+                wait_token.wait();
+            }
         }
 
         match self.try_recv() {
index ffd33f8518f682720c9cd3ba45fc755b2cc23aee..02506e7c2f3d91f7bf173e8e5da5949dec27d4ad 100644 (file)
@@ -265,15 +265,18 @@ mod tests {
 
             // Ensure the borrowchecker works
             match queue.peek() {
-                Some(vec) => match &**vec {
-                    // Note that `pop` is not allowed here due to borrow
-                    [1] => {}
-                    _ => return
+                Some(vec) => {
+                    assert_eq!(&*vec, &[1]);
                 },
                 None => unreachable!()
             }
 
-            queue.pop();
+            match queue.pop() {
+                Some(vec) => {
+                    assert_eq!(&*vec, &[1]);
+                },
+                None => unreachable!()
+            }
         }
     }
 
index e8012ca470b02190c03a9b96766e1591cf5424c4..aa1254c8641f571aef2f68c16a9be534e97707f8 100644 (file)
@@ -25,6 +25,7 @@ use self::Message::*;
 use core::cmp;
 use core::isize;
 use thread;
+use time::Instant;
 
 use sync::atomic::{AtomicIsize, AtomicUsize, Ordering, AtomicBool};
 use sync::mpsc::Receiver;
@@ -172,7 +173,7 @@ impl<T> Packet<T> {
         Err(unsafe { SignalToken::cast_from_usize(ptr) })
     }
 
-    pub fn recv(&mut self) -> Result<T, Failure<T>> {
+    pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure<T>> {
         // Optimistic preflight check (scheduling is expensive).
         match self.try_recv() {
             Err(Empty) => {}
@@ -183,7 +184,15 @@ impl<T> Packet<T> {
         // initiate the blocking protocol.
         let (wait_token, signal_token) = blocking::tokens();
         if self.decrement(signal_token).is_ok() {
-            wait_token.wait()
+            if let Some(deadline) = deadline {
+                let timed_out = !wait_token.wait_max_until(deadline);
+                if timed_out {
+                    try!(self.abort_selection(/* was_upgrade = */ false)
+                             .map_err(Upgraded));
+                }
+            } else {
+                wait_token.wait();
+            }
         }
 
         match self.try_recv() {
@@ -332,7 +341,7 @@ impl<T> Packet<T> {
         // the internal state.
         match self.queue.peek() {
             Some(&mut GoUp(..)) => {
-                match self.recv() {
+                match self.recv(None) {
                     Err(Upgraded(port)) => Err(port),
                     _ => unreachable!(),
                 }
index b98fc2859afcc43a09e189ac3ca3b9af53271507..f021689acad58849a9bc986d5435e3ee06d0eb0b 100644 (file)
@@ -44,6 +44,7 @@ use sync::atomic::{Ordering, AtomicUsize};
 use sync::mpsc::blocking::{self, WaitToken, SignalToken};
 use sync::mpsc::select::StartResult::{self, Installed, Abort};
 use sync::{Mutex, MutexGuard};
+use time::Instant;
 
 pub struct Packet<T> {
     /// Only field outside of the mutex. Just done for kicks, but mainly because
@@ -126,6 +127,38 @@ fn wait<'a, 'b, T>(lock: &'a Mutex<State<T>>,
     lock.lock().unwrap() // relock
 }
 
+/// Same as wait, but waiting at most until `deadline`.
+fn wait_timeout_receiver<'a, 'b, T>(lock: &'a Mutex<State<T>>,
+                                    deadline: Instant,
+                                    mut guard: MutexGuard<'b, State<T>>,
+                                    success: &mut bool)
+                                    -> MutexGuard<'a, State<T>>
+{
+    let (wait_token, signal_token) = blocking::tokens();
+    match mem::replace(&mut guard.blocker, BlockedReceiver(signal_token)) {
+        NoneBlocked => {}
+        _ => unreachable!(),
+    }
+    drop(guard);         // unlock
+    *success = wait_token.wait_max_until(deadline);   // block
+    let mut new_guard = lock.lock().unwrap(); // relock
+    if !*success {
+        abort_selection(&mut new_guard);
+    }
+    new_guard
+}
+
+fn abort_selection<'a, T>(guard: &mut MutexGuard<'a , State<T>>) -> bool {
+    match mem::replace(&mut guard.blocker, NoneBlocked) {
+        NoneBlocked => true,
+        BlockedSender(token) => {
+            guard.blocker = BlockedSender(token);
+            true
+        }
+        BlockedReceiver(token) => { drop(token); false }
+    }
+}
+
 /// Wakes up a thread, dropping the lock at the correct time
 fn wakeup<T>(token: SignalToken, guard: MutexGuard<State<T>>) {
     // We need to be careful to wake up the waiting thread *outside* of the mutex
@@ -238,22 +271,37 @@ impl<T> Packet<T> {
     //
     // When reading this, remember that there can only ever be one receiver at
     // time.
-    pub fn recv(&self) -> Result<T, ()> {
+    pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure> {
         let mut guard = self.lock.lock().unwrap();
 
-        // Wait for the buffer to have something in it. No need for a while loop
-        // because we're the only receiver.
-        let mut waited = false;
+        let mut woke_up_after_waiting = false;
+        // Wait for the buffer to have something in it. No need for a
+        // while loop because we're the only receiver.
         if !guard.disconnected && guard.buf.size() == 0 {
-            guard = wait(&self.lock, guard, BlockedReceiver);
-            waited = true;
+            if let Some(deadline) = deadline {
+                guard = wait_timeout_receiver(&self.lock,
+                                              deadline,
+                                              guard,
+                                              &mut woke_up_after_waiting);
+            } else {
+                guard = wait(&self.lock, guard, BlockedReceiver);
+                woke_up_after_waiting = true;
+            }
+        }
+
+        // NB: Channel could be disconnected while waiting, so the order of
+        // these conditionals is important.
+        if guard.disconnected && guard.buf.size() == 0 {
+            return Err(Disconnected);
         }
-        if guard.disconnected && guard.buf.size() == 0 { return Err(()) }
 
         // Pick up the data, wake up our neighbors, and carry on
-        assert!(guard.buf.size() > 0);
+        assert!(guard.buf.size() > 0 || (deadline.is_some() && !woke_up_after_waiting));
+
+        if guard.buf.size() == 0 { return Err(Empty); }
+
         let ret = guard.buf.dequeue();
-        self.wakeup_senders(waited, guard);
+        self.wakeup_senders(woke_up_after_waiting, guard);
         Ok(ret)
     }
 
@@ -392,14 +440,7 @@ impl<T> Packet<T> {
     // The return value indicates whether there's data on this port.
     pub fn abort_selection(&self) -> bool {
         let mut guard = self.lock.lock().unwrap();
-        match mem::replace(&mut guard.blocker, NoneBlocked) {
-            NoneBlocked => true,
-            BlockedSender(token) => {
-                guard.blocker = BlockedSender(token);
-                true
-            }
-            BlockedReceiver(token) => { drop(token); false }
-        }
+        abort_selection(&mut guard)
     }
 }
 
index 15e69628c7a5d20df96a0aab5e24d3f79b4e6d79..c75a5c09146a495abe9e626efea78e605f9d8b12 100644 (file)
@@ -204,10 +204,14 @@ impl<T> Mutex<T> {
     /// Creates a new mutex in an unlocked state ready for use.
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn new(t: T) -> Mutex<T> {
-        Mutex {
+        let mut m = Mutex {
             inner: box StaticMutex::new(),
             data: UnsafeCell::new(t),
+        };
+        unsafe {
+            m.inner.lock.init();
         }
+        m
     }
 }
 
index e1e764bd255cbd7302cf26b2eb4368330042f060..03d3483902dcf87b899aefcbe1f2a66ea07d2a30 100644 (file)
@@ -468,42 +468,6 @@ impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> {
             }
         })
     }
-
-    /// Transform this guard to hold a sub-borrow of the original data.
-    ///
-    /// Applies the supplied closure to the data, returning a new lock
-    /// guard referencing the borrow returned by the closure.
-    ///
-    /// # Examples
-    ///
-    /// ```rust
-    /// # #![feature(guard_map)]
-    /// # use std::sync::{RwLockReadGuard, RwLock};
-    /// let x = RwLock::new(vec![1, 2]);
-    ///
-    /// let y = RwLockReadGuard::map(x.read().unwrap(), |v| &v[0]);
-    /// assert_eq!(*y, 1);
-    /// ```
-    #[unstable(feature = "guard_map",
-               reason = "recently added, needs RFC for stabilization,
-                         questionable interaction with Condvar",
-               issue = "27746")]
-    #[rustc_deprecated(since = "1.8.0",
-                       reason = "unsound on Mutex because of Condvar and \
-                                 RwLock may also with to be used with Condvar \
-                                 one day")]
-    pub fn map<U: ?Sized, F>(this: Self, cb: F) -> RwLockReadGuard<'rwlock, U>
-        where F: FnOnce(&T) -> &U
-    {
-        let new = RwLockReadGuard {
-            __lock: this.__lock,
-            __data: cb(this.__data)
-        };
-
-        mem::forget(this);
-
-        new
-    }
 }
 
 #[allow(deprecated)]
@@ -518,57 +482,6 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> {
             }
         })
     }
-
-    /// Transform this guard to hold a sub-borrow of the original data.
-    ///
-    /// Applies the supplied closure to the data, returning a new lock
-    /// guard referencing the borrow returned by the closure.
-    ///
-    /// # Examples
-    ///
-    /// ```rust
-    /// # #![feature(guard_map)]
-    /// # use std::sync::{RwLockWriteGuard, RwLock};
-    /// let x = RwLock::new(vec![1, 2]);
-    ///
-    /// {
-    ///     let mut y = RwLockWriteGuard::map(x.write().unwrap(), |v| &mut v[0]);
-    ///     assert_eq!(*y, 1);
-    ///
-    ///     *y = 10;
-    /// }
-    ///
-    /// assert_eq!(&**x.read().unwrap(), &[10, 2]);
-    /// ```
-    #[unstable(feature = "guard_map",
-               reason = "recently added, needs RFC for stabilization,
-                         questionable interaction with Condvar",
-               issue = "27746")]
-    #[rustc_deprecated(since = "1.8.0",
-                       reason = "unsound on Mutex because of Condvar and \
-                                 RwLock may also with to be used with Condvar \
-                                 one day")]
-    pub fn map<U: ?Sized, F>(this: Self, cb: F) -> RwLockWriteGuard<'rwlock, U>
-        where F: FnOnce(&mut T) -> &mut U
-    {
-        // Compute the new data while still owning the original lock
-        // in order to correctly poison if the callback panics.
-        let data = unsafe { ptr::read(&this.__data) };
-        let new_data = cb(data);
-
-        // We don't want to unlock the lock by running the destructor of the
-        // original lock, so just read the fields we need and forget it.
-        let (poison, lock) = unsafe {
-            (ptr::read(&this.__poison), ptr::read(&this.__lock))
-        };
-        mem::forget(this);
-
-        RwLockWriteGuard {
-            __lock: lock,
-            __data: new_data,
-            __poison: poison
-        }
-    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -619,7 +532,7 @@ mod tests {
     use rand::{self, Rng};
     use sync::mpsc::channel;
     use thread;
-    use sync::{Arc, RwLock, StaticRwLock, TryLockError, RwLockWriteGuard};
+    use sync::{Arc, RwLock, StaticRwLock, TryLockError};
     use sync::atomic::{AtomicUsize, Ordering};
 
     #[derive(Eq, PartialEq, Debug)]
@@ -867,20 +780,4 @@ mod tests {
             Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {:?}", x),
         }
     }
-
-    #[test]
-    fn test_rwlock_write_map_poison() {
-        let rwlock = Arc::new(RwLock::new(vec![1, 2]));
-        let rwlock2 = rwlock.clone();
-
-        thread::spawn(move || {
-            let _ = RwLockWriteGuard::map::<usize, _>(rwlock2.write().unwrap(), |_| panic!());
-        }).join().unwrap_err();
-
-        match rwlock.read() {
-            Ok(r) => panic!("Read lock on poisioned RwLock is Ok: {:?}", &*r),
-            Err(_) => {}
-        };
-    }
 }
-
index 5a6dfe7fb1a1507739b6b47447490ec60d426b92..7a2183c522f5b92154b198e55aea3cdc4455a5f4 100644 (file)
@@ -27,6 +27,12 @@ impl Mutex {
     /// first used with any of the functions below.
     pub const fn new() -> Mutex { Mutex(imp::Mutex::new()) }
 
+    /// Prepare the mutex for use.
+    ///
+    /// This should be called once the mutex is at a stable memory address.
+    #[inline]
+    pub unsafe fn init(&mut self) { self.0.init() }
+
     /// Locks the mutex blocking the current thread until it is available.
     ///
     /// Behavior is undefined if the mutex has been moved between this and any
index 54b9b466c42240b7f8dab99bb962af50f4e1e553..274e495d70eb620b3f0d4e5f2ea91ee618825dfb 100644 (file)
@@ -119,14 +119,22 @@ pub struct LookupHost {
 }
 
 impl Iterator for LookupHost {
-    type Item = io::Result<SocketAddr>;
-    fn next(&mut self) -> Option<io::Result<SocketAddr>> {
-        unsafe {
-            if self.cur.is_null() { return None }
-            let ret = sockaddr_to_addr(mem::transmute((*self.cur).ai_addr),
-                                       (*self.cur).ai_addrlen as usize);
-            self.cur = (*self.cur).ai_next as *mut c::addrinfo;
-            Some(ret)
+    type Item = SocketAddr;
+    fn next(&mut self) -> Option<SocketAddr> {
+        loop {
+            unsafe {
+                let cur = match self.cur.as_ref() {
+                    None => return None,
+                    Some(c) => c,
+                };
+                self.cur = cur.ai_next;
+                match sockaddr_to_addr(mem::transmute(cur.ai_addr),
+                                       cur.ai_addrlen as usize)
+                {
+                    Ok(addr) => return Some(addr),
+                    Err(_) => continue,
+                }
+            }
         }
     }
 }
index 1df511a8818c414849262f14df1926e7e1818741..b5d0357633875829c1164e6f8e1685456c77d0f3 100644 (file)
@@ -42,20 +42,28 @@ pub fn dumb_print(args: fmt::Arguments) {
 // implemented as an illegal instruction.
 #[cfg(unix)]
 unsafe fn abort_internal() -> ! {
-    use libc;
-    libc::abort()
+    ::libc::abort()
 }
 
-// On Windows, we want to avoid using libc, and there isn't a direct
-// equivalent of libc::abort.  The __failfast intrinsic may be a reasonable
-// substitute, but desireability of using it over the abort instrinsic is
-// debateable; see https://github.com/rust-lang/rust/pull/31519 for details.
-#[cfg(not(unix))]
+// On Windows, use the processor-specific __fastfail mechanism.  In Windows 8
+// and later, this will terminate the process immediately without running any
+// in-process exception handlers.  In earlier versions of Windows, this
+// sequence of instructions will be treated as an access violation,
+// terminating the process but without necessarily bypassing all exception
+// handlers.
+//
+// https://msdn.microsoft.com/en-us/library/dn774154.aspx
+#[cfg(all(windows, any(target_arch = "x86", target_arch = "x86_64")))]
 unsafe fn abort_internal() -> ! {
-    use intrinsics;
-    intrinsics::abort()
+    asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT
+    ::intrinsics::unreachable();
 }
 
+// Other platforms should use the appropriate platform-specific mechanism for
+// aborting the process.  If no platform-specific mechanism is available,
+// ::intrinsics::abort() may be used instead.  The above implementations cover
+// all targets currently supported by libstd.
+
 pub fn abort(args: fmt::Arguments) -> ! {
     dumb_print(format_args!("fatal runtime error: {}\n", args));
     unsafe { abort_internal(); }
index 55e485e5811acc687dce80d57e2f43571489f3d3..b6be85a4dfa2879b04548c74964195a5f3f8c1ce 100644 (file)
@@ -566,8 +566,8 @@ impl Wtf8 {
         if len < 3 {
             return None
         }
-        match &self.bytes[(len - 3)..] {
-            [0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)),
+        match ::slice_pat(&&self.bytes[(len - 3)..]) {
+            &[0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)),
             _ => None
         }
     }
@@ -578,8 +578,8 @@ impl Wtf8 {
         if len < 3 {
             return None
         }
-        match &self.bytes[..3] {
-            [0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)),
+        match ::slice_pat(&&self.bytes[..3]) {
+            &[0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)),
             _ => None
         }
     }
@@ -715,7 +715,7 @@ impl<'a> Iterator for Wtf8CodePoints<'a> {
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        let (len, _) = self.bytes.size_hint();
+        let len = self.bytes.len();
         (len.saturating_add(3) / 4, Some(len))
     }
 }
index b0fed2f4694bfcd4163fc53509e1ab9e0de9d225..430ec5f94a6f85da5a0444ddfa0e73b4f52d9f33 100644 (file)
@@ -34,21 +34,6 @@ pub trait CommandExt {
     #[stable(feature = "rust1", since = "1.0.0")]
     fn gid(&mut self, id: u32) -> &mut process::Command;
 
-    /// Create a new session (cf. `setsid(2)`) for the child process. This means
-    /// that the child is the leader of a new process group. The parent process
-    /// remains the child reaper of the new process.
-    ///
-    /// This is not enough to create a daemon process. The *init* process should
-    /// be the child reaper of a daemon. This can be achieved if the parent
-    /// process exit. Moreover, a daemon should not have a controlling terminal.
-    /// To achieve this, a session leader (the child) must spawn another process
-    /// (the daemon) in the same session.
-    #[unstable(feature = "process_session_leader", reason = "recently added",
-               issue = "27811")]
-    #[rustc_deprecated(reason = "use `before_exec` instead",
-                       since = "1.9.0")]
-    fn session_leader(&mut self, on: bool) -> &mut process::Command;
-
     /// Schedules a closure to be run just before the `exec` function is
     /// invoked.
     ///
@@ -112,11 +97,6 @@ impl CommandExt for process::Command {
         self
     }
 
-    fn session_leader(&mut self, on: bool) -> &mut process::Command {
-        self.as_inner_mut().session_leader(on);
-        self
-    }
-
     fn before_exec<F>(&mut self, f: F) -> &mut process::Command
         where F: FnMut() -> io::Result<()> + Send + Sync + 'static
     {
index 96535e886041803b50598d8e4a1f1f2ae1811800..7972990e67dfeb2760840c0affb98ff60ffa42e8 100644 (file)
@@ -23,7 +23,7 @@
 #[stable(feature = "raw_ext", since = "1.1.0")] pub type pid_t = i32;
 
 #[doc(inline)]
-#[unstable(feature = "pthread_t", issue = "29791")]
+#[stable(feature = "pthread_t", since = "1.8.0")]
 pub use sys::platform::raw::pthread_t;
 #[doc(inline)]
 #[stable(feature = "raw_ext", since = "1.1.0")]
index 94c48be02ffc4b3f3093763b8e8b557100bf6499..b99f4a2eacde563d489fa690b06bbb19fea50f35 100644 (file)
@@ -62,32 +62,31 @@ impl FileDesc {
     }
 
     #[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten")))]
-    pub fn set_cloexec(&self) {
+    pub fn set_cloexec(&self) -> io::Result<()> {
         unsafe {
-            let ret = libc::ioctl(self.fd, libc::FIOCLEX);
-            debug_assert_eq!(ret, 0);
+            cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
+            Ok(())
         }
     }
     #[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten"))]
-    pub fn set_cloexec(&self) {
+    pub fn set_cloexec(&self) -> io::Result<()> {
         unsafe {
-            let previous = libc::fcntl(self.fd, libc::F_GETFD);
-            let ret = libc::fcntl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC);
-            debug_assert_eq!(ret, 0);
+            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
+            cvt(libc::fcntl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC))?;
+            Ok(())
         }
     }
 
-    pub fn set_nonblocking(&self, nonblocking: bool) {
+    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
         unsafe {
-            let previous = libc::fcntl(self.fd, libc::F_GETFL);
-            debug_assert!(previous != -1);
+            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
             let new = if nonblocking {
                 previous | libc::O_NONBLOCK
             } else {
                 previous & !libc::O_NONBLOCK
             };
-            let ret = libc::fcntl(self.fd, libc::F_SETFL, new);
-            debug_assert!(ret != -1);
+            cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
+            Ok(())
         }
     }
 
@@ -114,8 +113,8 @@ impl FileDesc {
 
         let make_filedesc = |fd| {
             let fd = FileDesc::new(fd);
-            fd.set_cloexec();
-            fd
+            fd.set_cloexec()?;
+            Ok(fd)
         };
         static TRY_CLOEXEC: AtomicBool =
             AtomicBool::new(!cfg!(target_os = "android"));
@@ -127,7 +126,7 @@ impl FileDesc {
                 // though it reported doing so on F_DUPFD_CLOEXEC.
                 Ok(fd) => {
                     return Ok(if cfg!(target_os = "linux") {
-                        make_filedesc(fd)
+                        make_filedesc(fd)?
                     } else {
                         FileDesc::new(fd)
                     })
@@ -138,7 +137,7 @@ impl FileDesc {
                 Err(e) => return Err(e),
             }
         }
-        cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).map(make_filedesc)
+        cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc)
     }
 }
 
index 7f23ae53fcd1ac7375e0f3abe140b6d9f38f6724..0524851df91abeb0c091a0a254b616388dd4b007 100644 (file)
@@ -418,7 +418,7 @@ impl File {
         // The CLOEXEC flag, however, is supported on versions of OSX/BSD/etc
         // that we support, so we only do this on Linux currently.
         if cfg!(target_os = "linux") {
-            fd.set_cloexec();
+            fd.set_cloexec()?;
         }
 
         Ok(File(fd))
index 12a877f7478204feb105c8ee78b51c11b4499efb..f0fd42fc99b806eadba0b261ddefe2ca61872980 100644 (file)
@@ -12,8 +12,6 @@
 
 use io::{self, ErrorKind};
 use libc;
-use num::One;
-use ops::Neg;
 
 #[cfg(target_os = "android")]   pub use os::android as platform;
 #[cfg(target_os = "bitrig")]    pub use os::bitrig as platform;
@@ -123,9 +121,23 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
     }
 }
 
-pub fn cvt<T: One + PartialEq + Neg<Output=T>>(t: T) -> io::Result<T> {
-    let one: T = T::one();
-    if t == -one {
+#[doc(hidden)]
+pub trait IsMinusOne {
+    fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+    ($($t:ident)*) => ($(impl IsMinusOne for $t {
+        fn is_minus_one(&self) -> bool {
+            *self == -1
+        }
+    })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+    if t.is_minus_one() {
         Err(io::Error::last_os_error())
     } else {
         Ok(t)
@@ -133,7 +145,8 @@ pub fn cvt<T: One + PartialEq + Neg<Output=T>>(t: T) -> io::Result<T> {
 }
 
 pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
-    where T: One + PartialEq + Neg<Output=T>, F: FnMut() -> T
+    where T: IsMinusOne,
+          F: FnMut() -> T
 {
     loop {
         match cvt(f()) {
index 4e4abcfbeee4ddb9256451e8f6bdb54e9a0f3d4f..52cf3f97c5c83ba49e12fb37a746710d87840160 100644 (file)
@@ -30,6 +30,39 @@ impl Mutex {
         Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
     }
     #[inline]
+    pub unsafe fn init(&mut self) {
+        // Issue #33770
+        //
+        // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
+        // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you
+        // try to re-lock it from the same thread when you already hold a lock.
+        //
+        // In practice, glibc takes advantage of this undefined behavior to
+        // implement hardware lock elision, which uses hardware transactional
+        // memory to avoid acquiring the lock. While a transaction is in
+        // progress, the lock appears to be unlocked. This isn't a problem for
+        // other threads since the transactional memory will abort if a conflict
+        // is detected, however no abort is generated if re-locking from the
+        // same thread.
+        //
+        // Since locking the same mutex twice will result in two aliasing &mut
+        // references, we instead create the mutex with type
+        // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to
+        // re-lock it from the same thread, thus avoiding undefined behavior.
+        //
+        // We can't do anything for StaticMutex, but that type is deprecated
+        // anyways.
+        let mut attr: libc::pthread_mutexattr_t = mem::uninitialized();
+        let r = libc::pthread_mutexattr_init(&mut attr);
+        debug_assert_eq!(r, 0);
+        let r = libc::pthread_mutexattr_settype(&mut attr, libc::PTHREAD_MUTEX_NORMAL);
+        debug_assert_eq!(r, 0);
+        let r = libc::pthread_mutex_init(self.inner.get(), &attr);
+        debug_assert_eq!(r, 0);
+        let r = libc::pthread_mutexattr_destroy(&mut attr);
+        debug_assert_eq!(r, 0);
+    }
+    #[inline]
     pub unsafe fn lock(&self) {
         let r = libc::pthread_mutex_lock(self.inner.get());
         debug_assert_eq!(r, 0);
index 830957a7e59c76943a6f080b28e98473e008b856..a784741c88cc7f3536857065b6490db772e82dc1 100644 (file)
@@ -77,7 +77,7 @@ impl Socket {
 
             let fd = cvt(libc::socket(fam, ty, 0))?;
             let fd = FileDesc::new(fd);
-            fd.set_cloexec();
+            fd.set_cloexec()?;
             Ok(Socket(fd))
         }
     }
@@ -99,9 +99,9 @@ impl Socket {
 
             cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
             let a = FileDesc::new(fds[0]);
-            a.set_cloexec();
             let b = FileDesc::new(fds[1]);
-            b.set_cloexec();
+            a.set_cloexec()?;
+            b.set_cloexec()?;
             Ok((Socket(a), Socket(b)))
         }
     }
@@ -132,7 +132,7 @@ impl Socket {
             libc::accept(self.0.raw(), storage, len)
         })?;
         let fd = FileDesc::new(fd);
-        fd.set_cloexec();
+        fd.set_cloexec()?;
         Ok(Socket(fd))
     }
 
index beca2d467536d82a4ea9acf2956b9df7055b3737..2dde9c0e615f2ec055540adea76fc09ecae55d7b 100644 (file)
@@ -44,17 +44,18 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
         }
     }
     if unsafe { libc::pipe(fds.as_mut_ptr()) == 0 } {
-        Ok((AnonPipe::from_fd(fds[0]), AnonPipe::from_fd(fds[1])))
+        let fd0 = FileDesc::new(fds[0]);
+        let fd1 = FileDesc::new(fds[1]);
+        Ok((AnonPipe::from_fd(fd0)?, AnonPipe::from_fd(fd1)?))
     } else {
         Err(io::Error::last_os_error())
     }
 }
 
 impl AnonPipe {
-    pub fn from_fd(fd: libc::c_int) -> AnonPipe {
-        let fd = FileDesc::new(fd);
-        fd.set_cloexec();
-        AnonPipe(fd)
+    pub fn from_fd(fd: FileDesc) -> io::Result<AnonPipe> {
+        fd.set_cloexec()?;
+        Ok(AnonPipe(fd))
     }
 
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
@@ -81,8 +82,8 @@ pub fn read2(p1: AnonPipe,
     // in the `select` loop below, and we wouldn't want one to block the other!
     let p1 = p1.into_fd();
     let p2 = p2.into_fd();
-    p1.set_nonblocking(true);
-    p2.set_nonblocking(true);
+    p1.set_nonblocking(true)?;
+    p2.set_nonblocking(true)?;
 
     let max = cmp::max(p1.raw(), p2.raw());
     loop {
@@ -114,11 +115,11 @@ pub fn read2(p1: AnonPipe,
             }
         };
         if read(&p1, v1)? {
-            p2.set_nonblocking(false);
+            p2.set_nonblocking(false)?;
             return p2.read_to_end(v2).map(|_| ());
         }
         if read(&p2, v2)? {
-            p1.set_nonblocking(false);
+            p1.set_nonblocking(false)?;
             return p1.read_to_end(v1).map(|_| ());
         }
     }
index d57191675426455a6856527dad9aab8e5e9e4dce..98cfdcdf11041510049283485fac0d5a8a22b337 100644 (file)
@@ -55,7 +55,6 @@ pub struct Command {
     cwd: Option<CString>,
     uid: Option<uid_t>,
     gid: Option<gid_t>,
-    session_leader: bool,
     saw_nul: bool,
     closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
     stdin: Option<Stdio>,
@@ -105,7 +104,6 @@ impl Command {
             cwd: None,
             uid: None,
             gid: None,
-            session_leader: false,
             saw_nul: saw_nul,
             closures: Vec::new(),
             stdin: None,
@@ -197,9 +195,6 @@ impl Command {
     pub fn gid(&mut self, id: gid_t) {
         self.gid = Some(id);
     }
-    pub fn session_leader(&mut self, session_leader: bool) {
-        self.session_leader = session_leader;
-    }
 
     pub fn before_exec(&mut self,
                        f: Box<FnMut() -> io::Result<()> + Send + Sync>) {
@@ -367,12 +362,6 @@ impl Command {
 
             t!(cvt(libc::setuid(u as uid_t)));
         }
-        if self.session_leader {
-            // Don't check the error of setsid because it fails if we're the
-            // process leader already. We just forked so it shouldn't return
-            // error, but ignore it anyway.
-            let _ = libc::setsid();
-        }
         if let Some(ref cwd) = self.cwd {
             t!(cvt(libc::chdir(cwd.as_ptr())));
         }
index 44bd5d895f2e4373af64e753349c799b193be556..fbd4e1d120817ebc21a0b5e4e08d2f8593ff02d2 100644 (file)
 
 use libc;
 use cell::UnsafeCell;
+use sync::atomic::{AtomicUsize, Ordering};
 
-pub struct RWLock { inner: UnsafeCell<libc::pthread_rwlock_t> }
+pub struct RWLock {
+    inner: UnsafeCell<libc::pthread_rwlock_t>,
+    write_locked: UnsafeCell<bool>,
+    num_readers: AtomicUsize,
+}
 
 unsafe impl Send for RWLock {}
 unsafe impl Sync for RWLock {}
 
 impl RWLock {
     pub const fn new() -> RWLock {
-        RWLock { inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER) }
+        RWLock {
+            inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER),
+            write_locked: UnsafeCell::new(false),
+            num_readers: AtomicUsize::new(0),
+        }
     }
     #[inline]
     pub unsafe fn read(&self) {
@@ -35,37 +44,86 @@ impl RWLock {
         //
         // We roughly maintain the deadlocking behavior by panicking to ensure
         // that this lock acquisition does not succeed.
-        if r == libc::EDEADLK {
+        //
+        // We also check whether there this lock is already write locked. This
+        // is only possible if it was write locked by the current thread and
+        // the implementation allows recursive locking. The POSIX standard
+        // doesn't require recursivly locking a rwlock to deadlock, but we can't
+        // allow that because it could lead to aliasing issues.
+        if r == libc::EDEADLK || *self.write_locked.get() {
+            if r == 0 {
+                self.raw_unlock();
+            }
             panic!("rwlock read lock would result in deadlock");
         } else {
             debug_assert_eq!(r, 0);
+            self.num_readers.fetch_add(1, Ordering::Relaxed);
         }
     }
     #[inline]
     pub unsafe fn try_read(&self) -> bool {
-        libc::pthread_rwlock_tryrdlock(self.inner.get()) == 0
+        let r = libc::pthread_rwlock_tryrdlock(self.inner.get());
+        if r == 0 {
+            if *self.write_locked.get() {
+                self.raw_unlock();
+                false
+            } else {
+                self.num_readers.fetch_add(1, Ordering::Relaxed);
+                true
+            }
+        } else {
+            false
+        }
     }
     #[inline]
     pub unsafe fn write(&self) {
         let r = libc::pthread_rwlock_wrlock(self.inner.get());
-        // see comments above for why we check for EDEADLK
-        if r == libc::EDEADLK {
+        // See comments above for why we check for EDEADLK and write_locked. We
+        // also need to check that num_readers is 0.
+        if r == libc::EDEADLK || *self.write_locked.get() ||
+           self.num_readers.load(Ordering::Relaxed) != 0 {
+            if r == 0 {
+                self.raw_unlock();
+            }
             panic!("rwlock write lock would result in deadlock");
         } else {
             debug_assert_eq!(r, 0);
         }
+        *self.write_locked.get() = true;
     }
     #[inline]
     pub unsafe fn try_write(&self) -> bool {
-        libc::pthread_rwlock_trywrlock(self.inner.get()) == 0
+        let r = libc::pthread_rwlock_trywrlock(self.inner.get());
+        if r == 0 {
+            if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 {
+                self.raw_unlock();
+                false
+            } else {
+                *self.write_locked.get() = true;
+                true
+            }
+        } else {
+            false
+        }
     }
     #[inline]
-    pub unsafe fn read_unlock(&self) {
+    unsafe fn raw_unlock(&self) {
         let r = libc::pthread_rwlock_unlock(self.inner.get());
         debug_assert_eq!(r, 0);
     }
     #[inline]
-    pub unsafe fn write_unlock(&self) { self.read_unlock() }
+    pub unsafe fn read_unlock(&self) {
+        debug_assert!(!*self.write_locked.get());
+        self.num_readers.fetch_sub(1, Ordering::Relaxed);
+        self.raw_unlock();
+    }
+    #[inline]
+    pub unsafe fn write_unlock(&self) {
+        debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0);
+        debug_assert!(*self.write_locked.get());
+        *self.write_locked.get() = false;
+        self.raw_unlock();
+    }
     #[inline]
     pub unsafe fn destroy(&self) {
         let r = libc::pthread_rwlock_destroy(self.inner.get());
index cb34d1a5fbcd113b42a82bb8552b71764ea22960..371319a93d2f2440cf117f7b1d18991613fd7040 100644 (file)
@@ -125,16 +125,25 @@ impl Thread {
     }
 
     pub fn sleep(dur: Duration) {
-        let mut ts = libc::timespec {
-            tv_sec: dur.as_secs() as libc::time_t,
-            tv_nsec: dur.subsec_nanos() as libc::c_long,
-        };
+        let mut secs = dur.as_secs();
+        let mut nsecs = dur.subsec_nanos() as libc::c_long;
 
         // If we're awoken with a signal then the return value will be -1 and
         // nanosleep will fill in `ts` with the remaining time.
         unsafe {
-            while libc::nanosleep(&ts, &mut ts) == -1 {
-                assert_eq!(os::errno(), libc::EINTR);
+            while secs > 0 || nsecs > 0 {
+                let mut ts = libc::timespec {
+                    tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
+                    tv_nsec: nsecs,
+                };
+                secs -= ts.tv_sec as u64;
+                if libc::nanosleep(&ts, &mut ts) == -1 {
+                    assert_eq!(os::errno(), libc::EINTR);
+                    secs += ts.tv_sec as u64;
+                    nsecs = ts.tv_nsec;
+                } else {
+                    nsecs = 0;
+                }
             }
         }
     }
index 2acf6485eb3dad6bf9252a609020ede0945706fb..ce563dc7b16d3e4fe31c3b6ab343bd82dfc0d484 100644 (file)
@@ -181,6 +181,7 @@ pub const ERROR_ACCESS_DENIED: DWORD = 5;
 pub const ERROR_INVALID_HANDLE: DWORD = 6;
 pub const ERROR_NO_MORE_FILES: DWORD = 18;
 pub const ERROR_HANDLE_EOF: DWORD = 38;
+pub const ERROR_FILE_EXISTS: DWORD = 80;
 pub const ERROR_BROKEN_PIPE: DWORD = 109;
 pub const ERROR_CALL_NOT_IMPLEMENTED: DWORD = 120;
 pub const ERROR_INSUFFICIENT_BUFFER: DWORD = 122;
index 3cd45afaf014815e88d9c894e4ca2f3cb93269dc..c243e890526f78526d1777c7de94862f0b8f87c6 100644 (file)
@@ -117,10 +117,10 @@ impl Drop for FindNextFileHandle {
 
 impl DirEntry {
     fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> {
-        match &wfd.cFileName[0..3] {
+        match ::slice_pat(&&wfd.cFileName[0..3]) {
             // check for '.' and '..'
-            [46, 0, ..] |
-            [46, 46, 0, ..] => return None,
+            &[46, 0, ..] |
+            &[46, 46, 0, ..] => return None,
             _ => {}
         }
 
index 384940e4dc446edbe0f0101378000b60a431727b..12219c1e9d42bf750717bdb1c8f16b169c17d1e7 100644 (file)
@@ -14,7 +14,6 @@ use prelude::v1::*;
 
 use ffi::{OsStr, OsString};
 use io::{self, ErrorKind};
-use num::Zero;
 use os::windows::ffi::{OsStrExt, OsStringExt};
 use path::PathBuf;
 use time::Duration;
@@ -68,6 +67,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
     match errno as c::DWORD {
         c::ERROR_ACCESS_DENIED => return ErrorKind::PermissionDenied,
         c::ERROR_ALREADY_EXISTS => return ErrorKind::AlreadyExists,
+        c::ERROR_FILE_EXISTS => return ErrorKind::AlreadyExists,
         c::ERROR_BROKEN_PIPE => return ErrorKind::BrokenPipe,
         c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound,
         c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound,
@@ -177,8 +177,22 @@ pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
     }
 }
 
-fn cvt<I: PartialEq + Zero>(i: I) -> io::Result<I> {
-    if i == I::zero() {
+trait IsZero {
+    fn is_zero(&self) -> bool;
+}
+
+macro_rules! impl_is_zero {
+    ($($t:ident)*) => ($(impl IsZero for $t {
+        fn is_zero(&self) -> bool {
+            *self == 0
+        }
+    })*)
+}
+
+impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }
+
+fn cvt<I: IsZero>(i: I) -> io::Result<I> {
+    if i.is_zero() {
         Err(io::Error::last_os_error())
     } else {
         Ok(i)
index b770156582d3bd6ff87ba053d1fd7af9833bc235..8762b34e3da484df7fbf49854f4ac57f754a8a1d 100644 (file)
@@ -64,6 +64,8 @@ impl Mutex {
             held: UnsafeCell::new(false),
         }
     }
+    #[inline]
+    pub unsafe fn init(&mut self) {}
     pub unsafe fn lock(&self) {
         match kind() {
             Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)),
index b05dcf42a3324636d3a5a8ae1acb3a9a6e5c8789..71e164f012f1f7c4dfe9cba2ef212660414da296 100644 (file)
@@ -17,8 +17,6 @@ use io::{self, Read};
 use libc::{c_int, c_void, c_ulong};
 use mem;
 use net::{SocketAddr, Shutdown};
-use num::One;
-use ops::Neg;
 use ptr;
 use sync::Once;
 use sys::c;
@@ -60,11 +58,26 @@ fn last_error() -> io::Error {
     io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
 }
 
+#[doc(hidden)]
+pub trait IsMinusOne {
+    fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+    ($($t:ident)*) => ($(impl IsMinusOne for $t {
+        fn is_minus_one(&self) -> bool {
+            *self == -1
+        }
+    })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
 /// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1)
 /// and if so, returns the last error from the Windows socket interface. This
 /// function must be called before another call to the socket API is made.
-pub fn cvt<T: One + PartialEq + Neg<Output=T>>(t: T) -> io::Result<T> {
-    if t == -T::one() {
+pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+    if t.is_minus_one() {
         Err(last_error())
     } else {
         Ok(t)
@@ -82,7 +95,8 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> {
 
 /// Just to provide the same interface as sys/unix/net.rs
 pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
-    where T: One + PartialEq + Neg<Output=T>, F: FnMut() -> T
+    where T: IsMinusOne,
+          F: FnMut() -> T
 {
     cvt(f())
 }
index 6b54ec8afca9cfff881dffc814082ded0b41e62d..152b9771086b80c7a32017eab810b14a0a152a96 100644 (file)
@@ -100,18 +100,52 @@ pub struct LocalKey<T: 'static> {
 
 /// Declare a new thread local storage key of type `std::thread::LocalKey`.
 ///
+/// # Syntax
+///
+/// The macro wraps any number of static declarations and makes them thread local.
+/// Each static may be public or private, and attributes are allowed. Example:
+///
+/// ```
+/// use std::cell::RefCell;
+/// thread_local! {
+///     pub static FOO: RefCell<u32> = RefCell::new(1);
+///
+///     #[allow(unused)]
+///     static BAR: RefCell<f32> = RefCell::new(1.0);
+/// }
+/// # fn main() {}
+/// ```
+///
 /// See [LocalKey documentation](thread/struct.LocalKey.html) for more
 /// information.
 #[macro_export]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[allow_internal_unstable]
 macro_rules! thread_local {
-    (static $name:ident: $t:ty = $init:expr) => (
-        static $name: $crate::thread::LocalKey<$t> =
+    // rule 0: empty (base case for the recursion)
+    () => {};
+
+    // rule 1: process multiple declarations where the first one is private
+    ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
+        thread_local!($(#[$attr])* static $name: $t = $init); // go to rule 2
+        thread_local!($($rest)*);
+    );
+
+    // rule 2: handle a single private declaration
+    ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => (
+        $(#[$attr])* static $name: $crate::thread::LocalKey<$t> =
             __thread_local_inner!($t, $init);
     );
-    (pub static $name:ident: $t:ty = $init:expr) => (
-        pub static $name: $crate::thread::LocalKey<$t> =
+
+    // rule 3: handle multiple declarations where the first one is public
+    ($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
+        thread_local!($(#[$attr])* pub static $name: $t = $init); // go to rule 4
+        thread_local!($($rest)*);
+    );
+
+    // rule 4: handle a single public declaration
+    ($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr) => (
+        $(#[$attr])* pub static $name: $crate::thread::LocalKey<$t> =
             __thread_local_inner!($t, $init);
     );
 }
index dc26370590cf825297e0e9d5901e79d1191ed144..e9736fea7b37f43955f04d7137cefa517cfaaa9b 100644 (file)
@@ -183,25 +183,15 @@ use time::Duration;
 ////////////////////////////////////////////////////////////////////////////////
 
 #[macro_use] mod local;
-#[macro_use] mod scoped_tls;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::local::{LocalKey, LocalKeyState};
 
-#[unstable(feature = "scoped_tls",
-           reason = "scoped TLS has yet to have wide enough use to fully \
-                     consider stabilizing its interface",
-           issue = "27715")]
-#[allow(deprecated)]
-pub use self::scoped_tls::ScopedKey;
-
 #[unstable(feature = "libstd_thread_internals", issue = "0")]
 #[cfg(target_thread_local)]
 #[doc(hidden)] pub use self::local::elf::Key as __ElfLocalKeyInner;
 #[unstable(feature = "libstd_thread_internals", issue = "0")]
 #[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner;
-#[unstable(feature = "libstd_thread_internals", issue = "0")]
-#[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Builder
@@ -230,6 +220,21 @@ impl Builder {
 
     /// Names the thread-to-be. Currently the name is used for identification
     /// only in panic messages.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use std::thread;
+    ///
+    /// let builder = thread::Builder::new()
+    ///     .name("foo".into());
+    ///
+    /// let handler = builder.spawn(|| {
+    ///     assert_eq!(thread::current().name(), Some("foo"))
+    /// }).unwrap();
+    ///
+    /// handler.join().unwrap();
+    /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn name(mut self, name: String) -> Builder {
         self.name = Some(name);
@@ -331,6 +336,35 @@ pub fn yield_now() {
 }
 
 /// Determines whether the current thread is unwinding because of panic.
+///
+/// # Examples
+///
+/// ```rust,should_panic
+/// use std::thread;
+///
+/// struct SomeStruct;
+///
+/// impl Drop for SomeStruct {
+///     fn drop(&mut self) {
+///         if thread::panicking() {
+///             println!("dropped while unwinding");
+///         } else {
+///             println!("dropped while not unwinding");
+///         }
+///     }
+/// }
+///
+/// {
+///     print!("a: ");
+///     let a = SomeStruct;
+/// }
+///
+/// {
+///     print!("b: ");
+///     let b = SomeStruct;
+///     panic!()
+/// }
+/// ```
 #[inline]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn panicking() -> bool {
@@ -360,6 +394,19 @@ pub fn sleep_ms(ms: u32) {
 /// signal being received or a spurious wakeup. Platforms which do not support
 /// nanosecond precision for sleeping will have `dur` rounded up to the nearest
 /// granularity of time they can sleep for.
+///
+/// # Examples
+///
+/// ```rust,no_run
+/// use std::{thread, time};
+///
+/// let ten_millis = time::Duration::from_millis(10);
+/// let now = time::Instant::now();
+///
+/// thread::sleep(ten_millis);
+///
+/// assert!(now.elapsed() >= ten_millis);
+/// ```
 #[stable(feature = "thread_sleep", since = "1.4.0")]
 pub fn sleep(dur: Duration) {
     imp::Thread::sleep(dur)
@@ -404,10 +451,10 @@ pub fn park() {
 /// the specified duration has been reached (may wake spuriously).
 ///
 /// The semantics of this function are equivalent to `park()` except that the
-/// thread will be blocked for roughly no longer than *ms*. This method
+/// thread will be blocked for roughly no longer than `ms`. This method
 /// should not be used for precise timing due to anomalies such as
 /// preemption or platform differences that may not cause the maximum
-/// amount of time waited to be precisely *ms* long.
+/// amount of time waited to be precisely `ms` long.
 ///
 /// See the module doc for more detail.
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -420,10 +467,10 @@ pub fn park_timeout_ms(ms: u32) {
 /// the specified duration has been reached (may wake spuriously).
 ///
 /// The semantics of this function are equivalent to `park()` except that the
-/// thread will be blocked for roughly no longer than *dur*. This method
+/// thread will be blocked for roughly no longer than `dur`. This method
 /// should not be used for precise timing due to anomalies such as
 /// preemption or platform differences that may not cause the maximum
-/// amount of time waited to be precisely *dur* long.
+/// amount of time waited to be precisely `dur` long.
 ///
 /// See the module doc for more detail.
 ///
@@ -463,9 +510,9 @@ pub struct Thread {
 impl Thread {
     // Used only internally to construct a thread object without spawning
     fn new(name: Option<String>) -> Thread {
-        let cname = name.map(|n| CString::new(n).unwrap_or_else(|_| {
-            panic!("thread name may not contain interior null bytes")
-        }));
+        let cname = name.map(|n| {
+            CString::new(n).expect("thread name may not contain interior null bytes")
+        });
         Thread {
             inner: Arc::new(Inner {
                 name: cname,
@@ -488,6 +535,37 @@ impl Thread {
     }
 
     /// Gets the thread's name.
+    ///
+    /// # Examples
+    ///
+    /// Threads by default have no name specified:
+    ///
+    /// ```
+    /// use std::thread;
+    ///
+    /// let builder = thread::Builder::new();
+    ///
+    /// let handler = builder.spawn(|| {
+    ///     assert!(thread::current().name().is_none());
+    /// }).unwrap();
+    ///
+    /// handler.join().unwrap();
+    /// ```
+    ///
+    /// Thread with a specified name:
+    ///
+    /// ```
+    /// use std::thread;
+    ///
+    /// let builder = thread::Builder::new()
+    ///     .name("foo".into());
+    ///
+    /// let handler = builder.spawn(|| {
+    ///     assert_eq!(thread::current().name(), Some("foo"))
+    /// }).unwrap();
+    ///
+    /// handler.join().unwrap();
+    /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn name(&self) -> Option<&str> {
         self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) } )
@@ -560,6 +638,36 @@ impl<T> JoinInner<T> {
 /// Due to platform restrictions, it is not possible to `Clone` this
 /// handle: the ability to join a child thread is a uniquely-owned
 /// permission.
+///
+/// This `struct` is created by the [`thread::spawn`] function and the
+/// [`thread::Builder::spawn`] method.
+///
+/// # Examples
+///
+/// Creation from [`thread::spawn`]:
+///
+/// ```rust
+/// use std::thread;
+///
+/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| {
+///     // some work here
+/// });
+/// ```
+///
+/// Creation from [`thread::Builder::spawn`]:
+///
+/// ```rust
+/// use std::thread;
+///
+/// let builder = thread::Builder::new();
+///
+/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| {
+///     // some work here
+/// }).unwrap();
+/// ```
+///
+/// [`thread::spawn`]: fn.spawn.html
+/// [`thread::Builder::spawn`]: struct.Builder.html#method.spawn
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct JoinHandle<T>(JoinInner<T>);
 
diff --git a/src/libstd/thread/scoped_tls.rs b/src/libstd/thread/scoped_tls.rs
deleted file mode 100644 (file)
index dea58d0..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Scoped thread-local storage
-//!
-//! This module provides the ability to generate *scoped* thread-local
-//! variables. In this sense, scoped indicates that thread local storage
-//! actually stores a reference to a value, and this reference is only placed
-//! in storage for a scoped amount of time.
-//!
-//! There are no restrictions on what types can be placed into a scoped
-//! variable, but all scoped variables are initialized to the equivalent of
-//! null. Scoped thread local storage is useful when a value is present for a known
-//! period of time and it is not required to relinquish ownership of the
-//! contents.
-//!
-//! # Examples
-//!
-//! ```
-//! #![feature(scoped_tls)]
-//!
-//! scoped_thread_local!(static FOO: u32);
-//!
-//! // Initially each scoped slot is empty.
-//! assert!(!FOO.is_set());
-//!
-//! // When inserting a value, the value is only in place for the duration
-//! // of the closure specified.
-//! FOO.set(&1, || {
-//!     FOO.with(|slot| {
-//!         assert_eq!(*slot, 1);
-//!     });
-//! });
-//! ```
-
-#![unstable(feature = "thread_local_internals", issue = "0")]
-#![allow(deprecated)]
-
-#[doc(hidden)]
-pub use self::imp::KeyInner as __KeyInner;
-
-/// Type representing a thread local storage key corresponding to a reference
-/// to the type parameter `T`.
-///
-/// Keys are statically allocated and can contain a reference to an instance of
-/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
-/// and `with`, both of which currently use closures to control the scope of
-/// their contents.
-#[unstable(feature = "scoped_tls",
-           reason = "scoped TLS has yet to have wide enough use to fully consider \
-                     stabilizing its interface",
-           issue = "27715")]
-#[rustc_deprecated(since = "1.8.0",
-                   reason = "hasn't proven itself over LocalKey")]
-pub struct ScopedKey<T:'static> { inner: fn() -> &'static imp::KeyInner<T> }
-
-/// Declare a new scoped thread local storage key.
-///
-/// This macro declares a `static` item on which methods are used to get and
-/// set the value stored within.
-///
-/// See [ScopedKey documentation](thread/struct.ScopedKey.html) for more
-/// information.
-#[unstable(feature = "thread_local_internals",
-           reason = "should not be necessary",
-           issue = "0")]
-#[rustc_deprecated(since = "1.8.0",
-                   reason = "hasn't proven itself over LocalKey")]
-#[macro_export]
-#[allow_internal_unstable]
-macro_rules! scoped_thread_local {
-    (static $name:ident: $t:ty) => (
-        static $name: $crate::thread::ScopedKey<$t> =
-            __scoped_thread_local_inner!($t);
-    );
-    (pub static $name:ident: $t:ty) => (
-        pub static $name: $crate::thread::ScopedKey<$t> =
-            __scoped_thread_local_inner!($t);
-    );
-}
-
-#[doc(hidden)]
-#[unstable(feature = "thread_local_internals",
-           reason = "should not be necessary",
-           issue = "0")]
-#[rustc_deprecated(since = "1.8.0",
-                   reason = "hasn't proven itself over LocalKey")]
-#[macro_export]
-#[allow_internal_unstable]
-macro_rules! __scoped_thread_local_inner {
-    ($t:ty) => {{
-        #[cfg_attr(target_thread_local, thread_local)]
-        static _KEY: $crate::thread::__ScopedKeyInner<$t> =
-            $crate::thread::__ScopedKeyInner::new();
-        fn _getit() -> &'static $crate::thread::__ScopedKeyInner<$t> { &_KEY }
-        $crate::thread::ScopedKey::new(_getit)
-    }}
-}
-
-#[unstable(feature = "scoped_tls",
-           reason = "scoped TLS has yet to have wide enough use to fully consider \
-                     stabilizing its interface",
-           issue = "27715")]
-#[rustc_deprecated(since = "1.8.0",
-                   reason = "hasn't proven itself over LocalKey")]
-impl<T> ScopedKey<T> {
-    #[doc(hidden)]
-    pub const fn new(inner: fn() -> &'static imp::KeyInner<T>) -> ScopedKey<T> {
-        ScopedKey { inner: inner }
-    }
-
-    /// Inserts a value into this scoped thread local storage slot for a
-    /// duration of a closure.
-    ///
-    /// While `cb` is running, the value `t` will be returned by `get` unless
-    /// this function is called recursively inside of `cb`.
-    ///
-    /// Upon return, this function will restore the previous value, if any
-    /// was available.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(scoped_tls)]
-    ///
-    /// scoped_thread_local!(static FOO: u32);
-    ///
-    /// FOO.set(&100, || {
-    ///     let val = FOO.with(|v| *v);
-    ///     assert_eq!(val, 100);
-    ///
-    ///     // set can be called recursively
-    ///     FOO.set(&101, || {
-    ///         // ...
-    ///     });
-    ///
-    ///     // Recursive calls restore the previous value.
-    ///     let val = FOO.with(|v| *v);
-    ///     assert_eq!(val, 100);
-    /// });
-    /// ```
-    pub fn set<R, F>(&'static self, t: &T, cb: F) -> R where
-        F: FnOnce() -> R,
-    {
-        struct Reset<'a, T: 'a> {
-            key: &'a imp::KeyInner<T>,
-            val: *mut T,
-        }
-                impl<'a, T> Drop for Reset<'a, T> {
-            fn drop(&mut self) {
-                unsafe { self.key.set(self.val) }
-            }
-        }
-
-        let inner = (self.inner)();
-        let prev = unsafe {
-            let prev = inner.get();
-            inner.set(t as *const T as *mut T);
-            prev
-        };
-
-        let _reset = Reset { key: inner, val: prev };
-        cb()
-    }
-
-    /// Gets a value out of this scoped variable.
-    ///
-    /// This function takes a closure which receives the value of this
-    /// variable.
-    ///
-    /// # Panics
-    ///
-    /// This function will panic if `set` has not previously been called.
-    ///
-    /// # Examples
-    ///
-    /// ```no_run
-    /// #![feature(scoped_tls)]
-    ///
-    /// scoped_thread_local!(static FOO: u32);
-    ///
-    /// FOO.with(|slot| {
-    ///     // work with `slot`
-    /// });
-    /// ```
-    pub fn with<R, F>(&'static self, cb: F) -> R where
-        F: FnOnce(&T) -> R
-    {
-        unsafe {
-            let ptr = (self.inner)().get();
-            assert!(!ptr.is_null(), "cannot access a scoped thread local \
-                                     variable without calling `set` first");
-            cb(&*ptr)
-        }
-    }
-
-    /// Test whether this TLS key has been `set` for the current thread.
-    pub fn is_set(&'static self) -> bool {
-        unsafe { !(self.inner)().get().is_null() }
-    }
-}
-
-#[cfg(target_thread_local)]
-#[doc(hidden)]
-mod imp {
-    use cell::Cell;
-    use ptr;
-
-    pub struct KeyInner<T> { inner: Cell<*mut T> }
-
-    unsafe impl<T> ::marker::Sync for KeyInner<T> { }
-
-    impl<T> KeyInner<T> {
-        pub const fn new() -> KeyInner<T> {
-            KeyInner { inner: Cell::new(ptr::null_mut()) }
-        }
-        pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr); }
-        pub unsafe fn get(&self) -> *mut T { self.inner.get() }
-    }
-}
-
-#[cfg(not(target_thread_local))]
-#[doc(hidden)]
-mod imp {
-    use cell::Cell;
-    use marker;
-    use sys_common::thread_local::StaticKey as OsStaticKey;
-
-    pub struct KeyInner<T> {
-        pub inner: OsStaticKey,
-        pub marker: marker::PhantomData<Cell<T>>,
-    }
-
-    unsafe impl<T> marker::Sync for KeyInner<T> { }
-
-    impl<T> KeyInner<T> {
-        pub const fn new() -> KeyInner<T> {
-            KeyInner {
-                inner: OsStaticKey::new(None),
-                marker: marker::PhantomData
-            }
-        }
-        pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) }
-        pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ }
-    }
-}
-
-
-#[cfg(test)]
-mod tests {
-    use cell::Cell;
-
-    scoped_thread_local!(static FOO: u32);
-
-    #[test]
-    fn smoke() {
-        scoped_thread_local!(static BAR: u32);
-
-        assert!(!BAR.is_set());
-        BAR.set(&1, || {
-            assert!(BAR.is_set());
-            BAR.with(|slot| {
-                assert_eq!(*slot, 1);
-            });
-        });
-        assert!(!BAR.is_set());
-    }
-
-    #[test]
-    fn cell_allowed() {
-        scoped_thread_local!(static BAR: Cell<u32>);
-
-        BAR.set(&Cell::new(1), || {
-            BAR.with(|slot| {
-                assert_eq!(slot.get(), 1);
-            });
-        });
-    }
-
-    #[test]
-    fn scope_item_allowed() {
-        assert!(!FOO.is_set());
-        FOO.set(&1, || {
-            assert!(FOO.is_set());
-            FOO.with(|slot| {
-                assert_eq!(*slot, 1);
-            });
-        });
-        assert!(!FOO.is_set());
-    }
-}
index 80963a9b735a5e32294d0753d953d8247f27d0e8..0e1508a1c4c28d130baf1813096a7c5ac2960cc8 100644 (file)
@@ -143,13 +143,6 @@ impl Instant {
         self.0.sub_instant(&earlier.0)
     }
 
-    /// Deprecated, renamed to `duration_since`
-    #[unstable(feature = "time2_old", issue = "29866")]
-    #[rustc_deprecated(since = "1.8.0", reason = "renamed to duration_since")]
-    pub fn duration_from_earlier(&self, earlier: Instant) -> Duration {
-        self.0.sub_instant(&earlier.0)
-    }
-
     /// Returns the amount of time elapsed since this instant was created.
     ///
     /// # Panics
@@ -235,14 +228,6 @@ impl SystemTime {
         self.0.sub_time(&earlier.0).map_err(SystemTimeError)
     }
 
-    /// Deprecated, renamed to `duration_since`
-    #[unstable(feature = "time2_old", issue = "29866")]
-    #[rustc_deprecated(since = "1.8.0", reason = "renamed to duration_since")]
-    pub fn duration_from_earlier(&self, earlier: SystemTime)
-                                 -> Result<Duration, SystemTimeError> {
-        self.0.sub_time(&earlier.0).map_err(SystemTimeError)
-    }
-
     /// Returns the amount of time elapsed since this system time was created.
     ///
     /// This function may fail as the underlying system clock is susceptible to
index 964f2dcb6b6b79119615c42b14b42e9c2e16603e..8b61e1b0d3a384c1960b88b4a11751c95edd2d80 100644 (file)
@@ -12,3 +12,5 @@ crate-type = ["dylib"]
 serialize = { path = "../libserialize" }
 log = { path = "../liblog" }
 rustc_bitflags = { path = "../librustc_bitflags" }
+syntax_pos = { path = "../libsyntax_pos" }
+rustc_errors = { path = "../librustc_errors" }
index d9409d3bbd9213bf3fe842c69a316b6d67d30153..cc033cec8b8b1db948ee3f47ae983315a6e8d81e 100644 (file)
@@ -14,22 +14,18 @@ pub use self::TyParamBound::*;
 pub use self::UnsafeSource::*;
 pub use self::ViewPath_::*;
 pub use self::PathParameters::*;
+pub use util::ThinVec;
 
-use attr::ThinAttributes;
-use codemap::{mk_sp, respan, Span, Spanned, DUMMY_SP, ExpnId};
+use syntax_pos::{mk_sp, Span, DUMMY_SP, ExpnId};
+use codemap::{respan, Spanned};
 use abi::Abi;
-use errors;
-use ext::base;
-use ext::tt::macro_parser;
 use parse::token::{self, keywords, InternedString};
-use parse::lexer;
-use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
 use print::pprust;
 use ptr::P;
+use tokenstream::{TokenTree};
 
 use std::fmt;
 use std::rc::Rc;
-use std::borrow::Cow;
 use std::hash::{Hash, Hasher};
 use serialize::{Encodable, Decodable, Encoder, Decoder};
 
@@ -60,10 +56,6 @@ impl Name {
     pub fn as_str(self) -> token::InternedString {
         token::InternedString::new_from_name(self)
     }
-
-    pub fn unhygienize(self) -> Name {
-        token::intern(&self.as_str())
-    }
 }
 
 impl fmt::Debug for Name {
@@ -175,16 +167,19 @@ impl fmt::Debug for Lifetime {
     }
 }
 
-/// A lifetime definition, eg `'a: 'b+'c+'d`
+/// A lifetime definition, e.g. `'a: 'b+'c+'d`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct LifetimeDef {
     pub lifetime: Lifetime,
     pub bounds: Vec<Lifetime>
 }
 
-/// A "Path" is essentially Rust's notion of a name; for instance:
-/// std::cmp::PartialEq  .  It's represented as a sequence of identifiers,
+/// A "Path" is essentially Rust's notion of a name.
+///
+/// It's represented as a sequence of identifiers,
 /// along with a bunch of supporting information.
+///
+/// E.g. `std::cmp::PartialEq`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]
 pub struct Path {
     pub span: Span,
@@ -224,8 +219,9 @@ impl Path {
     }
 }
 
-/// A segment of a path: an identifier, an optional lifetime, and a set of
-/// types.
+/// A segment of a path: an identifier, an optional lifetime, and a set of types.
+///
+/// E.g. `std`, `String` or `Box<T>`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct PathSegment {
     /// The identifier portion of this path segment.
@@ -239,6 +235,9 @@ pub struct PathSegment {
     pub parameters: PathParameters,
 }
 
+/// Parameters of a path segment.
+///
+/// E.g. `<A, B>` as in `Foo<A, B>` or `(A, B)` as in `Foo(A, B)`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum PathParameters {
     /// The `<'a, A,B,C>` in `foo::bar::baz::<'a, A,B,C>`
@@ -326,7 +325,8 @@ pub struct AngleBracketedParameterData {
     /// The type parameters for this path segment, if present.
     pub types: P<[P<Ty>]>,
     /// Bindings (equality constraints) on associated types, if present.
-    /// e.g., `Foo<A=Bar>`.
+    ///
+    /// E.g., `Foo<A=Bar>`.
     pub bindings: P<[TypeBinding]>,
 }
 
@@ -361,15 +361,6 @@ pub const CRATE_NODE_ID: NodeId = 0;
 /// small, positive ids.
 pub const DUMMY_NODE_ID: NodeId = !0;
 
-pub trait NodeIdAssigner {
-    fn next_node_id(&self) -> NodeId;
-    fn peek_node_id(&self) -> NodeId;
-
-    fn diagnostic(&self) -> &errors::Handler {
-        panic!("this ID assigner cannot emit diagnostics")
-    }
-}
-
 /// The AST represents all type param bounds as types.
 /// typeck::collect::compute_bounds matches these against
 /// the "special" built-in traits (see middle::lang_items) and
@@ -451,7 +442,9 @@ pub enum WherePredicate {
     EqPredicate(WhereEqPredicate),
 }
 
-/// A type bound, e.g. `for<'c> Foo: Send+Clone+'c`
+/// A type bound.
+///
+/// E.g. `for<'c> Foo: Send+Clone+'c`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct WhereBoundPredicate {
     pub span: Span,
@@ -463,7 +456,9 @@ pub struct WhereBoundPredicate {
     pub bounds: TyParamBounds,
 }
 
-/// A lifetime predicate, e.g. `'a: 'b+'c`
+/// A lifetime predicate.
+///
+/// E.g. `'a: 'b+'c`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct WhereRegionPredicate {
     pub span: Span,
@@ -471,7 +466,9 @@ pub struct WhereRegionPredicate {
     pub bounds: Vec<Lifetime>,
 }
 
-/// An equality predicate (unsupported), e.g. `T=int`
+/// An equality predicate (unsupported).
+///
+/// E.g. `T=int`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct WhereEqPredicate {
     pub id: NodeId,
@@ -493,12 +490,27 @@ pub struct Crate {
     pub exported_macros: Vec<MacroDef>,
 }
 
+/// A spanned compile-time attribute item.
+///
+/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
 pub type MetaItem = Spanned<MetaItemKind>;
 
+/// A compile-time attribute item.
+///
+/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
 #[derive(Clone, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum MetaItemKind {
+    /// Word meta item.
+    ///
+    /// E.g. `test` as in `#[test]`
     Word(InternedString),
+    /// List meta item.
+    ///
+    /// E.g. `derive(..)` as in `#[derive(..)]`
     List(InternedString, Vec<P<MetaItem>>),
+    /// Name value meta item.
+    ///
+    /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
     NameValue(InternedString, Lit),
 }
 
@@ -528,13 +540,13 @@ impl PartialEq for MetaItemKind {
     }
 }
 
+/// A Block (`{ .. }`).
+///
+/// E.g. `{ .. }` as in `fn foo() { .. }`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Block {
     /// Statements in a block
     pub stmts: Vec<Stmt>,
-    /// An expression at the end of the block
-    /// without a semicolon, if any
-    pub expr: Option<P<Expr>>,
     pub id: NodeId,
     /// Distinguishes between `unsafe { ... }` and `{ ... }`
     pub rules: BlockCheckMode,
@@ -567,7 +579,7 @@ impl Pat {
             PatKind::Struct(_, ref fields, _) => {
                 fields.iter().all(|field| field.node.pat.walk(it))
             }
-            PatKind::TupleStruct(_, Some(ref s)) | PatKind::Tup(ref s) => {
+            PatKind::TupleStruct(_, ref s, _) | PatKind::Tuple(ref s, _) => {
                 s.iter().all(|p| p.walk(it))
             }
             PatKind::Box(ref s) | PatKind::Ref(ref s, _) => {
@@ -582,9 +594,7 @@ impl Pat {
             PatKind::Lit(_) |
             PatKind::Range(_, _) |
             PatKind::Ident(_, _, _) |
-            PatKind::TupleStruct(..) |
             PatKind::Path(..) |
-            PatKind::QPath(_, _) |
             PatKind::Mac(_) => {
                 true
             }
@@ -617,36 +627,31 @@ pub enum PatKind {
     /// Represents a wildcard pattern (`_`)
     Wild,
 
-    /// A `PatKind::Ident` may either be a new bound variable,
-    /// or a unit struct/variant pattern, or a const pattern (in the last two cases
-    /// the third field must be `None`).
-    ///
-    /// In the unit or const pattern case, the parser can't determine
-    /// which it is. The resolver determines this, and
-    /// records this pattern's `NodeId` in an auxiliary
-    /// set (of "PatIdents that refer to unit patterns or constants").
+    /// A `PatKind::Ident` may either be a new bound variable (`ref mut binding @ OPT_SUBPATTERN`),
+    /// or a unit struct/variant pattern, or a const pattern (in the last two cases the third
+    /// field must be `None`). Disambiguation cannot be done with parser alone, so it happens
+    /// during name resolution.
     Ident(BindingMode, SpannedIdent, Option<P<Pat>>),
 
     /// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`.
     /// The `bool` is `true` in the presence of a `..`.
     Struct(Path, Vec<Spanned<FieldPat>>, bool),
 
-    /// A tuple struct/variant pattern `Variant(x, y, z)`.
-    /// "None" means a `Variant(..)` pattern where we don't bind the fields to names.
-    TupleStruct(Path, Option<Vec<P<Pat>>>),
-
-    /// A path pattern.
-    /// Such pattern can be resolved to a unit struct/variant or a constant.
-    Path(Path),
+    /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
+    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    TupleStruct(Path, Vec<P<Pat>>, Option<usize>),
 
-    /// An associated const named using the qualified path `<T>::CONST` or
-    /// `<T as Trait>::CONST`. Associated consts from inherent impls can be
-    /// referred to as simply `T::CONST`, in which case they will end up as
-    /// PatKind::Path, and the resolver will have to sort that out.
-    QPath(QSelf, Path),
+    /// A possibly qualified path pattern.
+    /// Unquailfied path patterns `A::B::C` can legally refer to variants, structs, constants
+    /// or associated constants. Quailfied path patterns `<A>::B::C`/`<A as Trait>::B::C` can
+    /// only legally refer to associated constants.
+    Path(Option<QSelf>, Path),
 
-    /// A tuple pattern `(a, b)`
-    Tup(Vec<P<Pat>>),
+    /// A tuple pattern `(a, b)`.
+    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
+    /// 0 <= position <= subpats.len()
+    Tuple(Vec<P<Pat>>, Option<usize>),
     /// A `box` pattern
     Box(P<Pat>),
     /// A reference pattern, e.g. `&mut (a, b)`
@@ -792,51 +797,34 @@ impl UnOp {
 }
 
 /// A statement
-pub type Stmt = Spanned<StmtKind>;
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]
+pub struct Stmt {
+    pub id: NodeId,
+    pub node: StmtKind,
+    pub span: Span,
+}
 
 impl fmt::Debug for Stmt {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "stmt({}: {})",
-               self.node.id()
-                   .map_or(Cow::Borrowed("<macro>"),|id|Cow::Owned(id.to_string())),
-               pprust::stmt_to_string(self))
+        write!(f, "stmt({}: {})", self.id.to_string(), pprust::stmt_to_string(self))
     }
 }
 
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]
 pub enum StmtKind {
-    /// Could be an item or a local (let) binding:
-    Decl(P<Decl>, NodeId),
-
-    /// Expr without trailing semi-colon (must have unit type):
-    Expr(P<Expr>, NodeId),
+    /// A local (let) binding.
+    Local(P<Local>),
 
-    /// Expr with trailing semi-colon (may have any type):
-    Semi(P<Expr>, NodeId),
+    /// An item definition.
+    Item(P<Item>),
 
-    Mac(P<Mac>, MacStmtStyle, ThinAttributes),
-}
+    /// Expr without trailing semi-colon.
+    Expr(P<Expr>),
 
-impl StmtKind {
-    pub fn id(&self) -> Option<NodeId> {
-        match *self {
-            StmtKind::Decl(_, id) => Some(id),
-            StmtKind::Expr(_, id) => Some(id),
-            StmtKind::Semi(_, id) => Some(id),
-            StmtKind::Mac(..) => None,
-        }
-    }
+    Semi(P<Expr>),
 
-    pub fn attrs(&self) -> &[Attribute] {
-        match *self {
-            StmtKind::Decl(ref d, _) => d.attrs(),
-            StmtKind::Expr(ref e, _) |
-            StmtKind::Semi(ref e, _) => e.attrs(),
-            StmtKind::Mac(_, _, Some(ref b)) => b,
-            StmtKind::Mac(_, _, None) => &[],
-        }
-    }
+    Mac(P<(Mac, MacStmtStyle, ThinVec<Attribute>)>),
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -863,38 +851,19 @@ pub struct Local {
     pub init: Option<P<Expr>>,
     pub id: NodeId,
     pub span: Span,
-    pub attrs: ThinAttributes,
-}
-
-impl Local {
-    pub fn attrs(&self) -> &[Attribute] {
-        match self.attrs {
-            Some(ref b) => b,
-            None => &[],
-        }
-    }
-}
-
-pub type Decl = Spanned<DeclKind>;
-
-#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
-pub enum DeclKind {
-    /// A local (let) binding:
-    Local(P<Local>),
-    /// An item binding:
-    Item(P<Item>),
+    pub attrs: ThinVec<Attribute>,
 }
 
-impl Decl {
-    pub fn attrs(&self) -> &[Attribute] {
-        match self.node {
-            DeclKind::Local(ref l) => l.attrs(),
-            DeclKind::Item(ref i) => i.attrs(),
-        }
-    }
-}
-
-/// represents one arm of a 'match'
+/// An arm of a 'match'.
+///
+/// E.g. `0...10 => { println!("match!") }` as in
+///
+/// ```rust,ignore
+/// match n {
+///     0...10 => { println!("match!") },
+///     // ..
+/// }
+/// ```
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Arm {
     pub attrs: Vec<Attribute>,
@@ -930,16 +899,7 @@ pub struct Expr {
     pub id: NodeId,
     pub node: ExprKind,
     pub span: Span,
-    pub attrs: ThinAttributes
-}
-
-impl Expr {
-    pub fn attrs(&self) -> &[Attribute] {
-        match self.attrs {
-            Some(ref b) => b,
-            None => &[],
-        }
-    }
+    pub attrs: ThinVec<Attribute>
 }
 
 impl fmt::Debug for Expr {
@@ -1007,23 +967,23 @@ pub enum ExprKind {
     /// A while loop, with an optional label
     ///
     /// `'label: while expr { block }`
-    While(P<Expr>, P<Block>, Option<Ident>),
+    While(P<Expr>, P<Block>, Option<SpannedIdent>),
     /// A while-let loop, with an optional label
     ///
     /// `'label: while let pat = expr { block }`
     ///
     /// This is desugared to a combination of `loop` and `match` expressions.
-    WhileLet(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
+    WhileLet(P<Pat>, P<Expr>, P<Block>, Option<SpannedIdent>),
     /// A for loop, with an optional label
     ///
     /// `'label: for pat in expr { block }`
     ///
     /// This is desugared to a combination of `loop` and `match` expressions.
-    ForLoop(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
+    ForLoop(P<Pat>, P<Expr>, P<Block>, Option<SpannedIdent>),
     /// Conditionless loop (can be exited with break, continue, or return)
     ///
     /// `'label: loop { block }`
-    Loop(P<Block>, Option<Ident>),
+    Loop(P<Block>, Option<SpannedIdent>),
     /// A `match` block.
     Match(P<Expr>, Vec<Arm>),
     /// A closure (for example, `move |a, b, c| {a + b + c}`)
@@ -1054,7 +1014,7 @@ pub enum ExprKind {
     /// parameters, e.g. foo::bar::<baz>.
     ///
     /// Optionally "qualified",
-    /// e.g. `<Vec<T> as SomeTrait>::SomeType`.
+    /// E.g. `<Vec<T> as SomeTrait>::SomeType`.
     Path(Option<QSelf>, Path),
 
     /// A referencing operation (`&a` or `&mut a`)
@@ -1062,7 +1022,7 @@ pub enum ExprKind {
     /// A `break`, with an optional label to break
     Break(Option<SpannedIdent>),
     /// A `continue`, with an optional label
-    Again(Option<SpannedIdent>),
+    Continue(Option<SpannedIdent>),
     /// A `return`, with an optional value to be returned
     Ret(Option<P<Expr>>),
 
@@ -1096,7 +1056,7 @@ pub enum ExprKind {
 /// separately. `position` represents the index of the associated
 /// item qualified with this Self type.
 ///
-/// ```ignore
+/// ```rust,ignore
 /// <Vec<T> as a::b::Trait>::AssociatedItem
 ///  ^~~~~     ~~~~~~~~~~~~~~^
 ///  ty        position = 3
@@ -1118,193 +1078,6 @@ pub enum CaptureBy {
     Ref,
 }
 
-/// A delimited sequence of token trees
-#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
-pub struct Delimited {
-    /// The type of delimiter
-    pub delim: token::DelimToken,
-    /// The span covering the opening delimiter
-    pub open_span: Span,
-    /// The delimited sequence of token trees
-    pub tts: Vec<TokenTree>,
-    /// The span covering the closing delimiter
-    pub close_span: Span,
-}
-
-impl Delimited {
-    /// Returns the opening delimiter as a token.
-    pub fn open_token(&self) -> token::Token {
-        token::OpenDelim(self.delim)
-    }
-
-    /// Returns the closing delimiter as a token.
-    pub fn close_token(&self) -> token::Token {
-        token::CloseDelim(self.delim)
-    }
-
-    /// Returns the opening delimiter as a token tree.
-    pub fn open_tt(&self) -> TokenTree {
-        TokenTree::Token(self.open_span, self.open_token())
-    }
-
-    /// Returns the closing delimiter as a token tree.
-    pub fn close_tt(&self) -> TokenTree {
-        TokenTree::Token(self.close_span, self.close_token())
-    }
-}
-
-/// A sequence of token trees
-#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
-pub struct SequenceRepetition {
-    /// The sequence of token trees
-    pub tts: Vec<TokenTree>,
-    /// The optional separator
-    pub separator: Option<token::Token>,
-    /// Whether the sequence can be repeated zero (*), or one or more times (+)
-    pub op: KleeneOp,
-    /// The number of `MatchNt`s that appear in the sequence (and subsequences)
-    pub num_captures: usize,
-}
-
-/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star)
-/// for token sequences.
-#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
-pub enum KleeneOp {
-    ZeroOrMore,
-    OneOrMore,
-}
-
-/// When the main rust parser encounters a syntax-extension invocation, it
-/// parses the arguments to the invocation as a token-tree. This is a very
-/// loose structure, such that all sorts of different AST-fragments can
-/// be passed to syntax extensions using a uniform type.
-///
-/// If the syntax extension is an MBE macro, it will attempt to match its
-/// LHS token tree against the provided token tree, and if it finds a
-/// match, will transcribe the RHS token tree, splicing in any captured
-/// macro_parser::matched_nonterminals into the `SubstNt`s it finds.
-///
-/// The RHS of an MBE macro is the only place `SubstNt`s are substituted.
-/// Nothing special happens to misnamed or misplaced `SubstNt`s.
-#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
-pub enum TokenTree {
-    /// A single token
-    Token(Span, token::Token),
-    /// A delimited sequence of token trees
-    Delimited(Span, Rc<Delimited>),
-
-    // This only makes sense in MBE macros.
-
-    /// A kleene-style repetition sequence with a span
-    // FIXME(eddyb) #12938 Use DST.
-    Sequence(Span, Rc<SequenceRepetition>),
-}
-
-impl TokenTree {
-    pub fn len(&self) -> usize {
-        match *self {
-            TokenTree::Token(_, token::DocComment(name)) => {
-                match doc_comment_style(&name.as_str()) {
-                    AttrStyle::Outer => 2,
-                    AttrStyle::Inner => 3
-                }
-            }
-            TokenTree::Token(_, token::SpecialVarNt(..)) => 2,
-            TokenTree::Token(_, token::MatchNt(..)) => 3,
-            TokenTree::Delimited(_, ref delimed) => {
-                delimed.tts.len() + 2
-            }
-            TokenTree::Sequence(_, ref seq) => {
-                seq.tts.len()
-            }
-            TokenTree::Token(..) => 0
-        }
-    }
-
-    pub fn get_tt(&self, index: usize) -> TokenTree {
-        match (self, index) {
-            (&TokenTree::Token(sp, token::DocComment(_)), 0) => {
-                TokenTree::Token(sp, token::Pound)
-            }
-            (&TokenTree::Token(sp, token::DocComment(name)), 1)
-            if doc_comment_style(&name.as_str()) == AttrStyle::Inner => {
-                TokenTree::Token(sp, token::Not)
-            }
-            (&TokenTree::Token(sp, token::DocComment(name)), _) => {
-                let stripped = strip_doc_comment_decoration(&name.as_str());
-
-                // Searches for the occurrences of `"#*` and returns the minimum number of `#`s
-                // required to wrap the text.
-                let num_of_hashes = stripped.chars().scan(0, |cnt, x| {
-                    *cnt = if x == '"' {
-                        1
-                    } else if *cnt != 0 && x == '#' {
-                        *cnt + 1
-                    } else {
-                        0
-                    };
-                    Some(*cnt)
-                }).max().unwrap_or(0);
-
-                TokenTree::Delimited(sp, Rc::new(Delimited {
-                    delim: token::Bracket,
-                    open_span: sp,
-                    tts: vec![TokenTree::Token(sp, token::Ident(token::str_to_ident("doc"))),
-                              TokenTree::Token(sp, token::Eq),
-                              TokenTree::Token(sp, token::Literal(
-                                  token::StrRaw(token::intern(&stripped), num_of_hashes), None))],
-                    close_span: sp,
-                }))
-            }
-            (&TokenTree::Delimited(_, ref delimed), _) => {
-                if index == 0 {
-                    return delimed.open_tt();
-                }
-                if index == delimed.tts.len() + 1 {
-                    return delimed.close_tt();
-                }
-                delimed.tts[index - 1].clone()
-            }
-            (&TokenTree::Token(sp, token::SpecialVarNt(var)), _) => {
-                let v = [TokenTree::Token(sp, token::Dollar),
-                         TokenTree::Token(sp, token::Ident(token::str_to_ident(var.as_str())))];
-                v[index].clone()
-            }
-            (&TokenTree::Token(sp, token::MatchNt(name, kind)), _) => {
-                let v = [TokenTree::Token(sp, token::SubstNt(name)),
-                         TokenTree::Token(sp, token::Colon),
-                         TokenTree::Token(sp, token::Ident(kind))];
-                v[index].clone()
-            }
-            (&TokenTree::Sequence(_, ref seq), _) => {
-                seq.tts[index].clone()
-            }
-            _ => panic!("Cannot expand a token tree")
-        }
-    }
-
-    /// Returns the `Span` corresponding to this token tree.
-    pub fn get_span(&self) -> Span {
-        match *self {
-            TokenTree::Token(span, _)     => span,
-            TokenTree::Delimited(span, _) => span,
-            TokenTree::Sequence(span, _)  => span,
-        }
-    }
-
-    /// Use this token tree as a matcher to parse given tts.
-    pub fn parse(cx: &base::ExtCtxt, mtch: &[TokenTree], tts: &[TokenTree])
-                 -> macro_parser::NamedParseResult {
-        // `None` is because we're not interpolating
-        let arg_rdr = lexer::new_tt_reader_with_doc_flag(&cx.parse_sess().span_diagnostic,
-                                                         None,
-                                                         None,
-                                                         tts.iter().cloned().collect(),
-                                                         true);
-        macro_parser::parse(cx.parse_sess(), cx.cfg(), arg_rdr, mtch)
-    }
-}
-
 pub type Mac = Spanned<Mac_>;
 
 /// Represents a macro invocation. The Path indicates which macro
@@ -1317,7 +1090,6 @@ pub type Mac = Spanned<Mac_>;
 pub struct Mac_ {
     pub path: Path,
     pub tts: Vec<TokenTree>,
-    pub ctxt: SyntaxContext,
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
@@ -1340,6 +1112,9 @@ pub enum LitIntType {
     Unsuffixed,
 }
 
+/// Literal kind.
+///
+/// E.g. `"foo"`, `42`, `12.34` or `bool`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum LitKind {
     /// A string literal (`"foo"`)
@@ -1387,7 +1162,6 @@ pub struct MethodSig {
     pub abi: Abi,
     pub decl: P<FnDecl>,
     pub generics: Generics,
-    pub explicit_self: ExplicitSelf,
 }
 
 /// Represents an item declaration within a trait declaration,
@@ -1408,6 +1182,7 @@ pub enum TraitItemKind {
     Const(P<Ty>, Option<P<Expr>>),
     Method(MethodSig, Option<P<Block>>),
     Type(TyParamBounds, Option<P<Ty>>),
+    Macro(Mac),
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -1608,8 +1383,8 @@ pub struct BareFnTy {
     pub decl: P<FnDecl>
 }
 
-#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 /// The different kinds of types recognized by the compiler
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum TyKind {
     Vec(P<Ty>),
     /// A fixed length array (`[T; n]`)
@@ -1638,16 +1413,24 @@ pub enum TyKind {
     /// TyKind::Infer means the type should be inferred instead of it having been
     /// specified. This can appear anywhere in a type.
     Infer,
+    /// Inferred type of a `self` or `&self` argument in a method.
+    ImplicitSelf,
     // A macro in the type position.
     Mac(Mac),
 }
 
+/// Inline assembly dialect.
+///
+/// E.g. `"intel"` as in `asm!("mov eax, 2" : "={eax}"(result) : : : "intel")``
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
 pub enum AsmDialect {
     Att,
     Intel,
 }
 
+/// Inline assembly.
+///
+/// E.g. `"={eax}"(result)` as in `asm!("mov eax, 2" : "={eax}"(result) : : : "intel")``
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct InlineAsmOutput {
     pub constraint: InternedString,
@@ -1656,6 +1439,9 @@ pub struct InlineAsmOutput {
     pub is_indirect: bool,
 }
 
+/// Inline assembly.
+///
+/// E.g. `asm!("NOP");`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct InlineAsm {
     pub asm: InternedString,
@@ -1669,7 +1455,9 @@ pub struct InlineAsm {
     pub expn_id: ExpnId,
 }
 
-/// represents an argument in a function header
+/// An argument in a function header.
+///
+/// E.g. `bar: usize` as in `fn foo(bar: usize)`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Arg {
     pub ty: P<Ty>,
@@ -1677,81 +1465,67 @@ pub struct Arg {
     pub id: NodeId,
 }
 
-/// Represents the kind of 'self' associated with a method.
-/// String representation of `Ident` here is always "self", but hygiene contexts may differ.
+/// Alternative representation for `Arg`s describing `self` parameter of methods.
+///
+/// E.g. `&mut self` as in `fn foo(&mut self)`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum SelfKind {
-    /// No self
-    Static,
     /// `self`, `mut self`
-    Value(Ident),
+    Value(Mutability),
     /// `&'lt self`, `&'lt mut self`
-    Region(Option<Lifetime>, Mutability, Ident),
+    Region(Option<Lifetime>, Mutability),
     /// `self: TYPE`, `mut self: TYPE`
-    Explicit(P<Ty>, Ident),
+    Explicit(P<Ty>, Mutability),
 }
 
 pub type ExplicitSelf = Spanned<SelfKind>;
 
 impl Arg {
-    #[unstable(feature = "rustc_private", issue = "27812")]
-    #[rustc_deprecated(since = "1.10.0", reason = "use `from_self` instead")]
-    pub fn new_self(span: Span, mutability: Mutability, self_ident: Ident) -> Arg {
-        let path = Spanned{span:span,node:self_ident};
-        Arg {
-            // HACK(eddyb) fake type for the self argument.
-            ty: P(Ty {
-                id: DUMMY_NODE_ID,
-                node: TyKind::Infer,
-                span: DUMMY_SP,
-            }),
-            pat: P(Pat {
-                id: DUMMY_NODE_ID,
-                node: PatKind::Ident(BindingMode::ByValue(mutability), path, None),
-                span: span
-            }),
-            id: DUMMY_NODE_ID
-        }
-    }
-
     pub fn to_self(&self) -> Option<ExplicitSelf> {
-        if let PatKind::Ident(_, ident, _) = self.pat.node {
+        if let PatKind::Ident(BindingMode::ByValue(mutbl), ident, _) = self.pat.node {
             if ident.node.name == keywords::SelfValue.name() {
                 return match self.ty.node {
-                    TyKind::Infer => Some(respan(self.pat.span, SelfKind::Value(ident.node))),
-                    TyKind::Rptr(lt, MutTy{ref ty, mutbl}) if ty.node == TyKind::Infer => {
-                        Some(respan(self.pat.span, SelfKind::Region(lt, mutbl, ident.node)))
+                    TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
+                    TyKind::Rptr(lt, MutTy{ref ty, mutbl}) if ty.node == TyKind::ImplicitSelf => {
+                        Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
                     }
                     _ => Some(respan(mk_sp(self.pat.span.lo, self.ty.span.hi),
-                                     SelfKind::Explicit(self.ty.clone(), ident.node))),
+                                     SelfKind::Explicit(self.ty.clone(), mutbl))),
                 }
             }
         }
         None
     }
 
-    pub fn from_self(eself: ExplicitSelf, ident_sp: Span, mutbl: Mutability) -> Arg {
-        let pat = |ident, span| P(Pat {
-            id: DUMMY_NODE_ID,
-            node: PatKind::Ident(BindingMode::ByValue(mutbl), respan(ident_sp, ident), None),
-            span: span,
-        });
+    pub fn is_self(&self) -> bool {
+        if let PatKind::Ident(_, ident, _) = self.pat.node {
+            ident.node.name == keywords::SelfValue.name()
+        } else {
+            false
+        }
+    }
+
+    pub fn from_self(eself: ExplicitSelf, eself_ident: SpannedIdent) -> Arg {
         let infer_ty = P(Ty {
             id: DUMMY_NODE_ID,
-            node: TyKind::Infer,
+            node: TyKind::ImplicitSelf,
             span: DUMMY_SP,
         });
-        let arg = |ident, ty, span| Arg {
-            pat: pat(ident, span),
+        let arg = |mutbl, ty, span| Arg {
+            pat: P(Pat {
+                id: DUMMY_NODE_ID,
+                node: PatKind::Ident(BindingMode::ByValue(mutbl), eself_ident, None),
+                span: span,
+            }),
             ty: ty,
             id: DUMMY_NODE_ID,
         };
         match eself.node {
-            SelfKind::Static => panic!("bug: `Arg::from_self` is called \
-                                        with `SelfKind::Static` argument"),
-            SelfKind::Explicit(ty, ident) => arg(ident, ty, mk_sp(eself.span.lo, ident_sp.hi)),
-            SelfKind::Value(ident) => arg(ident, infer_ty, eself.span),
-            SelfKind::Region(lt, mutbl, ident) => arg(ident, P(Ty {
+            SelfKind::Explicit(ty, mutbl) => {
+                arg(mutbl, ty, mk_sp(eself.span.lo, eself_ident.span.hi))
+            }
+            SelfKind::Value(mutbl) => arg(mutbl, infer_ty, eself.span),
+            SelfKind::Region(lt, mutbl) => arg(Mutability::Immutable, P(Ty {
                 id: DUMMY_NODE_ID,
                 node: TyKind::Rptr(lt, MutTy { ty: infer_ty, mutbl: mutbl }),
                 span: DUMMY_SP,
@@ -1760,7 +1534,9 @@ impl Arg {
     }
 }
 
-/// Represents the header (not the body) of a function declaration
+/// Header (not the body) of a function declaration.
+///
+/// E.g. `fn foo(bar: baz)`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct FnDecl {
     pub inputs: Vec<Arg>,
@@ -1768,6 +1544,15 @@ pub struct FnDecl {
     pub variadic: bool
 }
 
+impl FnDecl {
+    pub fn get_self(&self) -> Option<ExplicitSelf> {
+        self.inputs.get(0).and_then(Arg::to_self)
+    }
+    pub fn has_self(&self) -> bool {
+        self.inputs.get(0).map(Arg::is_self).unwrap_or(false)
+    }
+}
+
 #[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum Unsafety {
     Unsafe,
@@ -1838,6 +1623,9 @@ impl FunctionRetTy {
     }
 }
 
+/// Module declaration.
+///
+/// E.g. `mod foo;` or `mod foo { .. }`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Mod {
     /// A span from the first token past `{` to the last token until `}`.
@@ -1847,6 +1635,9 @@ pub struct Mod {
     pub items: Vec<P<Item>>,
 }
 
+/// Foreign module declaration.
+///
+/// E.g. `extern { .. }` or `extern C { .. }`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct ForeignMod {
     pub abi: Abi,
@@ -1863,7 +1654,7 @@ pub struct Variant_ {
     pub name: Ident,
     pub attrs: Vec<Attribute>,
     pub data: VariantData,
-    /// Explicit discriminant, eg `Foo = 1`
+    /// Explicit discriminant, e.g. `Foo = 1`
     pub disr_expr: Option<P<Expr>>,
 }
 
@@ -1873,12 +1664,12 @@ pub type Variant = Spanned<Variant_>;
 pub enum PathListItemKind {
     Ident {
         name: Ident,
-        /// renamed in list, eg `use foo::{bar as baz};`
+        /// renamed in list, e.g. `use foo::{bar as baz};`
         rename: Option<Ident>,
         id: NodeId
     },
     Mod {
-        /// renamed in list, eg `use foo::{self as baz};`
+        /// renamed in list, e.g. `use foo::{self as baz};`
         rename: Option<Ident>,
         id: NodeId
     }
@@ -1926,6 +1717,16 @@ pub enum ViewPath_ {
     ViewPathList(Path, Vec<PathListItem>)
 }
 
+impl ViewPath_ {
+    pub fn path(&self) -> &Path {
+        match *self {
+            ViewPathSimple(_, ref path) |
+            ViewPathGlob (ref path) |
+            ViewPathList(ref path, _) => path
+        }
+    }
+}
+
 /// Meta-data associated with an item
 pub type Attribute = Spanned<Attribute_>;
 
@@ -1981,6 +1782,9 @@ pub enum Visibility {
     Inherited,
 }
 
+/// Field of a struct.
+///
+/// E.g. `bar: usize` as in `struct Foo { bar: usize }`
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct StructField {
     pub span: Span,
@@ -2004,8 +1808,17 @@ pub struct StructField {
 /// Id of the whole struct lives in `Item`.
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum VariantData {
+    /// Struct variant.
+    ///
+    /// E.g. `Bar { .. }` as in `enum Foo { Bar { .. } }`
     Struct(Vec<StructField>, NodeId),
+    /// Tuple variant.
+    ///
+    /// E.g. `Bar(..)` as in `enum Foo { Bar(..) }`
     Tuple(Vec<StructField>, NodeId),
+    /// Unit variant.
+    ///
+    /// E.g. `Bar = ..` as in `enum Foo { Bar = .. }`
     Unit(NodeId),
 }
 
@@ -2049,52 +1862,68 @@ pub struct Item {
     pub span: Span,
 }
 
-impl Item {
-    pub fn attrs(&self) -> &[Attribute] {
-        &self.attrs
-    }
-}
-
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum ItemKind {
-    /// An`extern crate` item, with optional original crate name,
+    /// An`extern crate` item, with optional original crate name.
     ///
-    /// e.g. `extern crate foo` or `extern crate foo_bar as foo`
+    /// E.g. `extern crate foo` or `extern crate foo_bar as foo`
     ExternCrate(Option<Name>),
-    /// A `use` or `pub use` item
+    /// A use declaration (`use` or `pub use`) item.
+    ///
+    /// E.g. `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;`
     Use(P<ViewPath>),
-
-    /// A `static` item
+    /// A static item (`static` or `pub static`).
+    ///
+    /// E.g. `static FOO: i32 = 42;` or `static FOO: &'static str = "bar";`
     Static(P<Ty>, Mutability, P<Expr>),
-    /// A `const` item
+    /// A constant item (`const` or `pub const`).
+    ///
+    /// E.g. `const FOO: i32 = 42;`
     Const(P<Ty>, P<Expr>),
-    /// A function declaration
+    /// A function declaration (`fn` or `pub fn`).
+    ///
+    /// E.g. `fn foo(bar: usize) -> usize { .. }`
     Fn(P<FnDecl>, Unsafety, Constness, Abi, Generics, P<Block>),
-    /// A module
+    /// A module declaration (`mod` or `pub mod`).
+    ///
+    /// E.g. `mod foo;` or `mod foo { .. }`
     Mod(Mod),
-    /// An external module
+    /// An external module (`extern` or `pub extern`).
+    ///
+    /// E.g. `extern {}` or `extern "C" {}`
     ForeignMod(ForeignMod),
-    /// A type alias, e.g. `type Foo = Bar<u8>`
+    /// A type alias (`type` or `pub type`).
+    ///
+    /// E.g. `type Foo = Bar<u8>;`
     Ty(P<Ty>, Generics),
-    /// An enum definition, e.g. `enum Foo<A, B> {C<A>, D<B>}`
+    /// An enum definition (`enum` or `pub enum`).
+    ///
+    /// E.g. `enum Foo<A, B> { C<A>, D<B> }`
     Enum(EnumDef, Generics),
-    /// A struct definition, e.g. `struct Foo<A> {x: A}`
+    /// A struct definition (`struct` or `pub struct`).
+    ///
+    /// E.g. `struct Foo<A> { x: A }`
     Struct(VariantData, Generics),
-    /// Represents a Trait Declaration
+    /// A Trait declaration (`trait` or `pub trait`).
+    ///
+    /// E.g. `trait Foo { .. }` or `trait Foo<T> { .. }`
     Trait(Unsafety, Generics, TyParamBounds, Vec<TraitItem>),
-
-    // Default trait implementations
+    // Default trait implementation.
     ///
-    // `impl Trait for .. {}`
+    /// E.g. `impl Trait for .. {}` or `impl<T> Trait<T> for .. {}`
     DefaultImpl(Unsafety, TraitRef),
-    /// An implementation, eg `impl<A> Trait for Foo { .. }`
+    /// An implementation.
+    ///
+    /// E.g. `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`
     Impl(Unsafety,
              ImplPolarity,
              Generics,
              Option<TraitRef>, // (optional) trait this impl implements
              P<Ty>, // self
              Vec<ImplItem>),
-    /// A macro invocation (which includes macro definition)
+    /// A macro invocation (which includes macro definition).
+    ///
+    /// E.g. `macro_rules! foo { .. }` or `foo!(..)`
     Mac(Mac),
 }
 
index 8761ca3717895adb0aebba24551e46d88d71b67d..e01bd2a93aacdfcb9b806a0af3e5960fdff47d12 100644 (file)
@@ -16,17 +16,16 @@ pub use self::IntType::*;
 
 use ast;
 use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaItemKind};
-use ast::{Stmt, StmtKind, DeclKind};
-use ast::{Expr, Item, Local, Decl};
-use codemap::{Span, Spanned, spanned, dummy_spanned};
-use codemap::BytePos;
-use config::CfgDiag;
+use ast::{Expr, Item, Local, Stmt, StmtKind};
+use codemap::{spanned, dummy_spanned, Spanned};
+use syntax_pos::{Span, BytePos};
 use errors::Handler;
-use feature_gate::{GatedCfg, GatedCfgAttr};
+use feature_gate::{Features, GatedCfg};
 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
 use parse::token::InternedString;
-use parse::token;
+use parse::{ParseSess, token};
 use ptr::P;
+use util::ThinVec;
 
 use std::cell::{RefCell, Cell};
 use std::collections::HashSet;
@@ -35,6 +34,27 @@ thread_local! {
     static USED_ATTRS: RefCell<Vec<u64>> = RefCell::new(Vec::new())
 }
 
+enum AttrError {
+    MultipleItem(InternedString),
+    UnknownMetaItem(InternedString),
+    MissingSince,
+    MissingFeature,
+    MultipleStabilityLevels,
+}
+
+fn handle_errors(diag: &Handler, span: Span, error: AttrError) {
+    match error {
+        AttrError::MultipleItem(item) => span_err!(diag, span, E0538,
+                                                   "multiple '{}' items", item),
+        AttrError::UnknownMetaItem(item) => span_err!(diag, span, E0541,
+                                                      "unknown meta item '{}'", item),
+        AttrError::MissingSince => span_err!(diag, span, E0542, "missing 'since'"),
+        AttrError::MissingFeature => span_err!(diag, span, E0546, "missing 'feature'"),
+        AttrError::MultipleStabilityLevels => span_err!(diag, span, E0544,
+                                                        "multiple stability levels"),
+    }
+}
+
 pub fn mark_used(attr: &Attribute) {
     let AttrId(id) = attr.node.id;
     USED_ATTRS.with(|slot| {
@@ -304,10 +324,10 @@ pub fn find_export_name_attr(diag: &Handler, attrs: &[Attribute]) -> Option<Inte
             if let s@Some(_) = attr.value_str() {
                 s
             } else {
-                diag.struct_span_err(attr.span,
-                                     "export_name attribute has invalid format")
-                    .help("use #[export_name=\"*\"]")
-                    .emit();
+                struct_span_err!(diag, attr.span, E0533,
+                                 "export_name attribute has invalid format")
+                                .help("use #[export_name=\"*\"]")
+                                .emit();
                 None
             }
         } else {
@@ -340,14 +360,16 @@ pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> In
             MetaItemKind::List(ref n, ref items) if n == "inline" => {
                 mark_used(attr);
                 if items.len() != 1 {
-                    diagnostic.map(|d|{ d.span_err(attr.span, "expected one argument"); });
+                    diagnostic.map(|d|{ span_err!(d, attr.span, E0534, "expected one argument"); });
                     InlineAttr::None
                 } else if contains_name(&items[..], "always") {
                     InlineAttr::Always
                 } else if contains_name(&items[..], "never") {
                     InlineAttr::Never
                 } else {
-                    diagnostic.map(|d|{ d.span_err((*items[0]).span, "invalid argument"); });
+                    diagnostic.map(|d| {
+                        span_err!(d, (*items[0]).span, E0535, "invalid argument");
+                    });
                     InlineAttr::None
                 }
             }
@@ -365,35 +387,29 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool {
 }
 
 /// Tests if a cfg-pattern matches the cfg set
-pub fn cfg_matches<T: CfgDiag>(cfgs: &[P<MetaItem>],
-                           cfg: &ast::MetaItem,
-                           diag: &mut T) -> bool {
+pub fn cfg_matches(cfgs: &[P<MetaItem>], cfg: &ast::MetaItem,
+                   sess: &ParseSess, features: Option<&Features>)
+                   -> bool {
     match cfg.node {
         ast::MetaItemKind::List(ref pred, ref mis) if &pred[..] == "any" =>
-            mis.iter().any(|mi| cfg_matches(cfgs, &mi, diag)),
+            mis.iter().any(|mi| cfg_matches(cfgs, &mi, sess, features)),
         ast::MetaItemKind::List(ref pred, ref mis) if &pred[..] == "all" =>
-            mis.iter().all(|mi| cfg_matches(cfgs, &mi, diag)),
+            mis.iter().all(|mi| cfg_matches(cfgs, &mi, sess, features)),
         ast::MetaItemKind::List(ref pred, ref mis) if &pred[..] == "not" => {
             if mis.len() != 1 {
-                diag.emit_error(|diagnostic| {
-                    diagnostic.span_err(cfg.span, "expected 1 cfg-pattern");
-                });
+                span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
                 return false;
             }
-            !cfg_matches(cfgs, &mis[0], diag)
+            !cfg_matches(cfgs, &mis[0], sess, features)
         }
         ast::MetaItemKind::List(ref pred, _) => {
-            diag.emit_error(|diagnostic| {
-                diagnostic.span_err(cfg.span,
-                    &format!("invalid predicate `{}`", pred));
-            });
+            span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", pred);
             false
         },
         ast::MetaItemKind::Word(_) | ast::MetaItemKind::NameValue(..) => {
-            diag.flag_gated(|feature_gated_cfgs| {
-                feature_gated_cfgs.extend(
-                    GatedCfg::gate(cfg).map(GatedCfgAttr::GatedCfg));
-            });
+            if let (Some(features), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
+                gated_cfg.check_and_emit(sess, features);
+            }
             contains(cfgs, cfg)
         }
     }
@@ -453,15 +469,14 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
         if let Some(metas) = attr.meta_item_list() {
             let get = |meta: &MetaItem, item: &mut Option<InternedString>| {
                 if item.is_some() {
-                    diagnostic.span_err(meta.span, &format!("multiple '{}' items",
-                                                             meta.name()));
+                    handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name()));
                     return false
                 }
                 if let Some(v) = meta.value_str() {
                     *item = Some(v);
                     true
                 } else {
-                    diagnostic.span_err(meta.span, "incorrect meta item");
+                    span_err!(diagnostic, meta.span, E0539, "incorrect meta item");
                     false
                 }
             };
@@ -469,7 +484,8 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
             match tag {
                 "rustc_deprecated" => {
                     if rustc_depr.is_some() {
-                        diagnostic.span_err(item_sp, "multiple rustc_deprecated attributes");
+                        span_err!(diagnostic, item_sp, E0540,
+                                  "multiple rustc_deprecated attributes");
                         break
                     }
 
@@ -480,8 +496,8 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                             "since" => if !get(meta, &mut since) { continue 'outer },
                             "reason" => if !get(meta, &mut reason) { continue 'outer },
                             _ => {
-                                diagnostic.span_err(meta.span, &format!("unknown meta item '{}'",
-                                                                        meta.name()));
+                                handle_errors(diagnostic, meta.span,
+                                              AttrError::UnknownMetaItem(meta.name()));
                                 continue 'outer
                             }
                         }
@@ -495,18 +511,18 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                             })
                         }
                         (None, _) => {
-                            diagnostic.span_err(attr.span(), "missing 'since'");
+                            handle_errors(diagnostic, attr.span(), AttrError::MissingSince);
                             continue
                         }
                         _ => {
-                            diagnostic.span_err(attr.span(), "missing 'reason'");
+                            span_err!(diagnostic, attr.span(), E0543, "missing 'reason'");
                             continue
                         }
                     }
                 }
                 "unstable" => {
                     if stab.is_some() {
-                        diagnostic.span_err(item_sp, "multiple stability levels");
+                        handle_errors(diagnostic, attr.span(), AttrError::MultipleStabilityLevels);
                         break
                     }
 
@@ -519,8 +535,8 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                             "reason" => if !get(meta, &mut reason) { continue 'outer },
                             "issue" => if !get(meta, &mut issue) { continue 'outer },
                             _ => {
-                                diagnostic.span_err(meta.span, &format!("unknown meta item '{}'",
-                                                                        meta.name()));
+                                handle_errors(diagnostic, meta.span,
+                                              AttrError::UnknownMetaItem(meta.name()));
                                 continue 'outer
                             }
                         }
@@ -535,7 +551,8 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                                         if let Ok(issue) = issue.parse() {
                                             issue
                                         } else {
-                                            diagnostic.span_err(attr.span(), "incorrect 'issue'");
+                                            span_err!(diagnostic, attr.span(), E0545,
+                                                      "incorrect 'issue'");
                                             continue
                                         }
                                     }
@@ -545,18 +562,18 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                             })
                         }
                         (None, _, _) => {
-                            diagnostic.span_err(attr.span(), "missing 'feature'");
+                            handle_errors(diagnostic, attr.span(), AttrError::MissingFeature);
                             continue
                         }
                         _ => {
-                            diagnostic.span_err(attr.span(), "missing 'issue'");
+                            span_err!(diagnostic, attr.span(), E0547, "missing 'issue'");
                             continue
                         }
                     }
                 }
                 "stable" => {
                     if stab.is_some() {
-                        diagnostic.span_err(item_sp, "multiple stability levels");
+                        handle_errors(diagnostic, attr.span(), AttrError::MultipleStabilityLevels);
                         break
                     }
 
@@ -567,8 +584,8 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                             "feature" => if !get(meta, &mut feature) { continue 'outer },
                             "since" => if !get(meta, &mut since) { continue 'outer },
                             _ => {
-                                diagnostic.span_err(meta.span, &format!("unknown meta item '{}'",
-                                                                        meta.name()));
+                                handle_errors(diagnostic, meta.span,
+                                              AttrError::UnknownMetaItem(meta.name()));
                                 continue 'outer
                             }
                         }
@@ -585,11 +602,11 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                             })
                         }
                         (None, _) => {
-                            diagnostic.span_err(attr.span(), "missing 'feature'");
+                            handle_errors(diagnostic, attr.span(), AttrError::MissingFeature);
                             continue
                         }
                         _ => {
-                            diagnostic.span_err(attr.span(), "missing 'since'");
+                            handle_errors(diagnostic, attr.span(), AttrError::MissingSince);
                             continue
                         }
                     }
@@ -597,7 +614,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
                 _ => unreachable!()
             }
         } else {
-            diagnostic.span_err(attr.span(), "incorrect stability attribute type");
+            span_err!(diagnostic, attr.span(), E0548, "incorrect stability attribute type");
             continue
         }
     }
@@ -610,8 +627,9 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
             }
             stab.rustc_depr = Some(rustc_depr);
         } else {
-            diagnostic.span_err(item_sp, "rustc_deprecated attribute must be paired with \
-                                          either stable or unstable attribute");
+            span_err!(diagnostic, item_sp, E0549,
+                      "rustc_deprecated attribute must be paired with \
+                       either stable or unstable attribute");
         }
     }
 
@@ -634,22 +652,21 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
         mark_used(attr);
 
         if depr.is_some() {
-            diagnostic.span_err(item_sp, "multiple deprecated attributes");
+            span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes");
             break
         }
 
         depr = if let Some(metas) = attr.meta_item_list() {
             let get = |meta: &MetaItem, item: &mut Option<InternedString>| {
                 if item.is_some() {
-                    diagnostic.span_err(meta.span, &format!("multiple '{}' items",
-                                                             meta.name()));
+                    handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name()));
                     return false
                 }
                 if let Some(v) = meta.value_str() {
                     *item = Some(v);
                     true
                 } else {
-                    diagnostic.span_err(meta.span, "incorrect meta item");
+                    span_err!(diagnostic, meta.span, E0551, "incorrect meta item");
                     false
                 }
             };
@@ -661,8 +678,8 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
                     "since" => if !get(meta, &mut since) { continue 'outer },
                     "note" => if !get(meta, &mut note) { continue 'outer },
                     _ => {
-                        diagnostic.span_err(meta.span, &format!("unknown meta item '{}'",
-                                                                meta.name()));
+                        handle_errors(diagnostic, meta.span,
+                                      AttrError::UnknownMetaItem(meta.name()));
                         continue 'outer
                     }
                 }
@@ -696,7 +713,7 @@ pub fn require_unique_names(diagnostic: &Handler, metas: &[P<MetaItem>]) {
 
         if !set.insert(name.clone()) {
             panic!(diagnostic.span_fatal(meta.span,
-                                  &format!("duplicate meta item `{}`", name)));
+                                         &format!("duplicate meta item `{}`", name)));
         }
     }
 }
@@ -725,8 +742,8 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
                                 Some(ity) => Some(ReprInt(item.span, ity)),
                                 None => {
                                     // Not a word we recognize
-                                    diagnostic.span_err(item.span,
-                                                        "unrecognized representation hint");
+                                    span_err!(diagnostic, item.span, E0552,
+                                              "unrecognized representation hint");
                                     None
                                 }
                             }
@@ -738,7 +755,8 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
                         }
                     }
                     // Not a word:
-                    _ => diagnostic.span_err(item.span, "unrecognized enum representation hint")
+                    _ => span_err!(diagnostic, item.span, E0553,
+                                   "unrecognized enum representation hint"),
                 }
             }
         }
@@ -810,156 +828,84 @@ impl IntType {
     }
 }
 
-/// A list of attributes, behind a optional box as
-/// a space optimization.
-pub type ThinAttributes = Option<Box<Vec<Attribute>>>;
-
-pub trait ThinAttributesExt {
-    fn map_thin_attrs<F>(self, f: F) -> Self
-        where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>;
-    fn prepend(mut self, attrs: Self) -> Self;
-    fn append(mut self, attrs: Self) -> Self;
-    fn update<F>(&mut self, f: F)
-        where Self: Sized,
-              F: FnOnce(Self) -> Self;
-    fn as_attr_slice(&self) -> &[Attribute];
-    fn into_attr_vec(self) -> Vec<Attribute>;
+pub trait HasAttrs: Sized {
+    fn attrs(&self) -> &[ast::Attribute];
+    fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self;
 }
 
-impl ThinAttributesExt for ThinAttributes {
-    fn map_thin_attrs<F>(self, f: F) -> Self
-        where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>
-    {
-        f(self.map(|b| *b).unwrap_or(Vec::new())).into_thin_attrs()
+impl HasAttrs for Vec<Attribute> {
+    fn attrs(&self) -> &[Attribute] {
+        &self
     }
-
-    fn prepend(self, attrs: ThinAttributes) -> Self {
-        attrs.map_thin_attrs(|mut attrs| {
-            attrs.extend(self.into_attr_vec());
-            attrs
-        })
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        f(self)
     }
+}
 
-    fn append(self, attrs: ThinAttributes) -> Self {
-        self.map_thin_attrs(|mut self_| {
-            self_.extend(attrs.into_attr_vec());
-            self_
-        })
+impl HasAttrs for ThinVec<Attribute> {
+    fn attrs(&self) -> &[Attribute] {
+        &self
     }
-
-    fn update<F>(&mut self, f: F)
-        where Self: Sized,
-              F: FnOnce(ThinAttributes) -> ThinAttributes
-    {
-        let self_ = f(self.take());
-        *self = self_;
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        f(self.into()).into()
     }
+}
 
-    fn as_attr_slice(&self) -> &[Attribute] {
-        match *self {
-            Some(ref b) => b,
-            None => &[],
-        }
+impl<T: HasAttrs + 'static> HasAttrs for P<T> {
+    fn attrs(&self) -> &[Attribute] {
+        (**self).attrs()
     }
-
-    fn into_attr_vec(self) -> Vec<Attribute> {
-        match self {
-            Some(b) => *b,
-            None => Vec::new(),
-        }
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        self.map(|t| t.map_attrs(f))
     }
 }
 
-pub trait AttributesExt {
-    fn into_thin_attrs(self) -> ThinAttributes;
-}
-
-impl AttributesExt for Vec<Attribute> {
-    fn into_thin_attrs(self) -> ThinAttributes {
-        if self.len() == 0 {
-            None
-        } else {
-            Some(Box::new(self))
+impl HasAttrs for StmtKind {
+    fn attrs(&self) -> &[Attribute] {
+        match *self {
+            StmtKind::Local(ref local) => local.attrs(),
+            StmtKind::Item(..) => &[],
+            StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
+            StmtKind::Mac(ref mac) => {
+                let (_, _, ref attrs) = **mac;
+                attrs.attrs()
+            }
         }
     }
-}
-
-/// A cheap way to add Attributes to an AST node.
-pub trait WithAttrs {
-    // FIXME: Could be extended to anything IntoIter<Item=Attribute>
-    fn with_attrs(self, attrs: ThinAttributes) -> Self;
-}
 
-impl WithAttrs for P<Expr> {
-    fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|mut e| {
-            e.attrs.update(|a| a.append(attrs));
-            e
-        })
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        match self {
+            StmtKind::Local(local) => StmtKind::Local(local.map_attrs(f)),
+            StmtKind::Item(..) => self,
+            StmtKind::Expr(expr) => StmtKind::Expr(expr.map_attrs(f)),
+            StmtKind::Semi(expr) => StmtKind::Semi(expr.map_attrs(f)),
+            StmtKind::Mac(mac) => StmtKind::Mac(mac.map(|(mac, style, attrs)| {
+                (mac, style, attrs.map_attrs(f))
+            })),
+        }
     }
 }
 
-impl WithAttrs for P<Item> {
-    fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|Item { ident, attrs: mut ats, id, node, vis, span }| {
-            ats.extend(attrs.into_attr_vec());
-            Item {
-                ident: ident,
-                attrs: ats,
-                id: id,
-                node: node,
-                vis: vis,
-                span: span,
+macro_rules! derive_has_attrs_from_field {
+    ($($ty:path),*) => { derive_has_attrs_from_field!($($ty: .attrs),*); };
+    ($($ty:path : $(.$field:ident)*),*) => { $(
+        impl HasAttrs for $ty {
+            fn attrs(&self) -> &[Attribute] {
+                self $(.$field)* .attrs()
             }
-        })
-    }
-}
 
-impl WithAttrs for P<Local> {
-    fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|Local { pat, ty, init, id, span, attrs: mut ats }| {
-            ats.update(|a| a.append(attrs));
-            Local {
-                pat: pat,
-                ty: ty,
-                init: init,
-                id: id,
-                span: span,
-                attrs: ats,
+            fn map_attrs<F>(mut self, f: F) -> Self
+                where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>,
+            {
+                self $(.$field)* = self $(.$field)* .map_attrs(f);
+                self
             }
-        })
-    }
+        }
+    )* }
 }
 
-impl WithAttrs for P<Decl> {
-    fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|Spanned { span, node }| {
-            Spanned {
-                span: span,
-                node: match node {
-                    DeclKind::Local(local) => DeclKind::Local(local.with_attrs(attrs)),
-                    DeclKind::Item(item) => DeclKind::Item(item.with_attrs(attrs)),
-                }
-            }
-        })
-    }
+derive_has_attrs_from_field! {
+    Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm
 }
 
-impl WithAttrs for P<Stmt> {
-    fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|Spanned { span, node }| {
-            Spanned {
-                span: span,
-                node: match node {
-                    StmtKind::Decl(decl, id) => StmtKind::Decl(decl.with_attrs(attrs), id),
-                    StmtKind::Expr(expr, id) => StmtKind::Expr(expr.with_attrs(attrs), id),
-                    StmtKind::Semi(expr, id) => StmtKind::Semi(expr.with_attrs(attrs), id),
-                    StmtKind::Mac(mac, style, mut ats) => {
-                        ats.update(|a| a.append(attrs));
-                        StmtKind::Mac(mac, style, ats)
-                    }
-                },
-            }
-        })
-    }
-}
+derive_has_attrs_from_field! { Stmt: .node, ast::Variant: .node.attrs }
index ca8708fdc8326c480761a8d3d35daffc791e0052..743f96d737e2da8fe3a179befb42eacb40d3c0ae 100644 (file)
 
 pub use self::ExpnFormat::*;
 
-use std::cell::{Cell, RefCell};
-use std::ops::{Add, Sub};
-use std::path::Path;
+use std::cell::RefCell;
+use std::path::{Path,PathBuf};
 use std::rc::Rc;
-use std::cmp;
 
-use std::{fmt, fs};
+use std::env;
+use std::fs;
 use std::io::{self, Read};
-
-use serialize::{Encodable, Decodable, Encoder, Decoder};
+pub use syntax_pos::*;
+use errors::CodeMapper;
 
 use ast::Name;
 
-// _____________________________________________________________________________
-// Pos, BytePos, CharPos
-//
-
-pub trait Pos {
-    fn from_usize(n: usize) -> Self;
-    fn to_usize(&self) -> usize;
-}
-
-/// A byte offset. Keep this small (currently 32-bits), as AST contains
-/// a lot of them.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
-pub struct BytePos(pub u32);
-
-/// A character offset. Because of multibyte utf8 characters, a byte offset
-/// is not equivalent to a character offset. The CodeMap will convert BytePos
-/// values to CharPos values as necessary.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
-pub struct CharPos(pub usize);
-
-// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
-// have been unsuccessful
-
-impl Pos for BytePos {
-    fn from_usize(n: usize) -> BytePos { BytePos(n as u32) }
-    fn to_usize(&self) -> usize { let BytePos(n) = *self; n as usize }
-}
-
-impl Add for BytePos {
-    type Output = BytePos;
-
-    fn add(self, rhs: BytePos) -> BytePos {
-        BytePos((self.to_usize() + rhs.to_usize()) as u32)
-    }
-}
-
-impl Sub for BytePos {
-    type Output = BytePos;
-
-    fn sub(self, rhs: BytePos) -> BytePos {
-        BytePos((self.to_usize() - rhs.to_usize()) as u32)
-    }
-}
-
-impl Encodable for BytePos {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        s.emit_u32(self.0)
-    }
-}
-
-impl Decodable for BytePos {
-    fn decode<D: Decoder>(d: &mut D) -> Result<BytePos, D::Error> {
-        Ok(BytePos(d.read_u32()?))
-    }
-}
-
-impl Pos for CharPos {
-    fn from_usize(n: usize) -> CharPos { CharPos(n) }
-    fn to_usize(&self) -> usize { let CharPos(n) = *self; n }
-}
-
-impl Add for CharPos {
-    type Output = CharPos;
-
-    fn add(self, rhs: CharPos) -> CharPos {
-        CharPos(self.to_usize() + rhs.to_usize())
-    }
-}
-
-impl Sub for CharPos {
-    type Output = CharPos;
-
-    fn sub(self, rhs: CharPos) -> CharPos {
-        CharPos(self.to_usize() - rhs.to_usize())
-    }
-}
-
-// _____________________________________________________________________________
-// Span, MultiSpan, Spanned
-//
-
-/// Spans represent a region of code, used for error reporting. Positions in spans
-/// are *absolute* positions from the beginning of the codemap, not positions
-/// relative to FileMaps. Methods on the CodeMap can be used to relate spans back
-/// to the original source.
-/// You must be careful if the span crosses more than one file - you will not be
-/// able to use many of the functions on spans in codemap and you cannot assume
-/// that the length of the span = hi - lo; there may be space in the BytePos
-/// range between files.
-#[derive(Clone, Copy, Hash, PartialEq, Eq)]
-pub struct Span {
-    pub lo: BytePos,
-    pub hi: BytePos,
-    /// Information about where the macro came from, if this piece of
-    /// code was created by a macro expansion.
-    pub expn_id: ExpnId
-}
-
-/// A collection of spans. Spans have two orthogonal attributes:
-///
-/// - they can be *primary spans*. In this case they are the locus of
-///   the error, and would be rendered with `^^^`.
-/// - they can have a *label*. In this case, the label is written next
-///   to the mark in the snippet when we render.
-#[derive(Clone)]
-pub struct MultiSpan {
-    primary_spans: Vec<Span>,
-    span_labels: Vec<(Span, String)>,
-}
-
-#[derive(Clone, Debug)]
-pub struct SpanLabel {
-    /// The span we are going to include in the final snippet.
-    pub span: Span,
-
-    /// Is this a primary span? This is the "locus" of the message,
-    /// and is indicated with a `^^^^` underline, versus `----`.
-    pub is_primary: bool,
-
-    /// What label should we attach to this span (if any)?
-    pub label: Option<String>,
-}
-
-pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION };
-
-// Generic span to be used for code originating from the command line
-pub const COMMAND_LINE_SP: Span = Span { lo: BytePos(0),
-                                         hi: BytePos(0),
-                                         expn_id: COMMAND_LINE_EXPN };
-
-impl Span {
-    /// Returns a new span representing just the end-point of this span
-    pub fn end_point(self) -> Span {
-        let lo = cmp::max(self.hi.0 - 1, self.lo.0);
-        Span { lo: BytePos(lo), hi: self.hi, expn_id: self.expn_id}
-    }
-
-    /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
-    pub fn substitute_dummy(self, other: Span) -> Span {
-        if self.source_equal(&DUMMY_SP) { other } else { self }
-    }
-
-    pub fn contains(self, other: Span) -> bool {
-        self.lo <= other.lo && other.hi <= self.hi
-    }
-
-    /// Return true if the spans are equal with regards to the source text.
-    ///
-    /// Use this instead of `==` when either span could be generated code,
-    /// and you only care that they point to the same bytes of source text.
-    pub fn source_equal(&self, other: &Span) -> bool {
-        self.lo == other.lo && self.hi == other.hi
-    }
-
-    /// Returns `Some(span)`, a union of `self` and `other`, on overlap.
-    pub fn merge(self, other: Span) -> Option<Span> {
-        if self.expn_id != other.expn_id {
-            return None;
-        }
-
-        if (self.lo <= other.lo && self.hi > other.lo) ||
-           (self.lo >= other.lo && self.lo < other.hi) {
-            Some(Span {
-                lo: cmp::min(self.lo, other.lo),
-                hi: cmp::max(self.hi, other.hi),
-                expn_id: self.expn_id,
-            })
-        } else {
-            None
-        }
-    }
-
-    /// Returns `Some(span)`, where the start is trimmed by the end of `other`
-    pub fn trim_start(self, other: Span) -> Option<Span> {
-        if self.hi > other.hi {
-            Some(Span { lo: cmp::max(self.lo, other.hi), .. self })
-        } else {
-            None
-        }
-    }
-}
-
-#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
-pub struct Spanned<T> {
-    pub node: T,
-    pub span: Span,
-}
-
-impl Encodable for Span {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        s.emit_struct("Span", 2, |s| {
-            s.emit_struct_field("lo", 0, |s| {
-                self.lo.encode(s)
-            })?;
-
-            s.emit_struct_field("hi", 1, |s| {
-                self.hi.encode(s)
-            })
-        })
-    }
-}
-
-impl Decodable for Span {
-    fn decode<D: Decoder>(d: &mut D) -> Result<Span, D::Error> {
-        d.read_struct("Span", 2, |d| {
-            let lo = d.read_struct_field("lo", 0, |d| {
-                BytePos::decode(d)
-            })?;
-
-            let hi = d.read_struct_field("hi", 1, |d| {
-                BytePos::decode(d)
-            })?;
-
-            Ok(mk_sp(lo, hi))
-        })
-    }
-}
-
-fn default_span_debug(span: Span, f: &mut fmt::Formatter) -> fmt::Result {
-    write!(f, "Span {{ lo: {:?}, hi: {:?}, expn_id: {:?} }}",
-           span.lo, span.hi, span.expn_id)
-}
-
-thread_local!(pub static SPAN_DEBUG: Cell<fn(Span, &mut fmt::Formatter) -> fmt::Result> =
-                Cell::new(default_span_debug));
-
-impl fmt::Debug for Span {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        SPAN_DEBUG.with(|span_debug| span_debug.get()(*self, f))
-    }
-}
-
-pub fn spanned<T>(lo: BytePos, hi: BytePos, t: T) -> Spanned<T> {
-    respan(mk_sp(lo, hi), t)
-}
-
-pub fn respan<T>(sp: Span, t: T) -> Spanned<T> {
-    Spanned {node: t, span: sp}
-}
-
-pub fn dummy_spanned<T>(t: T) -> Spanned<T> {
-    respan(DUMMY_SP, t)
-}
-
-/* assuming that we're not in macro expansion */
-pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
-    Span {lo: lo, hi: hi, expn_id: NO_EXPANSION}
-}
-
 /// Return the span itself if it doesn't come from a macro expansion,
 /// otherwise return the call site span up to the `enclosing_sp` by
 /// following the `expn_info` chain.
@@ -294,123 +44,31 @@ pub fn original_sp(cm: &CodeMap, sp: Span, enclosing_sp: Span) -> Span {
     }
 }
 
-impl MultiSpan {
-    pub fn new() -> MultiSpan {
-        MultiSpan {
-            primary_spans: vec![],
-            span_labels: vec![]
-        }
-    }
-
-    pub fn from_span(primary_span: Span) -> MultiSpan {
-        MultiSpan {
-            primary_spans: vec![primary_span],
-            span_labels: vec![]
-        }
-    }
-
-    pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
-        MultiSpan {
-            primary_spans: vec,
-            span_labels: vec![]
-        }
-    }
-
-    pub fn push_span_label(&mut self, span: Span, label: String) {
-        self.span_labels.push((span, label));
-    }
-
-    /// Selects the first primary span (if any)
-    pub fn primary_span(&self) -> Option<Span> {
-        self.primary_spans.first().cloned()
-    }
-
-    /// Returns all primary spans.
-    pub fn primary_spans(&self) -> &[Span] {
-        &self.primary_spans
-    }
-
-    /// Returns the strings to highlight. We always ensure that there
-    /// is an entry for each of the primary spans -- for each primary
-    /// span P, if there is at least one label with span P, we return
-    /// those labels (marked as primary). But otherwise we return
-    /// `SpanLabel` instances with empty labels.
-    pub fn span_labels(&self) -> Vec<SpanLabel> {
-        let is_primary = |span| self.primary_spans.contains(&span);
-        let mut span_labels = vec![];
-
-        for &(span, ref label) in &self.span_labels {
-            span_labels.push(SpanLabel {
-                span: span,
-                is_primary: is_primary(span),
-                label: Some(label.clone())
-            });
-        }
-
-        for &span in &self.primary_spans {
-            if !span_labels.iter().any(|sl| sl.span == span) {
-                span_labels.push(SpanLabel {
-                    span: span,
-                    is_primary: true,
-                    label: None
-                });
-            }
-        }
-
-        span_labels
-    }
+/// The source of expansion.
+#[derive(Clone, Hash, Debug, PartialEq, Eq)]
+pub enum ExpnFormat {
+    /// e.g. #[derive(...)] <item>
+    MacroAttribute(Name),
+    /// e.g. `format!()`
+    MacroBang(Name),
 }
 
-impl From<Span> for MultiSpan {
-    fn from(span: Span) -> MultiSpan {
-        MultiSpan::from_span(span)
-    }
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
+pub struct Spanned<T> {
+    pub node: T,
+    pub span: Span,
 }
 
-// _____________________________________________________________________________
-// Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
-//
-
-/// A source code location used for error reporting
-#[derive(Debug)]
-pub struct Loc {
-    /// Information about the original source
-    pub file: Rc<FileMap>,
-    /// The (1-based) line number
-    pub line: usize,
-    /// The (0-based) column offset
-    pub col: CharPos
+pub fn spanned<T>(lo: BytePos, hi: BytePos, t: T) -> Spanned<T> {
+    respan(mk_sp(lo, hi), t)
 }
 
-/// A source code location used as the result of lookup_char_pos_adj
-// Actually, *none* of the clients use the filename *or* file field;
-// perhaps they should just be removed.
-#[derive(Debug)]
-pub struct LocWithOpt {
-    pub filename: FileName,
-    pub line: usize,
-    pub col: CharPos,
-    pub file: Option<Rc<FileMap>>,
+pub fn respan<T>(sp: Span, t: T) -> Spanned<T> {
+    Spanned {node: t, span: sp}
 }
 
-// used to be structural records. Better names, anyone?
-#[derive(Debug)]
-pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize }
-#[derive(Debug)]
-pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
-
-
-// _____________________________________________________________________________
-// ExpnFormat, NameAndSpan, ExpnInfo, ExpnId
-//
-
-/// The source of expansion.
-#[derive(Clone, Hash, Debug, PartialEq, Eq)]
-pub enum ExpnFormat {
-    /// e.g. #[derive(...)] <item>
-    MacroAttribute(Name),
-    /// e.g. `format!()`
-    MacroBang(Name),
+pub fn dummy_spanned<T>(t: T) -> Spanned<T> {
+    respan(DUMMY_SP, t)
 }
 
 #[derive(Clone, Hash, Debug)]
@@ -453,256 +111,18 @@ pub struct ExpnInfo {
     pub callee: NameAndSpan
 }
 
-#[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy)]
-pub struct ExpnId(u32);
-
-pub const NO_EXPANSION: ExpnId = ExpnId(!0);
-// For code appearing from the command line
-pub const COMMAND_LINE_EXPN: ExpnId = ExpnId(!1);
-
-impl ExpnId {
-    pub fn from_u32(id: u32) -> ExpnId {
-        ExpnId(id)
-    }
-
-    pub fn into_u32(self) -> u32 {
-        self.0
-    }
-}
-
 // _____________________________________________________________________________
 // FileMap, MultiByteChar, FileName, FileLines
 //
 
-pub type FileName = String;
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct LineInfo {
-    /// Index of line, starting from 0.
-    pub line_index: usize,
-
-    /// Column in line where span begins, starting from 0.
-    pub start_col: CharPos,
-
-    /// Column in line where span ends, starting from 0, exclusive.
-    pub end_col: CharPos,
-}
-
-pub struct FileLines {
-    pub file: Rc<FileMap>,
-    pub lines: Vec<LineInfo>
-}
-
-/// Identifies an offset of a multi-byte character in a FileMap
-#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)]
-pub struct MultiByteChar {
-    /// The absolute offset of the character in the CodeMap
-    pub pos: BytePos,
-    /// The number of bytes, >=2
-    pub bytes: usize,
-}
-
-/// A single source in the CodeMap.
-pub struct FileMap {
-    /// The name of the file that the source came from, source that doesn't
-    /// originate from files has names between angle brackets by convention,
-    /// e.g. `<anon>`
-    pub name: FileName,
-    /// The complete source code
-    pub src: Option<Rc<String>>,
-    /// The start position of this source in the CodeMap
-    pub start_pos: BytePos,
-    /// The end position of this source in the CodeMap
-    pub end_pos: BytePos,
-    /// Locations of lines beginnings in the source code
-    pub lines: RefCell<Vec<BytePos>>,
-    /// Locations of multi-byte characters in the source code
-    pub multibyte_chars: RefCell<Vec<MultiByteChar>>,
-}
-
-impl Encodable for FileMap {
-    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-        s.emit_struct("FileMap", 5, |s| {
-            s.emit_struct_field("name", 0, |s| self.name.encode(s))?;
-            s.emit_struct_field("start_pos", 1, |s| self.start_pos.encode(s))?;
-            s.emit_struct_field("end_pos", 2, |s| self.end_pos.encode(s))?;
-            s.emit_struct_field("lines", 3, |s| {
-                let lines = self.lines.borrow();
-                // store the length
-                s.emit_u32(lines.len() as u32)?;
-
-                if !lines.is_empty() {
-                    // In order to preserve some space, we exploit the fact that
-                    // the lines list is sorted and individual lines are
-                    // probably not that long. Because of that we can store lines
-                    // as a difference list, using as little space as possible
-                    // for the differences.
-                    let max_line_length = if lines.len() == 1 {
-                        0
-                    } else {
-                        lines.windows(2)
-                             .map(|w| w[1] - w[0])
-                             .map(|bp| bp.to_usize())
-                             .max()
-                             .unwrap()
-                    };
-
-                    let bytes_per_diff: u8 = match max_line_length {
-                        0 ... 0xFF => 1,
-                        0x100 ... 0xFFFF => 2,
-                        _ => 4
-                    };
-
-                    // Encode the number of bytes used per diff.
-                    bytes_per_diff.encode(s)?;
-
-                    // Encode the first element.
-                    lines[0].encode(s)?;
-
-                    let diff_iter = (&lines[..]).windows(2)
-                                                .map(|w| (w[1] - w[0]));
-
-                    match bytes_per_diff {
-                        1 => for diff in diff_iter { (diff.0 as u8).encode(s)? },
-                        2 => for diff in diff_iter { (diff.0 as u16).encode(s)? },
-                        4 => for diff in diff_iter { diff.0.encode(s)? },
-                        _ => unreachable!()
-                    }
-                }
-
-                Ok(())
-            })?;
-            s.emit_struct_field("multibyte_chars", 4, |s| {
-                (*self.multibyte_chars.borrow()).encode(s)
-            })
-        })
-    }
-}
-
-impl Decodable for FileMap {
-    fn decode<D: Decoder>(d: &mut D) -> Result<FileMap, D::Error> {
-
-        d.read_struct("FileMap", 5, |d| {
-            let name: String = d.read_struct_field("name", 0, |d| Decodable::decode(d))?;
-            let start_pos: BytePos = d.read_struct_field("start_pos", 1, |d| Decodable::decode(d))?;
-            let end_pos: BytePos = d.read_struct_field("end_pos", 2, |d| Decodable::decode(d))?;
-            let lines: Vec<BytePos> = d.read_struct_field("lines", 3, |d| {
-                let num_lines: u32 = Decodable::decode(d)?;
-                let mut lines = Vec::with_capacity(num_lines as usize);
-
-                if num_lines > 0 {
-                    // Read the number of bytes used per diff.
-                    let bytes_per_diff: u8 = Decodable::decode(d)?;
-
-                    // Read the first element.
-                    let mut line_start: BytePos = Decodable::decode(d)?;
-                    lines.push(line_start);
-
-                    for _ in 1..num_lines {
-                        let diff = match bytes_per_diff {
-                            1 => d.read_u8()? as u32,
-                            2 => d.read_u16()? as u32,
-                            4 => d.read_u32()?,
-                            _ => unreachable!()
-                        };
-
-                        line_start = line_start + BytePos(diff);
-
-                        lines.push(line_start);
-                    }
-                }
-
-                Ok(lines)
-            })?;
-            let multibyte_chars: Vec<MultiByteChar> =
-                d.read_struct_field("multibyte_chars", 4, |d| Decodable::decode(d))?;
-            Ok(FileMap {
-                name: name,
-                start_pos: start_pos,
-                end_pos: end_pos,
-                src: None,
-                lines: RefCell::new(lines),
-                multibyte_chars: RefCell::new(multibyte_chars)
-            })
-        })
-    }
-}
-
-impl fmt::Debug for FileMap {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write!(fmt, "FileMap({})", self.name)
-    }
-}
-
-impl FileMap {
-    /// EFFECT: register a start-of-line offset in the
-    /// table of line-beginnings.
-    /// UNCHECKED INVARIANT: these offsets must be added in the right
-    /// order and must be in the right places; there is shared knowledge
-    /// about what ends a line between this file and parse.rs
-    /// WARNING: pos param here is the offset relative to start of CodeMap,
-    /// and CodeMap will append a newline when adding a filemap without a newline at the end,
-    /// so the safe way to call this is with value calculated as
-    /// filemap.start_pos + newline_offset_relative_to_the_start_of_filemap.
-    pub fn next_line(&self, pos: BytePos) {
-        // the new charpos must be > the last one (or it's the first one).
-        let mut lines = self.lines.borrow_mut();
-        let line_len = lines.len();
-        assert!(line_len == 0 || ((*lines)[line_len - 1] < pos));
-        lines.push(pos);
-    }
-
-    /// get a line from the list of pre-computed line-beginnings.
-    /// line-number here is 0-based.
-    pub fn get_line(&self, line_number: usize) -> Option<&str> {
-        match self.src {
-            Some(ref src) => {
-                let lines = self.lines.borrow();
-                lines.get(line_number).map(|&line| {
-                    let begin: BytePos = line - self.start_pos;
-                    let begin = begin.to_usize();
-                    // We can't use `lines.get(line_number+1)` because we might
-                    // be parsing when we call this function and thus the current
-                    // line is the last one we have line info for.
-                    let slice = &src[begin..];
-                    match slice.find('\n') {
-                        Some(e) => &slice[..e],
-                        None => slice
-                    }
-                })
-            }
-            None => None
-        }
-    }
-
-    pub fn record_multibyte_char(&self, pos: BytePos, bytes: usize) {
-        assert!(bytes >=2 && bytes <= 4);
-        let mbc = MultiByteChar {
-            pos: pos,
-            bytes: bytes,
-        };
-        self.multibyte_chars.borrow_mut().push(mbc);
-    }
-
-    pub fn is_real_file(&self) -> bool {
-        !(self.name.starts_with("<") &&
-          self.name.ends_with(">"))
-    }
-
-    pub fn is_imported(&self) -> bool {
-        self.src.is_none()
-    }
-
-    fn count_lines(&self) -> usize {
-        self.lines.borrow().len()
-    }
-}
-
 /// An abstraction over the fs operations used by the Parser.
 pub trait FileLoader {
     /// Query the existence of a file.
     fn file_exists(&self, path: &Path) -> bool;
 
+    /// Return an absolute path to a file, if possible.
+    fn abs_path(&self, path: &Path) -> Option<PathBuf>;
+
     /// Read the contents of an UTF-8 file into memory.
     fn read_file(&self, path: &Path) -> io::Result<String>;
 }
@@ -715,6 +135,16 @@ impl FileLoader for RealFileLoader {
         fs::metadata(path).is_ok()
     }
 
+    fn abs_path(&self, path: &Path) -> Option<PathBuf> {
+        if path.is_absolute() {
+            Some(path.to_path_buf())
+        } else {
+            env::current_dir()
+                .ok()
+                .map(|cwd| cwd.join(path))
+        }
+    }
+
     fn read_file(&self, path: &Path) -> io::Result<String> {
         let mut src = String::new();
         fs::File::open(path)?.read_to_string(&mut src)?;
@@ -755,7 +185,8 @@ impl CodeMap {
 
     pub fn load_file(&self, path: &Path) -> io::Result<Rc<FileMap>> {
         let src = self.file_loader.read_file(path)?;
-        Ok(self.new_filemap(path.to_str().unwrap().to_string(), src))
+        let abs_path = self.file_loader.abs_path(path).map(|p| p.to_str().unwrap().to_string());
+        Ok(self.new_filemap(path.to_str().unwrap().to_string(), abs_path, src))
     }
 
     fn next_start_pos(&self) -> usize {
@@ -770,7 +201,8 @@ impl CodeMap {
 
     /// Creates a new filemap without setting its line information. If you don't
     /// intend to set the line information yourself, you should use new_filemap_and_lines.
-    pub fn new_filemap(&self, filename: FileName, mut src: String) -> Rc<FileMap> {
+    pub fn new_filemap(&self, filename: FileName, abs_path: Option<FileName>,
+                       mut src: String) -> Rc<FileMap> {
         let start_pos = self.next_start_pos();
         let mut files = self.files.borrow_mut();
 
@@ -783,6 +215,7 @@ impl CodeMap {
 
         let filemap = Rc::new(FileMap {
             name: filename,
+            abs_path: abs_path,
             src: Some(Rc::new(src)),
             start_pos: Pos::from_usize(start_pos),
             end_pos: Pos::from_usize(end_pos),
@@ -796,8 +229,11 @@ impl CodeMap {
     }
 
     /// Creates a new filemap and sets its line information.
-    pub fn new_filemap_and_lines(&self, filename: &str, src: &str) -> Rc<FileMap> {
-        let fm = self.new_filemap(filename.to_string(), src.to_owned());
+    pub fn new_filemap_and_lines(&self, filename: &str, abs_path: Option<&str>,
+                                 src: &str) -> Rc<FileMap> {
+        let fm = self.new_filemap(filename.to_string(),
+                                  abs_path.map(|s| s.to_owned()),
+                                  src.to_owned());
         let mut byte_pos: u32 = fm.start_pos.0;
         for line in src.lines() {
             // register the start of this line
@@ -816,6 +252,7 @@ impl CodeMap {
     /// information for things inlined from other crates.
     pub fn new_imported_filemap(&self,
                                 filename: FileName,
+                                abs_path: Option<FileName>,
                                 source_len: usize,
                                 mut file_local_lines: Vec<BytePos>,
                                 mut file_local_multibyte_chars: Vec<MultiByteChar>)
@@ -836,6 +273,7 @@ impl CodeMap {
 
         let filemap = Rc::new(FileMap {
             name: filename,
+            abs_path: abs_path,
             src: None,
             start_pos: start_pos,
             end_pos: end_pos,
@@ -1191,13 +629,13 @@ impl CodeMap {
         }
     }
 
-    pub fn get_filemap(&self, filename: &str) -> Rc<FileMap> {
+    pub fn get_filemap(&self, filename: &str) -> Option<Rc<FileMap>> {
         for fm in self.files.borrow().iter() {
             if filename == fm.name {
-                return fm.clone();
+                return Some(fm.clone());
             }
         }
-        panic!("asking for {} which we don't know about", filename);
+        None
     }
 
     /// For a global BytePos compute the local offset within the containing FileMap
@@ -1258,31 +696,6 @@ impl CodeMap {
         return a;
     }
 
-    /// Check if the backtrace `subtrace` contains `suptrace` as a prefix.
-    pub fn more_specific_trace(&self,
-                              mut subtrace: ExpnId,
-                              suptrace: ExpnId)
-                              -> bool {
-        loop {
-            if subtrace == suptrace {
-                return true;
-            }
-
-            let stop = self.with_expn_info(subtrace, |opt_expn_info| {
-                if let Some(expn_info) = opt_expn_info {
-                    subtrace = expn_info.call_site.expn_id;
-                    false
-                } else {
-                    true
-                }
-            });
-
-            if stop {
-                return false;
-            }
-        }
-    }
-
     pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId {
         let mut expansions = self.expansions.borrow_mut();
         expansions.push(expn_info);
@@ -1389,52 +802,24 @@ impl CodeMap {
     }
 }
 
-pub struct MacroBacktrace {
-    /// span where macro was applied to generate this code
-    pub call_site: Span,
-
-    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
-    pub macro_decl_name: String,
-
-    /// span where macro was defined (if known)
-    pub def_site_span: Option<Span>,
-}
-
-// _____________________________________________________________________________
-// SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions
-//
-
-pub type FileLinesResult = Result<FileLines, SpanLinesError>;
-
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub enum SpanLinesError {
-    IllFormedSpan(Span),
-    DistinctSources(DistinctSources),
-}
-
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub enum SpanSnippetError {
-    IllFormedSpan(Span),
-    DistinctSources(DistinctSources),
-    MalformedForCodemap(MalformedCodemapPositions),
-    SourceNotAvailable { filename: String }
-}
-
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct DistinctSources {
-    begin: (String, BytePos),
-    end: (String, BytePos)
-}
-
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub struct MalformedCodemapPositions {
-    name: String,
-    source_len: usize,
-    begin_pos: BytePos,
-    end_pos: BytePos
+impl CodeMapper for CodeMap {
+    fn lookup_char_pos(&self, pos: BytePos) -> Loc {
+        self.lookup_char_pos(pos)
+    }
+    fn span_to_lines(&self, sp: Span) -> FileLinesResult {
+        self.span_to_lines(sp)
+    }
+    fn span_to_string(&self, sp: Span) -> String {
+        self.span_to_string(sp)
+    }
+    fn span_to_filename(&self, sp: Span) -> FileName {
+        self.span_to_filename(sp)
+    }
+    fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace> {
+        self.macro_backtrace(span)
+    }
 }
 
-
 // _____________________________________________________________________________
 // Tests
 //
@@ -1442,11 +827,19 @@ pub struct MalformedCodemapPositions {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use errors::{Level, CodeSuggestion};
+    use errors::emitter::EmitterWriter;
+    use errors::snippet::{SnippetData, RenderedLine, FormatMode};
+    use std::sync::{Arc, Mutex};
+    use std::io::{self, Write};
+    use std::str::from_utf8;
+    use std::rc::Rc;
 
     #[test]
     fn t1 () {
         let cm = CodeMap::new();
         let fm = cm.new_filemap("blork.rs".to_string(),
+                                None,
                                 "first line.\nsecond line".to_string());
         fm.next_line(BytePos(0));
         // Test we can get lines with partial line info.
@@ -1463,6 +856,7 @@ mod tests {
     fn t2 () {
         let cm = CodeMap::new();
         let fm = cm.new_filemap("blork.rs".to_string(),
+                                None,
                                 "first line.\nsecond line".to_string());
         // TESTING *REALLY* BROKEN BEHAVIOR:
         fm.next_line(BytePos(0));
@@ -1473,10 +867,13 @@ mod tests {
     fn init_code_map() -> CodeMap {
         let cm = CodeMap::new();
         let fm1 = cm.new_filemap("blork.rs".to_string(),
+                                 None,
                                  "first line.\nsecond line".to_string());
         let fm2 = cm.new_filemap("empty.rs".to_string(),
+                                 None,
                                  "".to_string());
         let fm3 = cm.new_filemap("blork2.rs".to_string(),
+                                 None,
                                  "first line.\nsecond line".to_string());
 
         fm1.next_line(BytePos(0));
@@ -1539,8 +936,10 @@ mod tests {
         // € is a three byte utf8 char.
         let fm1 =
             cm.new_filemap("blork.rs".to_string(),
+                           None,
                            "fir€st €€€€ line.\nsecond line".to_string());
         let fm2 = cm.new_filemap("blork2.rs".to_string(),
+                                 None,
                                  "first line€€.\n€ second line".to_string());
 
         fm1.next_line(BytePos(0));
@@ -1608,7 +1007,7 @@ mod tests {
         let cm = CodeMap::new();
         let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
         let selection = "     \n    ~~\n~~~\n~~~~~     \n   \n";
-        cm.new_filemap_and_lines("blork.rs", inputtext);
+        cm.new_filemap_and_lines("blork.rs", None, inputtext);
         let span = span_from_selection(inputtext, selection);
 
         // check that we are extracting the text we thought we were extracting
@@ -1678,6 +1077,69 @@ mod tests {
                     blork.rs:1:1: 1:12\n  `first line.`\n");
     }
 
+    /// Returns the span corresponding to the `n`th occurrence of
+    /// `substring` in `source_text`.
+    trait CodeMapExtension {
+        fn span_substr(&self,
+                    file: &Rc<FileMap>,
+                    source_text: &str,
+                    substring: &str,
+                    n: usize)
+                    -> Span;
+    }
+
+    impl CodeMapExtension for CodeMap {
+        fn span_substr(&self,
+                    file: &Rc<FileMap>,
+                    source_text: &str,
+                    substring: &str,
+                    n: usize)
+                    -> Span
+        {
+            println!("span_substr(file={:?}/{:?}, substring={:?}, n={})",
+                    file.name, file.start_pos, substring, n);
+            let mut i = 0;
+            let mut hi = 0;
+            loop {
+                let offset = source_text[hi..].find(substring).unwrap_or_else(|| {
+                    panic!("source_text `{}` does not have {} occurrences of `{}`, only {}",
+                        source_text, n, substring, i);
+                });
+                let lo = hi + offset;
+                hi = lo + substring.len();
+                if i == n {
+                    let span = Span {
+                        lo: BytePos(lo as u32 + file.start_pos.0),
+                        hi: BytePos(hi as u32 + file.start_pos.0),
+                        expn_id: NO_EXPANSION,
+                    };
+                    assert_eq!(&self.span_to_snippet(span).unwrap()[..],
+                            substring);
+                    return span;
+                }
+                i += 1;
+            }
+        }
+    }
+
+    fn splice(start: Span, end: Span) -> Span {
+        Span {
+            lo: start.lo,
+            hi: end.hi,
+            expn_id: NO_EXPANSION,
+        }
+    }
+
+    fn make_string(lines: &[RenderedLine]) -> String {
+        lines.iter()
+            .flat_map(|rl| {
+                rl.text.iter()
+                        .map(|s| &s.text[..])
+                        .chain(Some("\n"))
+            })
+            .collect()
+    }
+
     fn init_expansion_chain(cm: &CodeMap) -> Span {
         // Creates an expansion chain containing two recursive calls
         // root -> expA -> expA -> expB -> expB -> end
@@ -1757,4 +1219,761 @@ r"blork2.rs:2:1: 2:12
 ";
         assert_eq!(sstr, res_str);
     }
+
+    struct Sink(Arc<Mutex<Vec<u8>>>);
+    impl Write for Sink {
+        fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+            Write::write(&mut *self.0.lock().unwrap(), data)
+        }
+        fn flush(&mut self) -> io::Result<()> { Ok(()) }
+    }
+
+    // Diagnostic doesn't align properly in span where line number increases by one digit
+    #[test]
+    fn test_hilight_suggestion_issue_11715() {
+        let data = Arc::new(Mutex::new(Vec::new()));
+        let cm = Rc::new(CodeMap::new());
+        let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())),
+                                        None,
+                                        cm.clone(),
+                                        FormatMode::NewErrorFormat);
+        let content = "abcdefg
+        koksi
+        line3
+        line4
+        cinq
+        line6
+        line7
+        line8
+        line9
+        line10
+        e-lä-vän
+        tolv
+        dreizehn
+        ";
+        let file = cm.new_filemap_and_lines("dummy.txt", None, content);
+        let start = file.lines.borrow()[10];
+        let end = file.lines.borrow()[11];
+        let sp = mk_sp(start, end);
+        let lvl = Level::Error;
+        println!("highlight_lines");
+        ew.highlight_lines(&sp.into(), lvl).unwrap();
+        println!("done");
+        let vec = data.lock().unwrap().clone();
+        let vec: &[u8] = &vec;
+        let str = from_utf8(vec).unwrap();
+        println!("r#\"\n{}\"#", str);
+        assert_eq!(str, &r#"
+  --> dummy.txt:11:1
+   |>
+11 |>         e-lä-vän
+   |> ^
+"#[1..]);
+    }
+
+    #[test]
+    fn test_single_span_splice() {
+        // Test that a `MultiSpan` containing a single span splices a substition correctly
+        let cm = CodeMap::new();
+        let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
+        let selection = "     \n    ~~\n~~~\n~~~~~     \n   \n";
+        cm.new_filemap_and_lines("blork.rs", None, inputtext);
+        let sp = span_from_selection(inputtext, selection);
+        let msp: MultiSpan = sp.into();
+
+        // check that we are extracting the text we thought we were extracting
+        assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD");
+
+        let substitute = "ZZZZZZ".to_owned();
+        let expected = "bbbbZZZZZZddddd";
+        let suggest = CodeSuggestion {
+            msp: msp,
+            substitutes: vec![substitute],
+        };
+        assert_eq!(suggest.splice_lines(&cm), expected);
+    }
+
+    #[test]
+    fn test_multi_span_splice() {
+        // Test that a `MultiSpan` containing multiple spans splices a substition correctly
+        let cm = CodeMap::new();
+        let inputtext  = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
+        let selection1 = "     \n      \n   \n          \n ~ \n"; // intentionally out of order
+        let selection2 = "     \n    ~~\n~~~\n~~~~~     \n   \n";
+        cm.new_filemap_and_lines("blork.rs", None, inputtext);
+        let sp1 = span_from_selection(inputtext, selection1);
+        let sp2 = span_from_selection(inputtext, selection2);
+        let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]);
+
+        let expected = "bbbbZZZZZZddddd\neXYZe";
+        let suggest = CodeSuggestion {
+            msp: msp,
+            substitutes: vec!["ZZZZZZ".to_owned(),
+                              "XYZ".to_owned()]
+        };
+
+        assert_eq!(suggest.splice_lines(&cm), expected);
+    }
+
+    #[test]
+    fn test_multispan_highlight() {
+        let data = Arc::new(Mutex::new(Vec::new()));
+        let cm = Rc::new(CodeMap::new());
+        let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())),
+                                          None,
+                                          cm.clone(),
+                                          FormatMode::NewErrorFormat);
+
+        let inp =       "_____aaaaaa____bbbbbb__cccccdd_";
+        let sp1 =       "     ~~~~~~                    ";
+        let sp2 =       "               ~~~~~~          ";
+        let sp3 =       "                       ~~~~~   ";
+        let sp4 =       "                          ~~~~ ";
+        let sp34 =      "                       ~~~~~~~ ";
+
+        let expect_start = &r#"
+ --> dummy.txt:1:6
+  |>
+1 |> _____aaaaaa____bbbbbb__cccccdd_
+  |>      ^^^^^^    ^^^^^^  ^^^^^^^
+"#[1..];
+
+        let span = |sp, expected| {
+            let sp = span_from_selection(inp, sp);
+            assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected);
+            sp
+        };
+        cm.new_filemap_and_lines("dummy.txt", None, inp);
+        let sp1 = span(sp1, "aaaaaa");
+        let sp2 = span(sp2, "bbbbbb");
+        let sp3 = span(sp3, "ccccc");
+        let sp4 = span(sp4, "ccdd");
+        let sp34 = span(sp34, "cccccdd");
+
+        let spans = vec![sp1, sp2, sp3, sp4];
+
+        let test = |expected, highlight: &mut FnMut()| {
+            data.lock().unwrap().clear();
+            highlight();
+            let vec = data.lock().unwrap().clone();
+            let actual = from_utf8(&vec[..]).unwrap();
+            println!("actual=\n{}", actual);
+            assert_eq!(actual, expected);
+        };
+
+        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]);
+        test(expect_start, &mut || {
+            diag.highlight_lines(&msp, Level::Error).unwrap();
+        });
+        test(expect_start, &mut || {
+            let msp = MultiSpan::from_spans(spans.clone());
+            diag.highlight_lines(&msp, Level::Error).unwrap();
+        });
+    }
+
+    #[test]
+    fn test_huge_multispan_highlight() {
+        let data = Arc::new(Mutex::new(Vec::new()));
+        let cm = Rc::new(CodeMap::new());
+        let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())),
+                                          None,
+                                          cm.clone(),
+                                          FormatMode::NewErrorFormat);
+
+        let inp = "aaaaa\n\
+                   aaaaa\n\
+                   aaaaa\n\
+                   bbbbb\n\
+                   ccccc\n\
+                   xxxxx\n\
+                   yyyyy\n\
+                   _____\n\
+                   ddd__eee_\n\
+                   elided\n\
+                   __f_gg";
+        let file = cm.new_filemap_and_lines("dummy.txt", None, inp);
+
+        let span = |lo, hi, (off_lo, off_hi)| {
+            let lines = file.lines.borrow();
+            let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]);
+            lo.0 += off_lo;
+            hi.0 += off_hi;
+            mk_sp(lo, hi)
+        };
+        let sp0 = span(4, 6, (0, 5));
+        let sp1 = span(0, 6, (0, 5));
+        let sp2 = span(8, 8, (0, 3));
+        let sp3 = span(8, 8, (5, 8));
+        let sp4 = span(10, 10, (2, 3));
+        let sp5 = span(10, 10, (4, 6));
+
+        let expect0 = &r#"
+   --> dummy.txt:5:1
+    |>
+5   |> ccccc
+    |> ^
+...
+9   |> ddd__eee_
+    |> ^^^  ^^^
+10  |> elided
+11  |> __f_gg
+    |>   ^ ^^
+"#[1..];
+
+        let expect = &r#"
+   --> dummy.txt:1:1
+    |>
+1   |> aaaaa
+    |> ^
+...
+9   |> ddd__eee_
+    |> ^^^  ^^^
+10  |> elided
+11  |> __f_gg
+    |>   ^ ^^
+"#[1..];
+
+        macro_rules! test {
+            ($expected: expr, $highlight: expr) => ({
+                data.lock().unwrap().clear();
+                $highlight();
+                let vec = data.lock().unwrap().clone();
+                let actual = from_utf8(&vec[..]).unwrap();
+                println!("actual:");
+                println!("{}", actual);
+                println!("expected:");
+                println!("{}", $expected);
+                assert_eq!(&actual[..], &$expected[..]);
+            });
+        }
+
+        let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]);
+        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]);
+
+        test!(expect0, || {
+            diag.highlight_lines(&msp0, Level::Error).unwrap();
+        });
+        test!(expect, || {
+            diag.highlight_lines(&msp, Level::Error).unwrap();
+        });
+    }
+
+    #[test]
+    fn tab() {
+        let file_text = "
+fn foo() {
+\tbar;
+}
+";
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+        let span_bar = cm.span_substr(&foo, file_text, "bar", 0);
+
+        let mut snippet = SnippetData::new(cm, Some(span_bar), FormatMode::NewErrorFormat);
+        snippet.push(span_bar, true, None);
+
+        let lines = snippet.render_lines();
+        let text = make_string(&lines);
+        assert_eq!(&text[..], &"
+ --> foo.rs:3:2
+  |>
+3 |> \tbar;
+  |> \t^^^
+"[1..]);
+    }
+
+    #[test]
+    fn one_line() {
+        let file_text = r#"
+fn foo() {
+    vec.push(vec.pop().unwrap());
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+        let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
+        let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
+        let span_semi = cm.span_substr(&foo, file_text, ";", 0);
+
+        let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
+        snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
+        snippet.push(span_vec1, false, Some(format!("error occurs here")));
+        snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
+
+        let lines = snippet.render_lines();
+        println!("{:#?}", lines);
+
+        let text: String = make_string(&lines);
+
+        println!("text=\n{}", text);
+        assert_eq!(&text[..], &r#"
+ ::: foo.rs
+  |>
+3 |>     vec.push(vec.pop().unwrap());
+  |>     ---      ---                - previous borrow ends here
+  |>     |        |
+  |>     |        error occurs here
+  |>     previous borrow of `vec` occurs here
+"#[1..]);
+    }
+
+    #[test]
+    fn two_files() {
+        let file_text_foo = r#"
+fn foo() {
+    vec.push(vec.pop().unwrap());
+}
+"#;
+
+        let file_text_bar = r#"
+fn bar() {
+    // these blank links here
+    // serve to ensure that the line numbers
+    // from bar.rs
+    // require more digits
+
+
+
+
+
+
+
+
+
+
+    vec.push();
+
+    // this line will get elided
+
+    vec.pop().unwrap());
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo_map = cm.new_filemap_and_lines("foo.rs", None, file_text_foo);
+        let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0);
+        let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1);
+        let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0);
+
+        let bar_map = cm.new_filemap_and_lines("bar.rs", None, file_text_bar);
+        let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0);
+        let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1);
+        let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0);
+
+        let mut snippet = SnippetData::new(cm, Some(span_foo_vec1), FormatMode::NewErrorFormat);
+        snippet.push(span_foo_vec0, false, Some(format!("a")));
+        snippet.push(span_foo_vec1, true, Some(format!("b")));
+        snippet.push(span_foo_semi, false, Some(format!("c")));
+        snippet.push(span_bar_vec0, false, Some(format!("d")));
+        snippet.push(span_bar_vec1, false, Some(format!("e")));
+        snippet.push(span_bar_semi, false, Some(format!("f")));
+
+        let lines = snippet.render_lines();
+        println!("{:#?}", lines);
+
+        let text: String = make_string(&lines);
+
+        println!("text=\n{}", text);
+
+        // Note that the `|>` remain aligned across both files:
+        assert_eq!(&text[..], &r#"
+   --> foo.rs:3:14
+    |>
+3   |>     vec.push(vec.pop().unwrap());
+    |>     ---      ^^^                - c
+    |>     |        |
+    |>     |        b
+    |>     a
+   ::: bar.rs
+    |>
+17  |>     vec.push();
+    |>     ---       - f
+    |>     |
+    |>     d
+...
+21  |>     vec.pop().unwrap());
+    |>     --- e
+"#[1..]);
+    }
+
+    #[test]
+    fn multi_line() {
+        let file_text = r#"
+fn foo() {
+    let name = find_id(&data, 22).unwrap();
+
+    // Add one more item we forgot to the vector. Silly us.
+    data.push(Data { name: format!("Hera"), id: 66 });
+
+    // Print everything out.
+    println!("Name: {:?}", name);
+    println!("Data: {:?}", data);
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+        let span_data0 = cm.span_substr(&foo, file_text, "data", 0);
+        let span_data1 = cm.span_substr(&foo, file_text, "data", 1);
+        let span_rbrace = cm.span_substr(&foo, file_text, "}", 3);
+
+        let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
+        snippet.push(span_data0, false, Some(format!("immutable borrow begins here")));
+        snippet.push(span_data1, false, Some(format!("mutable borrow occurs here")));
+        snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here")));
+
+        let lines = snippet.render_lines();
+        println!("{:#?}", lines);
+
+        let text: String = make_string(&lines);
+
+        println!("text=\n{}", text);
+        assert_eq!(&text[..], &r#"
+   ::: foo.rs
+    |>
+3   |>     let name = find_id(&data, 22).unwrap();
+    |>                         ---- immutable borrow begins here
+...
+6   |>     data.push(Data { name: format!("Hera"), id: 66 });
+    |>     ---- mutable borrow occurs here
+...
+11  |> }
+    |> - immutable borrow ends here
+"#[1..]);
+    }
+
+    #[test]
+    fn overlapping() {
+        let file_text = r#"
+fn foo() {
+    vec.push(vec.pop().unwrap());
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+        let span0 = cm.span_substr(&foo, file_text, "vec.push", 0);
+        let span1 = cm.span_substr(&foo, file_text, "vec", 0);
+        let span2 = cm.span_substr(&foo, file_text, "ec.push", 0);
+        let span3 = cm.span_substr(&foo, file_text, "unwrap", 0);
+
+        let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
+        snippet.push(span0, false, Some(format!("A")));
+        snippet.push(span1, false, Some(format!("B")));
+        snippet.push(span2, false, Some(format!("C")));
+        snippet.push(span3, false, Some(format!("D")));
+
+        let lines = snippet.render_lines();
+        println!("{:#?}", lines);
+        let text: String = make_string(&lines);
+
+        println!("text=r#\"\n{}\".trim_left()", text);
+        assert_eq!(&text[..], &r#"
+ ::: foo.rs
+  |>
+3 |>     vec.push(vec.pop().unwrap());
+  |>     --------           ------ D
+  |>     ||
+  |>     |C
+  |>     A
+  |>     B
+"#[1..]);
+    }
+
+    #[test]
+    fn one_line_out_of_order() {
+        let file_text = r#"
+fn foo() {
+    vec.push(vec.pop().unwrap());
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+        let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
+        let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
+        let span_semi = cm.span_substr(&foo, file_text, ";", 0);
+
+        // intentionally don't push the snippets left to right
+        let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
+        snippet.push(span_vec1, false, Some(format!("error occurs here")));
+        snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
+        snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
+
+        let lines = snippet.render_lines();
+        println!("{:#?}", lines);
+        let text: String = make_string(&lines);
+
+        println!("text=r#\"\n{}\".trim_left()", text);
+        assert_eq!(&text[..], &r#"
+ ::: foo.rs
+  |>
+3 |>     vec.push(vec.pop().unwrap());
+  |>     ---      ---                - previous borrow ends here
+  |>     |        |
+  |>     |        error occurs here
+  |>     previous borrow of `vec` occurs here
+"#[1..]);
+    }
+
+    #[test]
+    fn elide_unnecessary_lines() {
+        let file_text = r#"
+fn foo() {
+    let mut vec = vec![0, 1, 2];
+    let mut vec2 = vec;
+    vec2.push(3);
+    vec2.push(4);
+    vec2.push(5);
+    vec2.push(6);
+    vec.push(7);
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+        let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3);
+        let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8);
+
+        let mut snippet = SnippetData::new(cm, None, FormatMode::NewErrorFormat);
+        snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \
+            has type `collections::vec::Vec<i32>`")));
+        snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`")));
+
+        let lines = snippet.render_lines();
+        println!("{:#?}", lines);
+        let text: String = make_string(&lines);
+        println!("text=r#\"\n{}\".trim_left()", text);
+        assert_eq!(&text[..], &r#"
+   ::: foo.rs
+    |>
+4   |>     let mut vec2 = vec;
+    |>                    --- `vec` moved here because it has type `collections::vec::Vec<i32>`
+...
+9   |>     vec.push(7);
+    |>     --- use of moved value: `vec`
+"#[1..]);
+    }
+
+    #[test]
+    fn spans_without_labels() {
+        let file_text = r#"
+fn foo() {
+    let mut vec = vec![0, 1, 2];
+    let mut vec2 = vec;
+    vec2.push(3);
+    vec2.push(4);
+    vec2.push(5);
+    vec2.push(6);
+    vec.push(7);
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+        let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
+        for i in 0..4 {
+            let span_veci = cm.span_substr(&foo, file_text, "vec", i);
+            snippet.push(span_veci, false, None);
+        }
+
+        let lines = snippet.render_lines();
+        let text: String = make_string(&lines);
+        println!("text=&r#\"\n{}\n\"#[1..]", text);
+        assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+3 |>     let mut vec = vec![0, 1, 2];
+  |>             ---   ---
+4 |>     let mut vec2 = vec;
+  |>             ---    ---
+"#[1..]);
+    }
+
+    #[test]
+    fn span_long_selection() {
+        let file_text = r#"
+impl SomeTrait for () {
+    fn foo(x: u32) {
+        // impl 1
+        // impl 2
+        // impl 3
+    }
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+        let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
+        let fn_span = cm.span_substr(&foo, file_text, "fn", 0);
+        let rbrace_span = cm.span_substr(&foo, file_text, "}", 0);
+        snippet.push(splice(fn_span, rbrace_span), false, None);
+        let lines = snippet.render_lines();
+        let text: String = make_string(&lines);
+        println!("r#\"\n{}\"", text);
+        assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+3 |>     fn foo(x: u32) {
+  |>     -
+"#[1..]);
+    }
+
+    #[test]
+    fn span_overlap_label() {
+        // Test that we don't put `x_span` to the right of its highlight,
+        // since there is another highlight that overlaps it.
+
+        let file_text = r#"
+    fn foo(x: u32) {
+    }
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+        let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
+        let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0);
+        let x_span = cm.span_substr(&foo, file_text, "x", 0);
+        snippet.push(fn_span, false, Some(format!("fn_span")));
+        snippet.push(x_span, false, Some(format!("x_span")));
+        let lines = snippet.render_lines();
+        let text: String = make_string(&lines);
+        println!("r#\"\n{}\"", text);
+        assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+2 |>     fn foo(x: u32) {
+  |>     --------------
+  |>     |      |
+  |>     |      x_span
+  |>     fn_span
+"#[1..]);
+    }
+
+    #[test]
+    fn span_overlap_label2() {
+        // Test that we don't put `x_span` to the right of its highlight,
+        // since there is another highlight that overlaps it. In this
+        // case, the overlap is only at the beginning, but it's still
+        // better to show the beginning more clearly.
+
+        let file_text = r#"
+    fn foo(x: u32) {
+    }
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+        let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
+        let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0);
+        let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0);
+        snippet.push(fn_span, false, Some(format!("fn_span")));
+        snippet.push(x_span, false, Some(format!("x_span")));
+        let lines = snippet.render_lines();
+        let text: String = make_string(&lines);
+        println!("r#\"\n{}\"", text);
+        assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+2 |>     fn foo(x: u32) {
+  |>     --------------
+  |>     |      |
+  |>     |      x_span
+  |>     fn_span
+"#[1..]);
+    }
+
+    #[test]
+    fn span_overlap_label3() {
+        // Test that we don't put `x_span` to the right of its highlight,
+        // since there is another highlight that overlaps it. In this
+        // case, the overlap is only at the beginning, but it's still
+        // better to show the beginning more clearly.
+
+        let file_text = r#"
+    fn foo() {
+       let closure = || {
+           inner
+       };
+    }
+}
+"#;
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+        let mut snippet = SnippetData::new(cm.clone(), None, FormatMode::NewErrorFormat);
+
+        let closure_span = {
+            let closure_start_span = cm.span_substr(&foo, file_text, "||", 0);
+            let closure_end_span = cm.span_substr(&foo, file_text, "}", 0);
+            splice(closure_start_span, closure_end_span)
+        };
+
+        let inner_span = cm.span_substr(&foo, file_text, "inner", 0);
+
+        snippet.push(closure_span, false, Some(format!("foo")));
+        snippet.push(inner_span, false, Some(format!("bar")));
+
+        let lines = snippet.render_lines();
+        let text: String = make_string(&lines);
+        println!("r#\"\n{}\"", text);
+        assert_eq!(text, &r#"
+ ::: foo.rs
+  |>
+3 |>        let closure = || {
+  |>                      - foo
+4 |>            inner
+  |>            ----- bar
+"#[1..]);
+    }
+
+    #[test]
+    fn span_empty() {
+        // In one of the unit tests, we found that the parser sometimes
+        // gives empty spans, and in particular it supplied an EOF span
+        // like this one, which points at the very end. We want to
+        // fallback gracefully in this case.
+
+        let file_text = r#"
+fn main() {
+    struct Foo;
+
+    impl !Sync for Foo {}
+
+    unsafe impl Send for &'static Foo {
+    // error: cross-crate traits with a default impl, like `core::marker::Send`,
+    //        can only be implemented for a struct/enum type, not
+    //        `&'static Foo`
+}"#;
+
+
+        let cm = Rc::new(CodeMap::new());
+        let foo = cm.new_filemap_and_lines("foo.rs", None, file_text);
+
+        let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1);
+        rbrace_span.lo = rbrace_span.hi;
+
+        let mut snippet = SnippetData::new(cm.clone(),
+                                           Some(rbrace_span),
+                                           FormatMode::NewErrorFormat);
+        snippet.push(rbrace_span, false, None);
+        let lines = snippet.render_lines();
+        let text: String = make_string(&lines);
+        println!("r#\"\n{}\"", text);
+        assert_eq!(text, &r#"
+  --> foo.rs:11:2
+   |>
+11 |> }
+   |>  -
+"#[1..]);
+    }
 }
index 4554a280e5f198c1ddf11fc0e7dbf7750e40603e..6f0334f4779b365992654887e009fdaa8294fb76 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use attr::AttrMetaMethods;
-use errors::Handler;
-use feature_gate::GatedCfgAttr;
+use attr::{AttrMetaMethods, HasAttrs};
+use feature_gate::{emit_feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue};
 use fold::Folder;
-use {ast, fold, attr};
-use visit;
+use {fold, attr};
+use ast;
 use codemap::{Spanned, respan};
+use parse::{ParseSess, token};
 use ptr::P;
 
 use util::small_vector::SmallVector;
 
-/// A folder that strips out items that do not belong in the current
-/// configuration.
-struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
-    in_cfg: F,
-    diagnostic: &'a Handler,
+/// A folder that strips out items that do not belong in the current configuration.
+pub struct StripUnconfigured<'a> {
+    pub config: &'a ast::CrateConfig,
+    pub should_test: bool,
+    pub sess: &'a ParseSess,
+    pub features: Option<&'a Features>,
 }
 
-// Support conditional compilation by transforming the AST, stripping out
-// any items that do not belong in the current configuration
-pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate,
-                                feature_gated_cfgs: &mut Vec<GatedCfgAttr>)
-                                -> ast::Crate
-{
-    // Need to do this check here because cfg runs before feature_gates
-    check_for_gated_stmt_expr_attributes(&krate, feature_gated_cfgs);
-
-    let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs);
-    let config = krate.config.clone();
-    strip_items(diagnostic,
-                krate,
-                |attrs| {
-                    let mut diag = CfgDiagReal {
-                        diag: diagnostic,
-                        feature_gated_cfgs: feature_gated_cfgs,
-                    };
-                    in_cfg(&config, attrs, &mut diag)
-                })
-}
-
-impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
-    fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
-        fold_foreign_mod(self, foreign_mod)
-    }
-    fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
-        fold_item_kind(self, item)
-    }
-    fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
-        // If an expr is valid to cfg away it will have been removed by the
-        // outer stmt or expression folder before descending in here.
-        // Anything else is always required, and thus has to error out
-        // in case of a cfg attr.
-        //
-        // NB: This is intentionally not part of the fold_expr() function
-        //     in order for fold_opt_expr() to be able to avoid this check
-        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
-            self.diagnostic.span_err(attr.span,
-                "removing an expression is not supported in this position");
-        }
-        fold_expr(self, expr)
-    }
-    fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
-        fold_opt_expr(self, expr)
-    }
-    fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
-        fold_stmt(self, stmt)
-    }
-    fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
-        fold::noop_fold_mac(mac, self)
-    }
-    fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
-        fold_item(self, item)
-    }
-}
-
-pub fn strip_items<'a, F>(diagnostic: &'a Handler,
-                          krate: ast::Crate, in_cfg: F) -> ast::Crate where
-    F: FnMut(&[ast::Attribute]) -> bool,
-{
-    let mut ctxt = Context {
-        in_cfg: in_cfg,
-        diagnostic: diagnostic,
-    };
-    ctxt.fold_crate(krate)
-}
-
-fn filter_foreign_item<F>(cx: &mut Context<F>,
-                          item: ast::ForeignItem)
-                          -> Option<ast::ForeignItem> where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    if foreign_item_in_cfg(cx, &item) {
-        Some(item)
-    } else {
-        None
+impl<'a> StripUnconfigured<'a> {
+    fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
+        let node = self.process_cfg_attrs(node);
+        if self.in_cfg(node.attrs()) { Some(node) } else { None }
     }
-}
 
-fn fold_foreign_mod<F>(cx: &mut Context<F>,
-                       ast::ForeignMod {abi, items}: ast::ForeignMod)
-                       -> ast::ForeignMod where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    ast::ForeignMod {
-        abi: abi,
-        items: items.into_iter()
-                    .filter_map(|a| filter_foreign_item(cx, a))
-                    .collect()
+    fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T {
+        node.map_attrs(|attrs| {
+            attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect()
+        })
     }
-}
-
-fn fold_item<F>(cx: &mut Context<F>, item: P<ast::Item>) -> SmallVector<P<ast::Item>> where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    if item_in_cfg(cx, &item) {
-        SmallVector::one(item.map(|i| cx.fold_item_simple(i)))
-    } else {
-        SmallVector::zero()
-    }
-}
-
-fn fold_item_kind<F>(cx: &mut Context<F>, item: ast::ItemKind) -> ast::ItemKind where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    let item = match item {
-        ast::ItemKind::Impl(u, o, a, b, c, impl_items) => {
-            let impl_items = impl_items.into_iter()
-                                       .filter(|ii| (cx.in_cfg)(&ii.attrs))
-                                       .collect();
-            ast::ItemKind::Impl(u, o, a, b, c, impl_items)
-        }
-        ast::ItemKind::Trait(u, a, b, methods) => {
-            let methods = methods.into_iter()
-                                 .filter(|ti| (cx.in_cfg)(&ti.attrs))
-                                 .collect();
-            ast::ItemKind::Trait(u, a, b, methods)
-        }
-        ast::ItemKind::Struct(def, generics) => {
-            ast::ItemKind::Struct(fold_struct(cx, def), generics)
-        }
-        ast::ItemKind::Enum(def, generics) => {
-            let variants = def.variants.into_iter().filter_map(|v| {
-                if !(cx.in_cfg)(&v.node.attrs) {
-                    None
-                } else {
-                    Some(Spanned {
-                        node: ast::Variant_ {
-                            name: v.node.name,
-                            attrs: v.node.attrs,
-                            data: fold_struct(cx, v.node.data),
-                            disr_expr: v.node.disr_expr,
-                        },
-                        span: v.span
-                    })
-                }
-            });
-            ast::ItemKind::Enum(ast::EnumDef {
-                variants: variants.collect(),
-            }, generics)
-        }
-        item => item,
-    };
-
-    fold::noop_fold_item_kind(item, cx)
-}
-
-fn fold_struct<F>(cx: &mut Context<F>, vdata: ast::VariantData) -> ast::VariantData where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    match vdata {
-        ast::VariantData::Struct(fields, id) => {
-            ast::VariantData::Struct(fields.into_iter().filter(|m| {
-                (cx.in_cfg)(&m.attrs)
-            }).collect(), id)
-        }
-        ast::VariantData::Tuple(fields, id) => {
-            ast::VariantData::Tuple(fields.into_iter().filter(|m| {
-                (cx.in_cfg)(&m.attrs)
-            }).collect(), id)
-        }
-        ast::VariantData::Unit(id) => ast::VariantData::Unit(id)
-    }
-}
-
-fn fold_opt_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> Option<P<ast::Expr>>
-    where F: FnMut(&[ast::Attribute]) -> bool
-{
-    if expr_in_cfg(cx, &expr) {
-        Some(fold_expr(cx, expr))
-    } else {
-        None
-    }
-}
-
-fn fold_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> P<ast::Expr> where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    expr.map(|ast::Expr {id, span, node, attrs}| {
-        fold::noop_fold_expr(ast::Expr {
-            id: id,
-            node: match node {
-                ast::ExprKind::Match(m, arms) => {
-                    ast::ExprKind::Match(m, arms.into_iter()
-                                        .filter(|a| (cx.in_cfg)(&a.attrs))
-                                        .collect())
-                }
-                _ => node
-            },
-            span: span,
-            attrs: attrs,
-        }, cx)
-    })
-}
-
-fn fold_stmt<F>(cx: &mut Context<F>, stmt: ast::Stmt) -> SmallVector<ast::Stmt>
-    where F: FnMut(&[ast::Attribute]) -> bool
-{
-    if stmt_in_cfg(cx, &stmt) {
-        fold::noop_fold_stmt(stmt, cx)
-    } else {
-        SmallVector::zero()
-    }
-}
-
-fn stmt_in_cfg<F>(cx: &mut Context<F>, stmt: &ast::Stmt) -> bool where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    (cx.in_cfg)(stmt.node.attrs())
-}
-
-fn expr_in_cfg<F>(cx: &mut Context<F>, expr: &ast::Expr) -> bool where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    (cx.in_cfg)(expr.attrs())
-}
-
-fn item_in_cfg<F>(cx: &mut Context<F>, item: &ast::Item) -> bool where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    return (cx.in_cfg)(&item.attrs);
-}
-
-fn foreign_item_in_cfg<F>(cx: &mut Context<F>, item: &ast::ForeignItem) -> bool where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    return (cx.in_cfg)(&item.attrs);
-}
-
-fn is_cfg(attr: &ast::Attribute) -> bool {
-    attr.check_name("cfg")
-}
-
-// Determine if an item should be translated in the current crate
-// configuration based on the item's attributes
-fn in_cfg<T: CfgDiag>(cfg: &[P<ast::MetaItem>],
-                      attrs: &[ast::Attribute],
-                      diag: &mut T) -> bool {
-    attrs.iter().all(|attr| {
-        let mis = match attr.node.value.node {
-            ast::MetaItemKind::List(_, ref mis) if is_cfg(&attr) => mis,
-            _ => return true
-        };
-
-        if mis.len() != 1 {
-            diag.emit_error(|diagnostic| {
-                diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
-            });
-            return true;
-        }
-
-        attr::cfg_matches(cfg, &mis[0], diag)
-    })
-}
 
-struct CfgAttrFolder<'a, T> {
-    diag: T,
-    config: &'a ast::CrateConfig,
-}
-
-// Process `#[cfg_attr]`.
-fn process_cfg_attr(diagnostic: &Handler, krate: ast::Crate,
-                    feature_gated_cfgs: &mut Vec<GatedCfgAttr>) -> ast::Crate {
-    let mut fld = CfgAttrFolder {
-        diag: CfgDiagReal {
-            diag: diagnostic,
-            feature_gated_cfgs: feature_gated_cfgs,
-        },
-        config: &krate.config.clone(),
-    };
-    fld.fold_crate(krate)
-}
-
-impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> {
-    fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
+    fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
         if !attr.check_name("cfg_attr") {
-            return fold::noop_fold_attribute(attr, self);
+            return Some(attr);
         }
 
         let attr_list = match attr.meta_item_list() {
             Some(attr_list) => attr_list,
             None => {
-                self.diag.emit_error(|diag| {
-                    diag.span_err(attr.span,
-                        "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
-                });
+                let msg = "expected `#[cfg_attr(<cfg pattern>, <attr>)]`";
+                self.sess.span_diagnostic.span_err(attr.span, msg);
                 return None;
             }
         };
         let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) {
             (2, Some(cfg), Some(mi)) => (cfg, mi),
             _ => {
-                self.diag.emit_error(|diag| {
-                    diag.span_err(attr.span,
-                        "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
-                });
+                let msg = "expected `#[cfg_attr(<cfg pattern>, <attr>)]`";
+                self.sess.span_diagnostic.span_err(attr.span, msg);
                 return None;
             }
         };
 
-        if attr::cfg_matches(&self.config[..], &cfg, &mut self.diag) {
-            Some(respan(mi.span, ast::Attribute_ {
+        if attr::cfg_matches(self.config, &cfg, self.sess, self.features) {
+            self.process_cfg_attr(respan(mi.span, ast::Attribute_ {
                 id: attr::mk_attr_id(),
                 style: attr.node.style,
                 value: mi.clone(),
@@ -339,179 +73,199 @@ impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> {
         }
     }
 
-    // Need the ability to run pre-expansion.
-    fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
-        fold::noop_fold_mac(mac, self)
-    }
-}
-
-fn check_for_gated_stmt_expr_attributes(krate: &ast::Crate,
-                                        discovered: &mut Vec<GatedCfgAttr>) {
-    let mut v = StmtExprAttrFeatureVisitor {
-        config: &krate.config,
-        discovered: discovered,
-    };
-    visit::walk_crate(&mut v, krate);
-}
+    // Determine if a node with the given attributes should be included in this configuation.
+    fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
+        attrs.iter().all(|attr| {
+            // When not compiling with --test we should not compile the #[test] functions
+            if !self.should_test && is_test_or_bench(attr) {
+                return false;
+            }
 
-/// To cover this feature, we need to discover all attributes
-/// so we need to run before cfg.
-struct StmtExprAttrFeatureVisitor<'a, 'b> {
-    config: &'a ast::CrateConfig,
-    discovered: &'b mut Vec<GatedCfgAttr>,
-}
+            let mis = match attr.node.value.node {
+                ast::MetaItemKind::List(_, ref mis) if is_cfg(&attr) => mis,
+                _ => return true
+            };
 
-// Runs the cfg_attr and cfg folders locally in "silent" mode
-// to discover attribute use on stmts or expressions ahead of time
-impl<'v, 'a, 'b> visit::Visitor<'v> for StmtExprAttrFeatureVisitor<'a, 'b> {
-    fn visit_stmt(&mut self, s: &'v ast::Stmt) {
-        // check if there even are any attributes on this node
-        let stmt_attrs = s.node.attrs();
-        if stmt_attrs.len() > 0 {
-            // attributes on items are fine
-            if let ast::StmtKind::Decl(ref decl, _) = s.node {
-                if let ast::DeclKind::Item(_) = decl.node {
-                    visit::walk_stmt(self, s);
-                    return;
-                }
+            if mis.len() != 1 {
+                self.sess.span_diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
+                return true;
             }
 
-            // flag the offending attributes
-            for attr in stmt_attrs {
-                self.discovered.push(GatedCfgAttr::GatedAttr(attr.span));
-            }
+            attr::cfg_matches(self.config, &mis[0], self.sess, self.features)
+        })
+    }
 
-            // if the node does not end up being cfg-d away, walk down
-            if node_survives_cfg(stmt_attrs, self.config) {
-                visit::walk_stmt(self, s);
+    // Visit attributes on expression and statements (but not attributes on items in blocks).
+    fn visit_stmt_or_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
+        // flag the offending attributes
+        for attr in attrs.iter() {
+            if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
+                emit_feature_err(&self.sess.span_diagnostic,
+                                 "stmt_expr_attributes",
+                                 attr.span,
+                                 GateIssue::Language,
+                                 EXPLAIN_STMT_ATTR_SYNTAX);
             }
-        } else {
-            visit::walk_stmt(self, s);
         }
     }
+}
 
-    fn visit_expr(&mut self, ex: &'v ast::Expr) {
-        // check if there even are any attributes on this node
-        let expr_attrs = ex.attrs();
-        if expr_attrs.len() > 0 {
-
-            // flag the offending attributes
-            for attr in expr_attrs {
-                self.discovered.push(GatedCfgAttr::GatedAttr(attr.span));
-            }
+// Support conditional compilation by transforming the AST, stripping out
+// any items that do not belong in the current configuration
+pub fn strip_unconfigured_items(mut krate: ast::Crate, sess: &ParseSess, should_test: bool)
+                                -> (ast::Crate, Features) {
+    let features;
+    {
+        let mut strip_unconfigured = StripUnconfigured {
+            config: &krate.config.clone(),
+            should_test: should_test,
+            sess: sess,
+            features: None,
+        };
 
-            // if the node does not end up being cfg-d away, walk down
-            if node_survives_cfg(expr_attrs, self.config) {
-                visit::walk_expr(self, ex);
-            }
-        } else {
-            visit::walk_expr(self, ex);
+        let err_count = sess.span_diagnostic.err_count();
+        let krate_attrs = strip_unconfigured.configure(krate.attrs.clone()).unwrap_or_default();
+        features = get_features(&sess.span_diagnostic, &krate_attrs);
+        if err_count < sess.span_diagnostic.err_count() {
+            krate.attrs = krate_attrs.clone(); // Avoid reconfiguring malformed `cfg_attr`s
         }
-    }
 
-    fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
-        if node_survives_cfg(&i.attrs, self.config) {
-            visit::walk_foreign_item(self, i);
-        }
+        strip_unconfigured.features = Some(&features);
+        krate = strip_unconfigured.fold_crate(krate);
+        krate.attrs = krate_attrs;
     }
 
-    fn visit_item(&mut self, i: &'v ast::Item) {
-        if node_survives_cfg(&i.attrs, self.config) {
-            visit::walk_item(self, i);
-        }
-    }
+    (krate, features)
+}
 
-    fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
-        if node_survives_cfg(&ii.attrs, self.config) {
-            visit::walk_impl_item(self, ii);
+impl<'a> fold::Folder for StripUnconfigured<'a> {
+    fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
+        ast::ForeignMod {
+            abi: foreign_mod.abi,
+            items: foreign_mod.items.into_iter().filter_map(|item| {
+                self.configure(item).map(|item| fold::noop_fold_foreign_item(item, self))
+            }).collect(),
         }
     }
 
-    fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
-        if node_survives_cfg(&ti.attrs, self.config) {
-            visit::walk_trait_item(self, ti);
-        }
+    fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
+        let fold_struct = |this: &mut Self, vdata| match vdata {
+            ast::VariantData::Struct(fields, id) => {
+                let fields = fields.into_iter().filter_map(|field| this.configure(field));
+                ast::VariantData::Struct(fields.collect(), id)
+            }
+            ast::VariantData::Tuple(fields, id) => {
+                let fields = fields.into_iter().filter_map(|field| this.configure(field));
+                ast::VariantData::Tuple(fields.collect(), id)
+            }
+            ast::VariantData::Unit(id) => ast::VariantData::Unit(id)
+        };
+
+        let item = match item {
+            ast::ItemKind::Struct(def, generics) => {
+                ast::ItemKind::Struct(fold_struct(self, def), generics)
+            }
+            ast::ItemKind::Enum(def, generics) => {
+                let variants = def.variants.into_iter().filter_map(|v| {
+                    self.configure(v).map(|v| {
+                        Spanned {
+                            node: ast::Variant_ {
+                                name: v.node.name,
+                                attrs: v.node.attrs,
+                                data: fold_struct(self, v.node.data),
+                                disr_expr: v.node.disr_expr,
+                            },
+                            span: v.span
+                        }
+                    })
+                });
+                ast::ItemKind::Enum(ast::EnumDef {
+                    variants: variants.collect(),
+                }, generics)
+            }
+            item => item,
+        };
+
+        fold::noop_fold_item_kind(item, self)
     }
 
-    fn visit_struct_field(&mut self, s: &'v ast::StructField) {
-        if node_survives_cfg(&s.attrs, self.config) {
-            visit::walk_struct_field(self, s);
+    fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
+        self.visit_stmt_or_expr_attrs(expr.attrs());
+
+        // If an expr is valid to cfg away it will have been removed by the
+        // outer stmt or expression folder before descending in here.
+        // Anything else is always required, and thus has to error out
+        // in case of a cfg attr.
+        //
+        // NB: This is intentionally not part of the fold_expr() function
+        //     in order for fold_opt_expr() to be able to avoid this check
+        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a) || is_test_or_bench(a)) {
+            let msg = "removing an expression is not supported in this position";
+            self.sess.span_diagnostic.span_err(attr.span, msg);
         }
+
+        let expr = self.process_cfg_attrs(expr);
+        fold_expr(self, expr)
     }
 
-    fn visit_variant(&mut self, v: &'v ast::Variant,
-                     g: &'v ast::Generics, item_id: ast::NodeId) {
-        if node_survives_cfg(&v.node.attrs, self.config) {
-            visit::walk_variant(self, v, g, item_id);
-        }
+    fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
+        self.configure(expr).map(|expr| fold_expr(self, expr))
     }
 
-    fn visit_arm(&mut self, a: &'v ast::Arm) {
-        if node_survives_cfg(&a.attrs, self.config) {
-            visit::walk_arm(self, a);
-        }
+    fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
+        self.visit_stmt_or_expr_attrs(stmt.attrs());
+        self.configure(stmt).map(|stmt| fold::noop_fold_stmt(stmt, self))
+                            .unwrap_or(SmallVector::zero())
     }
 
-    // This visitor runs pre expansion, so we need to prevent
-    // the default panic here
-    fn visit_mac(&mut self, mac: &'v ast::Mac) {
-        visit::walk_mac(self, mac)
+    fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
+        fold::noop_fold_mac(mac, self)
     }
-}
 
-pub trait CfgDiag {
-    fn emit_error<F>(&mut self, f: F) where F: FnMut(&Handler);
-    fn flag_gated<F>(&mut self, f: F) where F: FnMut(&mut Vec<GatedCfgAttr>);
-}
+    fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
+        self.configure(item).map(|item| fold::noop_fold_item(item, self))
+                            .unwrap_or(SmallVector::zero())
+    }
 
-pub struct CfgDiagReal<'a, 'b> {
-    pub diag: &'a Handler,
-    pub feature_gated_cfgs: &'b mut Vec<GatedCfgAttr>,
-}
+    fn fold_impl_item(&mut self, item: ast::ImplItem) -> SmallVector<ast::ImplItem> {
+        self.configure(item).map(|item| fold::noop_fold_impl_item(item, self))
+                            .unwrap_or(SmallVector::zero())
+    }
 
-impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> {
-    fn emit_error<F>(&mut self, mut f: F) where F: FnMut(&Handler) {
-        f(self.diag)
+    fn fold_trait_item(&mut self, item: ast::TraitItem) -> SmallVector<ast::TraitItem> {
+        self.configure(item).map(|item| fold::noop_fold_trait_item(item, self))
+                            .unwrap_or(SmallVector::zero())
     }
-    fn flag_gated<F>(&mut self, mut f: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {
-        f(self.feature_gated_cfgs)
+
+    fn fold_interpolated(&mut self, nt: token::Nonterminal) -> token::Nonterminal {
+        // Don't configure interpolated AST (c.f. #34171).
+        // Interpolated AST will get configured once the surrounding tokens are parsed.
+        nt
     }
 }
 
-struct CfgDiagSilent {
-    error: bool,
+fn fold_expr(folder: &mut StripUnconfigured, expr: P<ast::Expr>) -> P<ast::Expr> {
+    expr.map(|ast::Expr {id, span, node, attrs}| {
+        fold::noop_fold_expr(ast::Expr {
+            id: id,
+            node: match node {
+                ast::ExprKind::Match(m, arms) => {
+                    ast::ExprKind::Match(m, arms.into_iter()
+                                        .filter_map(|a| folder.configure(a))
+                                        .collect())
+                }
+                _ => node
+            },
+            span: span,
+            attrs: attrs,
+        }, folder)
+    })
 }
 
-impl CfgDiag for CfgDiagSilent {
-    fn emit_error<F>(&mut self, _: F) where F: FnMut(&Handler) {
-        self.error = true;
-    }
-    fn flag_gated<F>(&mut self, _: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {}
+fn is_cfg(attr: &ast::Attribute) -> bool {
+    attr.check_name("cfg")
 }
 
-fn node_survives_cfg(attrs: &[ast::Attribute],
-                     config: &ast::CrateConfig) -> bool {
-    let mut survives_cfg = true;
-
-    for attr in attrs {
-        let mut fld = CfgAttrFolder {
-            diag: CfgDiagSilent { error: false },
-            config: config,
-        };
-        let attr = fld.fold_attribute(attr.clone());
-
-        // In case of error we can just return true,
-        // since the actual cfg folders will end compilation anyway.
-
-        if fld.diag.error { return true; }
-
-        survives_cfg &= attr.map(|attr| {
-            let mut diag = CfgDiagSilent { error: false };
-            let r = in_cfg(config, &[attr], &mut diag);
-            if diag.error { return true; }
-            r
-        }).unwrap_or(true)
-    }
-
-    survives_cfg
+fn is_test_or_bench(attr: &ast::Attribute) -> bool {
+    attr.check_name("test") || attr.check_name("bench")
 }
diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs
new file mode 100644 (file)
index 0000000..eb30657
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(non_snake_case)]
+
+// Error messages for EXXXX errors.
+// Each message should start and end with a new line, and be wrapped to 80 characters.
+// In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable.
+register_long_diagnostics! {
+
+E0533: r##"
+```compile_fail,E0533
+#[export_name]
+pub fn something() {}
+
+fn main() {}
+```
+"##,
+
+}
+
+register_diagnostics! {
+    E0534, // expected one argument
+    E0535, // invalid argument
+    E0536, // expected 1 cfg-pattern
+    E0537, // invalid predicate
+    E0538, // multiple [same] items
+    E0539, // incorrect meta item
+    E0540, // multiple rustc_deprecated attributes
+    E0541, // unknown meta item
+    E0542, // missing 'since'
+    E0543, // missing 'reason'
+    E0544, // multiple stability levels
+    E0545, // incorrect 'issue'
+    E0546, // missing 'feature'
+    E0547, // missing 'issue'
+    E0548, // incorrect stability attribute type
+    E0549, // rustc_deprecated attribute must be paired with either stable or unstable attribute
+    E0550, // multiple deprecated attributes
+    E0551, // incorrect meta item
+    E0552, // unrecognized representation hint
+    E0553, // unrecognized enum representation hint
+    E0554, // #[feature] may not be used on the [] release channel
+    E0555, // malformed feature attribute, expected #![feature(...)]
+    E0556, // malformed feature, expected just one word
+    E0557, // feature has been removed
+}
index 181b32594f1278d6a24ce365a0243ae277937b51..5bbd18bd9ee2e02f64af3b2f15f3ea092af48dbc 100644 (file)
@@ -20,7 +20,7 @@ use std::io::Write;
 use std::error::Error;
 use rustc_serialize::json::as_json;
 
-use codemap::Span;
+use syntax_pos::Span;
 use ext::base::ExtCtxt;
 use diagnostics::plugin::{ErrorMap, ErrorInfo};
 
index 26088b1242e2a326d3eac0ee476148a754d5ee5d..4e50299e836b3b71ef58be94ef9175bb342dc868 100644 (file)
@@ -13,16 +13,19 @@ use std::collections::BTreeMap;
 use std::env;
 
 use ast;
-use ast::{Ident, Name, TokenTree};
-use codemap::Span;
+use ast::{Ident, Name};
+use syntax_pos::Span;
 use ext::base::{ExtCtxt, MacEager, MacResult};
 use ext::build::AstBuilder;
 use parse::token;
 use ptr::P;
+use tokenstream::{TokenTree};
 use util::small_vector::SmallVector;
 
 use diagnostics::metadata::output_metadata;
 
+pub use errors::*;
+
 // Maximum width of any line in an extended error description (inclusive).
 const MAX_DESCRIPTION_WIDTH: usize = 80;
 
diff --git a/src/libsyntax/diagnostics/registry.rs b/src/libsyntax/diagnostics/registry.rs
deleted file mode 100644 (file)
index a6cfd1a..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use std::collections::HashMap;
-
-#[derive(Clone)]
-pub struct Registry {
-    descriptions: HashMap<&'static str, &'static str>
-}
-
-impl Registry {
-    pub fn new(descriptions: &[(&'static str, &'static str)]) -> Registry {
-        Registry { descriptions: descriptions.iter().cloned().collect() }
-    }
-
-    pub fn find_description(&self, code: &str) -> Option<&'static str> {
-        self.descriptions.get(code).cloned()
-    }
-}
diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs
deleted file mode 100644 (file)
index 7c9985d..0000000
+++ /dev/null
@@ -1,867 +0,0 @@
-// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use self::Destination::*;
-
-use codemap::{self, COMMAND_LINE_SP, DUMMY_SP, Span, MultiSpan};
-use diagnostics;
-
-use errors::check_old_skool;
-use errors::{Level, RenderSpan, CodeSuggestion, DiagnosticBuilder};
-use errors::RenderSpan::*;
-use errors::Level::*;
-use errors::snippet::{RenderedLineKind, SnippetData, Style};
-
-use std::{cmp, fmt};
-use std::io::prelude::*;
-use std::io;
-use std::rc::Rc;
-use term;
-
-/// Emitter trait for emitting errors. Do not implement this directly:
-/// implement `CoreEmitter` instead.
-pub trait Emitter {
-    /// Emit a standalone diagnostic message.
-    fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level);
-
-    /// Emit a structured diagnostic.
-    fn emit_struct(&mut self, db: &DiagnosticBuilder);
-}
-
-pub trait CoreEmitter {
-    fn emit_message(&mut self,
-                    rsp: &RenderSpan,
-                    msg: &str,
-                    code: Option<&str>,
-                    lvl: Level,
-                    is_header: bool,
-                    show_snippet: bool);
-}
-
-impl<T: CoreEmitter> Emitter for T {
-    fn emit(&mut self,
-            msp: &MultiSpan,
-            msg: &str,
-            code: Option<&str>,
-            lvl: Level) {
-        self.emit_message(&FullSpan(msp.clone()),
-                          msg,
-                          code,
-                          lvl,
-                          true,
-                          true);
-    }
-
-    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
-        let old_school = check_old_skool();
-        let db_span = FullSpan(db.span.clone());
-        self.emit_message(&FullSpan(db.span.clone()),
-                          &db.message,
-                          db.code.as_ref().map(|s| &**s),
-                          db.level,
-                          true,
-                          true);
-        for child in &db.children {
-            let render_span = child.render_span
-                                   .clone()
-                                   .unwrap_or_else(
-                                       || FullSpan(child.span.clone()));
-
-            if !old_school {
-                self.emit_message(&render_span,
-                                    &child.message,
-                                    None,
-                                    child.level,
-                                    false,
-                                    true);
-            } else {
-                let (render_span, show_snippet) = match render_span.span().primary_span() {
-                    None => (db_span.clone(), false),
-                    _ => (render_span, true)
-                };
-                self.emit_message(&render_span,
-                                    &child.message,
-                                    None,
-                                    child.level,
-                                    false,
-                                    show_snippet);
-            }
-        }
-    }
-}
-
-/// maximum number of lines we will print for each error; arbitrary.
-pub const MAX_HIGHLIGHT_LINES: usize = 6;
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum ColorConfig {
-    Auto,
-    Always,
-    Never,
-}
-
-impl ColorConfig {
-    fn use_color(&self) -> bool {
-        match *self {
-            ColorConfig::Always => true,
-            ColorConfig::Never  => false,
-            ColorConfig::Auto   => stderr_isatty(),
-        }
-    }
-}
-
-/// A basic emitter for when we don't have access to a codemap or registry. Used
-/// for reporting very early errors, etc.
-pub struct BasicEmitter {
-    dst: Destination,
-}
-
-impl CoreEmitter for BasicEmitter {
-    fn emit_message(&mut self,
-                    _rsp: &RenderSpan,
-                    msg: &str,
-                    code: Option<&str>,
-                    lvl: Level,
-                    _is_header: bool,
-                    _show_snippet: bool) {
-        // we ignore the span as we have no access to a codemap at this point
-        if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) {
-            panic!("failed to print diagnostics: {:?}", e);
-        }
-    }
-}
-
-impl BasicEmitter {
-    pub fn stderr(color_config: ColorConfig) -> BasicEmitter {
-        if color_config.use_color() {
-            let dst = Destination::from_stderr();
-            BasicEmitter { dst: dst }
-        } else {
-            BasicEmitter { dst: Raw(Box::new(io::stderr())) }
-        }
-    }
-}
-
-pub struct EmitterWriter {
-    dst: Destination,
-    registry: Option<diagnostics::registry::Registry>,
-    cm: Rc<codemap::CodeMap>,
-
-    /// Is this the first error emitted thus far? If not, we emit a
-    /// `\n` before the top-level errors.
-    first: bool,
-
-    // For now, allow an old-school mode while we transition
-    old_school: bool,
-}
-
-impl CoreEmitter for EmitterWriter {
-    fn emit_message(&mut self,
-                    rsp: &RenderSpan,
-                    msg: &str,
-                    code: Option<&str>,
-                    lvl: Level,
-                    is_header: bool,
-                    show_snippet: bool) {
-        match self.emit_message_(rsp, msg, code, lvl, is_header, show_snippet) {
-            Ok(()) => { }
-            Err(e) => panic!("failed to emit error: {}", e)
-        }
-    }
-}
-
-/// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See
-/// `EmitterWriter::print_maybe_styled` for details.
-macro_rules! print_maybe_styled {
-    ($dst: expr, $style: expr, $($arg: tt)*) => {
-        $dst.print_maybe_styled(format_args!($($arg)*), $style, false)
-    }
-}
-
-macro_rules! println_maybe_styled {
-    ($dst: expr, $style: expr, $($arg: tt)*) => {
-        $dst.print_maybe_styled(format_args!($($arg)*), $style, true)
-    }
-}
-
-impl EmitterWriter {
-    pub fn stderr(color_config: ColorConfig,
-                  registry: Option<diagnostics::registry::Registry>,
-                  code_map: Rc<codemap::CodeMap>)
-                  -> EmitterWriter {
-        let old_school = check_old_skool();
-        if color_config.use_color() {
-            let dst = Destination::from_stderr();
-            EmitterWriter { dst: dst,
-                            registry: registry,
-                            cm: code_map,
-                            first: true,
-                            old_school: old_school }
-        } else {
-            EmitterWriter { dst: Raw(Box::new(io::stderr())),
-                            registry: registry,
-                            cm: code_map,
-                            first: true,
-                            old_school: old_school }
-        }
-    }
-
-    pub fn new(dst: Box<Write + Send>,
-               registry: Option<diagnostics::registry::Registry>,
-               code_map: Rc<codemap::CodeMap>)
-               -> EmitterWriter {
-        let old_school = check_old_skool();
-        EmitterWriter { dst: Raw(dst),
-                        registry: registry,
-                        cm: code_map,
-                        first: true,
-                        old_school: old_school }
-    }
-
-    fn emit_message_(&mut self,
-                     rsp: &RenderSpan,
-                     msg: &str,
-                     code: Option<&str>,
-                     lvl: Level,
-                     is_header: bool,
-                     show_snippet: bool)
-                     -> io::Result<()> {
-        if is_header {
-            if self.first {
-                self.first = false;
-            } else {
-                if !self.old_school {
-                    write!(self.dst, "\n")?;
-                }
-            }
-        }
-
-        match code {
-            Some(code) if self.registry.as_ref()
-                                       .and_then(|registry| registry.find_description(code))
-                                       .is_some() => {
-                let code_with_explain = String::from("--explain ") + code;
-                if self.old_school {
-                    let loc = match rsp.span().primary_span() {
-                        Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(),
-                        Some(ps) => self.cm.span_to_string(ps),
-                        None => "".to_string()
-                    };
-                    print_diagnostic(&mut self.dst, &loc, lvl, msg, Some(code))?
-                }
-                else {
-                    print_diagnostic(&mut self.dst, "", lvl, msg, Some(&code_with_explain))?
-                }
-            }
-            _ => {
-                if self.old_school {
-                    let loc = match rsp.span().primary_span() {
-                        Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(),
-                        Some(ps) => self.cm.span_to_string(ps),
-                        None => "".to_string()
-                    };
-                    print_diagnostic(&mut self.dst, &loc, lvl, msg, code)?
-                }
-                else {
-                    print_diagnostic(&mut self.dst, "", lvl, msg, code)?
-                }
-            }
-        }
-
-        if !show_snippet {
-            return Ok(());
-        }
-
-        // Watch out for various nasty special spans; don't try to
-        // print any filename or anything for those.
-        match rsp.span().primary_span() {
-            Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => {
-                return Ok(());
-            }
-            _ => { }
-        }
-
-        // Otherwise, print out the snippet etc as needed.
-        match *rsp {
-            FullSpan(ref msp) => {
-                self.highlight_lines(msp, lvl)?;
-                if let Some(primary_span) = msp.primary_span() {
-                    self.print_macro_backtrace(primary_span)?;
-                }
-            }
-            Suggestion(ref suggestion) => {
-                self.highlight_suggestion(suggestion)?;
-                if let Some(primary_span) = rsp.span().primary_span() {
-                    self.print_macro_backtrace(primary_span)?;
-                }
-            }
-        }
-        if self.old_school {
-            match code {
-                Some(code) if self.registry.as_ref()
-                                        .and_then(|registry| registry.find_description(code))
-                                        .is_some() => {
-                    let loc = match rsp.span().primary_span() {
-                        Some(COMMAND_LINE_SP) | Some(DUMMY_SP) => "".to_string(),
-                        Some(ps) => self.cm.span_to_string(ps),
-                        None => "".to_string()
-                    };
-                    let msg = "run `rustc --explain ".to_string() + &code.to_string() +
-                        "` to see a detailed explanation";
-                    print_diagnostic(&mut self.dst, &loc, Level::Help, &msg,
-                        None)?
-                }
-                _ => ()
-            }
-        }
-        Ok(())
-    }
-
-    fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()>
-    {
-        let primary_span = suggestion.msp.primary_span().unwrap();
-        let lines = self.cm.span_to_lines(primary_span).unwrap();
-        assert!(!lines.lines.is_empty());
-
-        let complete = suggestion.splice_lines(&self.cm);
-        let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES);
-        let display_lines = &lines.lines[..line_count];
-
-        let fm = &*lines.file;
-        // Calculate the widest number to format evenly
-        let max_digits = line_num_max_digits(display_lines.last().unwrap());
-
-        // print the suggestion without any line numbers, but leave
-        // space for them. This helps with lining up with previous
-        // snippets from the actual error being reported.
-        let mut lines = complete.lines();
-        for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
-            write!(&mut self.dst, "{0}:{1:2$} {3}\n",
-                   fm.name, "", max_digits, line)?;
-        }
-
-        // if we elided some lines, add an ellipsis
-        if let Some(_) = lines.next() {
-            write!(&mut self.dst, "{0:1$} {0:2$} ...\n",
-                   "", fm.name.len(), max_digits)?;
-        }
-
-        Ok(())
-    }
-
-    fn highlight_lines(&mut self,
-                       msp: &MultiSpan,
-                       lvl: Level)
-                       -> io::Result<()>
-    {
-        let mut snippet_data = SnippetData::new(self.cm.clone(),
-                                                msp.primary_span());
-        if self.old_school {
-            let mut output_vec = vec![];
-
-            for span_label in msp.span_labels() {
-                let mut snippet_data = SnippetData::new(self.cm.clone(),
-                                                        Some(span_label.span));
-
-                snippet_data.push(span_label.span,
-                                  span_label.is_primary,
-                                  span_label.label);
-                if span_label.is_primary {
-                    output_vec.insert(0, snippet_data);
-                }
-                else {
-                    output_vec.push(snippet_data);
-                }
-            }
-
-            for snippet_data in output_vec.iter() {
-                let rendered_lines = snippet_data.render_lines();
-                for rendered_line in &rendered_lines {
-                    for styled_string in &rendered_line.text {
-                        self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?;
-                        write!(&mut self.dst, "{}", styled_string.text)?;
-                        self.dst.reset_attrs()?;
-                    }
-                    write!(&mut self.dst, "\n")?;
-                }
-            }
-        }
-        else {
-            for span_label in msp.span_labels() {
-                snippet_data.push(span_label.span,
-                                  span_label.is_primary,
-                                  span_label.label);
-            }
-            let rendered_lines = snippet_data.render_lines();
-            for rendered_line in &rendered_lines {
-                for styled_string in &rendered_line.text {
-                    self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?;
-                    write!(&mut self.dst, "{}", styled_string.text)?;
-                    self.dst.reset_attrs()?;
-                }
-                write!(&mut self.dst, "\n")?;
-            }
-        }
-        Ok(())
-    }
-
-    fn print_macro_backtrace(&mut self,
-                             sp: Span)
-                             -> io::Result<()> {
-        for trace in self.cm.macro_backtrace(sp) {
-            let mut diag_string =
-                format!("in this expansion of {}", trace.macro_decl_name);
-            if let Some(def_site_span) = trace.def_site_span {
-                diag_string.push_str(
-                    &format!(" (defined in {})",
-                        self.cm.span_to_filename(def_site_span)));
-            }
-            let snippet = self.cm.span_to_string(trace.call_site);
-            print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None)?;
-        }
-        Ok(())
-    }
-}
-
-fn line_num_max_digits(line: &codemap::LineInfo) -> usize {
-    let mut max_line_num = line.line_index + 1;
-    let mut digits = 0;
-    while max_line_num > 0 {
-        max_line_num /= 10;
-        digits += 1;
-    }
-    digits
-}
-
-fn print_diagnostic(dst: &mut Destination,
-                    topic: &str,
-                    lvl: Level,
-                    msg: &str,
-                    code: Option<&str>)
-                    -> io::Result<()> {
-    if !topic.is_empty() {
-        let old_school = check_old_skool();
-        if !old_school {
-            write!(dst, "{}: ", topic)?;
-        }
-        else {
-            write!(dst, "{} ", topic)?;
-        }
-        dst.reset_attrs()?;
-    }
-    dst.start_attr(term::Attr::Bold)?;
-    dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
-    write!(dst, "{}", lvl.to_string())?;
-    dst.reset_attrs()?;
-    write!(dst, ": ")?;
-    dst.start_attr(term::Attr::Bold)?;
-    write!(dst, "{}", msg)?;
-
-    if let Some(code) = code {
-        let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
-        print_maybe_styled!(dst, style, " [{}]", code.clone())?;
-    }
-
-    dst.reset_attrs()?;
-    write!(dst, "\n")?;
-    Ok(())
-}
-
-#[cfg(unix)]
-fn stderr_isatty() -> bool {
-    use libc;
-    unsafe { libc::isatty(libc::STDERR_FILENO) != 0 }
-}
-#[cfg(windows)]
-fn stderr_isatty() -> bool {
-    type DWORD = u32;
-    type BOOL = i32;
-    type HANDLE = *mut u8;
-    const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
-    extern "system" {
-        fn GetStdHandle(which: DWORD) -> HANDLE;
-        fn GetConsoleMode(hConsoleHandle: HANDLE,
-                          lpMode: *mut DWORD) -> BOOL;
-    }
-    unsafe {
-        let handle = GetStdHandle(STD_ERROR_HANDLE);
-        let mut out = 0;
-        GetConsoleMode(handle, &mut out) != 0
-    }
-}
-
-enum Destination {
-    Terminal(Box<term::StderrTerminal>),
-    Raw(Box<Write + Send>),
-}
-
-impl Destination {
-    fn from_stderr() -> Destination {
-        match term::stderr() {
-            Some(t) => Terminal(t),
-            None    => Raw(Box::new(io::stderr())),
-        }
-    }
-
-    fn apply_style(&mut self,
-                   lvl: Level,
-                   _kind: &RenderedLineKind,
-                   style: Style)
-                   -> io::Result<()> {
-        match style {
-            Style::FileNameStyle |
-            Style::LineAndColumn => {
-            }
-            Style::LineNumber => {
-                self.start_attr(term::Attr::Bold)?;
-                self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
-            }
-            Style::Quotation => {
-            }
-            Style::OldSkoolNote => {
-                self.start_attr(term::Attr::Bold)?;
-                self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_GREEN))?;
-            }
-            Style::OldSkoolNoteText => {
-                self.start_attr(term::Attr::Bold)?;
-            }
-            Style::UnderlinePrimary | Style::LabelPrimary => {
-                self.start_attr(term::Attr::Bold)?;
-                self.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
-            }
-            Style::UnderlineSecondary | Style::LabelSecondary => {
-                self.start_attr(term::Attr::Bold)?;
-                self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
-            }
-            Style::NoStyle => {
-            }
-        }
-        Ok(())
-    }
-
-    fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> {
-        match *self {
-            Terminal(ref mut t) => { t.attr(attr)?; }
-            Raw(_) => { }
-        }
-        Ok(())
-    }
-
-    fn reset_attrs(&mut self) -> io::Result<()> {
-        match *self {
-            Terminal(ref mut t) => { t.reset()?; }
-            Raw(_) => { }
-        }
-        Ok(())
-    }
-
-    fn print_maybe_styled(&mut self,
-                          args: fmt::Arguments,
-                          color: term::Attr,
-                          print_newline_at_end: bool)
-                          -> io::Result<()> {
-        match *self {
-            Terminal(ref mut t) => {
-                t.attr(color)?;
-                // If `msg` ends in a newline, we need to reset the color before
-                // the newline. We're making the assumption that we end up writing
-                // to a `LineBufferedWriter`, which means that emitting the reset
-                // after the newline ends up buffering the reset until we print
-                // another line or exit. Buffering the reset is a problem if we're
-                // sharing the terminal with any other programs (e.g. other rustc
-                // instances via `make -jN`).
-                //
-                // Note that if `msg` contains any internal newlines, this will
-                // result in the `LineBufferedWriter` flushing twice instead of
-                // once, which still leaves the opportunity for interleaved output
-                // to be miscolored. We assume this is rare enough that we don't
-                // have to worry about it.
-                t.write_fmt(args)?;
-                t.reset()?;
-                if print_newline_at_end {
-                    t.write_all(b"\n")
-                } else {
-                    Ok(())
-                }
-            }
-            Raw(ref mut w) => {
-                w.write_fmt(args)?;
-                if print_newline_at_end {
-                    w.write_all(b"\n")
-                } else {
-                    Ok(())
-                }
-            }
-        }
-    }
-}
-
-impl Write for Destination {
-    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
-        match *self {
-            Terminal(ref mut t) => t.write(bytes),
-            Raw(ref mut w) => w.write(bytes),
-        }
-    }
-    fn flush(&mut self) -> io::Result<()> {
-        match *self {
-            Terminal(ref mut t) => t.flush(),
-            Raw(ref mut w) => w.flush(),
-        }
-    }
-}
-
-
-#[cfg(test)]
-mod test {
-    use errors::{Level, CodeSuggestion};
-    use super::EmitterWriter;
-    use codemap::{mk_sp, CodeMap, Span, MultiSpan, BytePos, NO_EXPANSION};
-    use std::sync::{Arc, Mutex};
-    use std::io::{self, Write};
-    use std::str::from_utf8;
-    use std::rc::Rc;
-
-    struct Sink(Arc<Mutex<Vec<u8>>>);
-    impl Write for Sink {
-        fn write(&mut self, data: &[u8]) -> io::Result<usize> {
-            Write::write(&mut *self.0.lock().unwrap(), data)
-        }
-        fn flush(&mut self) -> io::Result<()> { Ok(()) }
-    }
-
-    /// Given a string like " ^~~~~~~~~~~~ ", produces a span
-    /// coverting that range. The idea is that the string has the same
-    /// length as the input, and we uncover the byte positions.  Note
-    /// that this can span lines and so on.
-    fn span_from_selection(input: &str, selection: &str) -> Span {
-        assert_eq!(input.len(), selection.len());
-        let left_index = selection.find('~').unwrap() as u32;
-        let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
-        Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION }
-    }
-
-    // Diagnostic doesn't align properly in span where line number increases by one digit
-    #[test]
-    fn test_hilight_suggestion_issue_11715() {
-        let data = Arc::new(Mutex::new(Vec::new()));
-        let cm = Rc::new(CodeMap::new());
-        let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
-        let content = "abcdefg
-        koksi
-        line3
-        line4
-        cinq
-        line6
-        line7
-        line8
-        line9
-        line10
-        e-lä-vän
-        tolv
-        dreizehn
-        ";
-        let file = cm.new_filemap_and_lines("dummy.txt", content);
-        let start = file.lines.borrow()[10];
-        let end = file.lines.borrow()[11];
-        let sp = mk_sp(start, end);
-        let lvl = Level::Error;
-        println!("highlight_lines");
-        ew.highlight_lines(&sp.into(), lvl).unwrap();
-        println!("done");
-        let vec = data.lock().unwrap().clone();
-        let vec: &[u8] = &vec;
-        let str = from_utf8(vec).unwrap();
-        println!("r#\"\n{}\"#", str);
-        assert_eq!(str, &r#"
-  --> dummy.txt:11:1
-11 |>         e-lä-vän
-   |> ^
-"#[1..]);
-    }
-
-    #[test]
-    fn test_single_span_splice() {
-        // Test that a `MultiSpan` containing a single span splices a substition correctly
-        let cm = CodeMap::new();
-        let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
-        let selection = "     \n    ~~\n~~~\n~~~~~     \n   \n";
-        cm.new_filemap_and_lines("blork.rs", inputtext);
-        let sp = span_from_selection(inputtext, selection);
-        let msp: MultiSpan = sp.into();
-
-        // check that we are extracting the text we thought we were extracting
-        assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD");
-
-        let substitute = "ZZZZZZ".to_owned();
-        let expected = "bbbbZZZZZZddddd";
-        let suggest = CodeSuggestion {
-            msp: msp,
-            substitutes: vec![substitute],
-        };
-        assert_eq!(suggest.splice_lines(&cm), expected);
-    }
-
-    #[test]
-    fn test_multi_span_splice() {
-        // Test that a `MultiSpan` containing multiple spans splices a substition correctly
-        let cm = CodeMap::new();
-        let inputtext  = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
-        let selection1 = "     \n      \n   \n          \n ~ \n"; // intentionally out of order
-        let selection2 = "     \n    ~~\n~~~\n~~~~~     \n   \n";
-        cm.new_filemap_and_lines("blork.rs", inputtext);
-        let sp1 = span_from_selection(inputtext, selection1);
-        let sp2 = span_from_selection(inputtext, selection2);
-        let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]);
-
-        let expected = "bbbbZZZZZZddddd\neXYZe";
-        let suggest = CodeSuggestion {
-            msp: msp,
-            substitutes: vec!["ZZZZZZ".to_owned(),
-                              "XYZ".to_owned()]
-        };
-
-        assert_eq!(suggest.splice_lines(&cm), expected);
-    }
-
-    #[test]
-    fn test_multispan_highlight() {
-        let data = Arc::new(Mutex::new(Vec::new()));
-        let cm = Rc::new(CodeMap::new());
-        let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
-
-        let inp =       "_____aaaaaa____bbbbbb__cccccdd_";
-        let sp1 =       "     ~~~~~~                    ";
-        let sp2 =       "               ~~~~~~          ";
-        let sp3 =       "                       ~~~~~   ";
-        let sp4 =       "                          ~~~~ ";
-        let sp34 =      "                       ~~~~~~~ ";
-
-        let expect_start = &r#"
- --> dummy.txt:1:6
-1 |> _____aaaaaa____bbbbbb__cccccdd_
-  |>      ^^^^^^    ^^^^^^  ^^^^^^^
-"#[1..];
-
-        let span = |sp, expected| {
-            let sp = span_from_selection(inp, sp);
-            assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected);
-            sp
-        };
-        cm.new_filemap_and_lines("dummy.txt", inp);
-        let sp1 = span(sp1, "aaaaaa");
-        let sp2 = span(sp2, "bbbbbb");
-        let sp3 = span(sp3, "ccccc");
-        let sp4 = span(sp4, "ccdd");
-        let sp34 = span(sp34, "cccccdd");
-
-        let spans = vec![sp1, sp2, sp3, sp4];
-
-        let test = |expected, highlight: &mut FnMut()| {
-            data.lock().unwrap().clear();
-            highlight();
-            let vec = data.lock().unwrap().clone();
-            let actual = from_utf8(&vec[..]).unwrap();
-            println!("actual=\n{}", actual);
-            assert_eq!(actual, expected);
-        };
-
-        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]);
-        test(expect_start, &mut || {
-            diag.highlight_lines(&msp, Level::Error).unwrap();
-        });
-        test(expect_start, &mut || {
-            let msp = MultiSpan::from_spans(spans.clone());
-            diag.highlight_lines(&msp, Level::Error).unwrap();
-        });
-    }
-
-    #[test]
-    fn test_huge_multispan_highlight() {
-        let data = Arc::new(Mutex::new(Vec::new()));
-        let cm = Rc::new(CodeMap::new());
-        let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
-
-        let inp = "aaaaa\n\
-                   aaaaa\n\
-                   aaaaa\n\
-                   bbbbb\n\
-                   ccccc\n\
-                   xxxxx\n\
-                   yyyyy\n\
-                   _____\n\
-                   ddd__eee_\n\
-                   elided\n\
-                   __f_gg";
-        let file = cm.new_filemap_and_lines("dummy.txt", inp);
-
-        let span = |lo, hi, (off_lo, off_hi)| {
-            let lines = file.lines.borrow();
-            let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]);
-            lo.0 += off_lo;
-            hi.0 += off_hi;
-            mk_sp(lo, hi)
-        };
-        let sp0 = span(4, 6, (0, 5));
-        let sp1 = span(0, 6, (0, 5));
-        let sp2 = span(8, 8, (0, 3));
-        let sp3 = span(8, 8, (5, 8));
-        let sp4 = span(10, 10, (2, 3));
-        let sp5 = span(10, 10, (4, 6));
-
-        let expect0 = &r#"
-   --> dummy.txt:5:1
-5   |> ccccc
-    |> ^
-...
-9   |> ddd__eee_
-    |> ^^^  ^^^
-10  |> elided
-11  |> __f_gg
-    |>   ^ ^^
-"#[1..];
-
-        let expect = &r#"
-   --> dummy.txt:1:1
-1   |> aaaaa
-    |> ^
-...
-9   |> ddd__eee_
-    |> ^^^  ^^^
-10  |> elided
-11  |> __f_gg
-    |>   ^ ^^
-"#[1..];
-
-        macro_rules! test {
-            ($expected: expr, $highlight: expr) => ({
-                data.lock().unwrap().clear();
-                $highlight();
-                let vec = data.lock().unwrap().clone();
-                let actual = from_utf8(&vec[..]).unwrap();
-                println!("actual:");
-                println!("{}", actual);
-                println!("expected:");
-                println!("{}", $expected);
-                assert_eq!(&actual[..], &$expected[..]);
-            });
-        }
-
-        let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]);
-        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]);
-
-        test!(expect0, || {
-            diag.highlight_lines(&msp0, Level::Error).unwrap();
-        });
-        test!(expect, || {
-            diag.highlight_lines(&msp, Level::Error).unwrap();
-        });
-    }
-}
diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs
deleted file mode 100644 (file)
index 93c6268..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! A JSON emitter for errors.
-//!
-//! This works by converting errors to a simplified structural format (see the
-//! structs at the start of the file) and then serialising them. These should
-//! contain as much information about the error as possible.
-//!
-//! The format of the JSON output should be considered *unstable*. For now the
-//! structs at the end of this file (Diagnostic*) specify the error format.
-
-// FIXME spec the JSON output properly.
-
-
-use codemap::{self, MacroBacktrace, Span, SpanLabel, MultiSpan, CodeMap};
-use diagnostics::registry::Registry;
-use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion};
-use errors::emitter::Emitter;
-
-use std::rc::Rc;
-use std::io::{self, Write};
-use std::vec;
-
-use rustc_serialize::json::as_json;
-
-pub struct JsonEmitter {
-    dst: Box<Write + Send>,
-    registry: Option<Registry>,
-    cm: Rc<CodeMap>,
-}
-
-impl JsonEmitter {
-    pub fn basic() -> JsonEmitter {
-        JsonEmitter::stderr(None, Rc::new(CodeMap::new()))
-    }
-
-    pub fn stderr(registry: Option<Registry>,
-                  code_map: Rc<CodeMap>) -> JsonEmitter {
-        JsonEmitter {
-            dst: Box::new(io::stderr()),
-            registry: registry,
-            cm: code_map,
-        }
-    }
-}
-
-impl Emitter for JsonEmitter {
-    fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, level: Level) {
-        let data = Diagnostic::new(span, msg, code, level, self);
-        if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
-            panic!("failed to print diagnostics: {:?}", e);
-        }
-    }
-
-    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
-        let data = Diagnostic::from_diagnostic_builder(db, self);
-        if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
-            panic!("failed to print diagnostics: {:?}", e);
-        }
-    }
-}
-
-// The following data types are provided just for serialisation.
-
-#[derive(RustcEncodable)]
-struct Diagnostic<'a> {
-    /// The primary error message.
-    message: &'a str,
-    code: Option<DiagnosticCode>,
-    /// "error: internal compiler error", "error", "warning", "note", "help".
-    level: &'static str,
-    spans: Vec<DiagnosticSpan>,
-    /// Associated diagnostic messages.
-    children: Vec<Diagnostic<'a>>,
-    /// The message as rustc would render it. Currently this is only
-    /// `Some` for "suggestions", but eventually it will include all
-    /// snippets.
-    rendered: Option<String>,
-}
-
-#[derive(RustcEncodable)]
-struct DiagnosticSpan {
-    file_name: String,
-    byte_start: u32,
-    byte_end: u32,
-    /// 1-based.
-    line_start: usize,
-    line_end: usize,
-    /// 1-based, character offset.
-    column_start: usize,
-    column_end: usize,
-    /// Is this a "primary" span -- meaning the point, or one of the points,
-    /// where the error occurred?
-    is_primary: bool,
-    /// Source text from the start of line_start to the end of line_end.
-    text: Vec<DiagnosticSpanLine>,
-    /// Label that should be placed at this location (if any)
-    label: Option<String>,
-    /// If we are suggesting a replacement, this will contain text
-    /// that should be sliced in atop this span. You may prefer to
-    /// load the fully rendered version from the parent `Diagnostic`,
-    /// however.
-    suggested_replacement: Option<String>,
-    /// Macro invocations that created the code at this span, if any.
-    expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
-}
-
-#[derive(RustcEncodable)]
-struct DiagnosticSpanLine {
-    text: String,
-
-    /// 1-based, character offset in self.text.
-    highlight_start: usize,
-
-    highlight_end: usize,
-}
-
-#[derive(RustcEncodable)]
-struct DiagnosticSpanMacroExpansion {
-    /// span where macro was applied to generate this code; note that
-    /// this may itself derive from a macro (if
-    /// `span.expansion.is_some()`)
-    span: DiagnosticSpan,
-
-    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
-    macro_decl_name: String,
-
-    /// span where macro was defined (if known)
-    def_site_span: Option<DiagnosticSpan>,
-}
-
-#[derive(RustcEncodable)]
-struct DiagnosticCode {
-    /// The code itself.
-    code: String,
-    /// An explanation for the code.
-    explanation: Option<&'static str>,
-}
-
-impl<'a> Diagnostic<'a> {
-    fn new(msp: &MultiSpan,
-           msg: &'a str,
-           code: Option<&str>,
-           level: Level,
-           je: &JsonEmitter)
-           -> Diagnostic<'a> {
-        Diagnostic {
-            message: msg,
-            code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je),
-            level: level.to_str(),
-            spans: DiagnosticSpan::from_multispan(msp, je),
-            children: vec![],
-            rendered: None,
-        }
-    }
-
-    fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
-                                   je: &JsonEmitter)
-                                   -> Diagnostic<'c> {
-        Diagnostic {
-            message: &db.message,
-            code: DiagnosticCode::map_opt_string(db.code.clone(), je),
-            level: db.level.to_str(),
-            spans: DiagnosticSpan::from_multispan(&db.span, je),
-            children: db.children.iter().map(|c| {
-                Diagnostic::from_sub_diagnostic(c, je)
-            }).collect(),
-            rendered: None,
-        }
-    }
-
-    fn from_sub_diagnostic<'c>(db: &'c SubDiagnostic, je: &JsonEmitter) -> Diagnostic<'c> {
-        Diagnostic {
-            message: &db.message,
-            code: None,
-            level: db.level.to_str(),
-            spans: db.render_span.as_ref()
-                     .map(|sp| DiagnosticSpan::from_render_span(sp, je))
-                     .unwrap_or_else(|| DiagnosticSpan::from_multispan(&db.span, je)),
-            children: vec![],
-            rendered: db.render_span.as_ref()
-                                    .and_then(|rsp| je.render(rsp)),
-        }
-    }
-}
-
-impl DiagnosticSpan {
-    fn from_span_label(span: SpanLabel,
-                       suggestion: Option<&String>,
-                       je: &JsonEmitter)
-                       -> DiagnosticSpan {
-        Self::from_span_etc(span.span,
-                            span.is_primary,
-                            span.label,
-                            suggestion,
-                            je)
-    }
-
-    fn from_span_etc(span: Span,
-                     is_primary: bool,
-                     label: Option<String>,
-                     suggestion: Option<&String>,
-                     je: &JsonEmitter)
-                     -> DiagnosticSpan {
-        // obtain the full backtrace from the `macro_backtrace`
-        // helper; in some ways, it'd be better to expand the
-        // backtrace ourselves, but the `macro_backtrace` helper makes
-        // some decision, such as dropping some frames, and I don't
-        // want to duplicate that logic here.
-        let backtrace = je.cm.macro_backtrace(span).into_iter();
-        DiagnosticSpan::from_span_full(span,
-                                       is_primary,
-                                       label,
-                                       suggestion,
-                                       backtrace,
-                                       je)
-    }
-
-    fn from_span_full(span: Span,
-                      is_primary: bool,
-                      label: Option<String>,
-                      suggestion: Option<&String>,
-                      mut backtrace: vec::IntoIter<MacroBacktrace>,
-                      je: &JsonEmitter)
-                      -> DiagnosticSpan {
-        let start = je.cm.lookup_char_pos(span.lo);
-        let end = je.cm.lookup_char_pos(span.hi);
-        let backtrace_step = backtrace.next().map(|bt| {
-            let call_site =
-                Self::from_span_full(bt.call_site,
-                                     false,
-                                     None,
-                                     None,
-                                     backtrace,
-                                     je);
-            let def_site_span = bt.def_site_span.map(|sp| {
-                Self::from_span_full(sp,
-                                     false,
-                                     None,
-                                     None,
-                                     vec![].into_iter(),
-                                     je)
-            });
-            Box::new(DiagnosticSpanMacroExpansion {
-                span: call_site,
-                macro_decl_name: bt.macro_decl_name,
-                def_site_span: def_site_span,
-            })
-        });
-        DiagnosticSpan {
-            file_name: start.file.name.clone(),
-            byte_start: span.lo.0,
-            byte_end: span.hi.0,
-            line_start: start.line,
-            line_end: end.line,
-            column_start: start.col.0 + 1,
-            column_end: end.col.0 + 1,
-            is_primary: is_primary,
-            text: DiagnosticSpanLine::from_span(span, je),
-            suggested_replacement: suggestion.cloned(),
-            expansion: backtrace_step,
-            label: label,
-        }
-    }
-
-    fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
-        msp.span_labels()
-           .into_iter()
-           .map(|span_str| Self::from_span_label(span_str, None, je))
-           .collect()
-    }
-
-    fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter)
-                       -> Vec<DiagnosticSpan> {
-        assert_eq!(suggestion.msp.span_labels().len(), suggestion.substitutes.len());
-        suggestion.msp.span_labels()
-                      .into_iter()
-                      .zip(&suggestion.substitutes)
-                      .map(|(span_label, suggestion)| {
-                          DiagnosticSpan::from_span_label(span_label,
-                                                          Some(suggestion),
-                                                          je)
-                      })
-                      .collect()
-    }
-
-    fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
-        match *rsp {
-            RenderSpan::FullSpan(ref msp) =>
-                DiagnosticSpan::from_multispan(msp, je),
-            RenderSpan::Suggestion(ref suggestion) =>
-                DiagnosticSpan::from_suggestion(suggestion, je),
-        }
-    }
-}
-
-impl DiagnosticSpanLine {
-    fn line_from_filemap(fm: &codemap::FileMap,
-                         index: usize,
-                         h_start: usize,
-                         h_end: usize)
-                         -> DiagnosticSpanLine {
-        DiagnosticSpanLine {
-            text: fm.get_line(index).unwrap().to_owned(),
-            highlight_start: h_start,
-            highlight_end: h_end,
-        }
-    }
-
-    /// Create a list of DiagnosticSpanLines from span - each line with any part
-    /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the
-    /// `span` within the line.
-    fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
-        je.cm.span_to_lines(span)
-             .map(|lines| {
-                 let fm = &*lines.file;
-                 lines.lines
-                      .iter()
-                      .map(|line| {
-                          DiagnosticSpanLine::line_from_filemap(fm,
-                                                                line.line_index,
-                                                                line.start_col.0 + 1,
-                                                                line.end_col.0 + 1)
-                      })
-                     .collect()
-             })
-            .unwrap_or(vec![])
-    }
-}
-
-impl DiagnosticCode {
-    fn map_opt_string(s: Option<String>, je: &JsonEmitter) -> Option<DiagnosticCode> {
-        s.map(|s| {
-
-            let explanation = je.registry
-                                .as_ref()
-                                .and_then(|registry| registry.find_description(&s));
-
-            DiagnosticCode {
-                code: s,
-                explanation: explanation,
-            }
-        })
-    }
-}
-
-impl JsonEmitter {
-    fn render(&self, render_span: &RenderSpan) -> Option<String> {
-        match *render_span {
-            RenderSpan::FullSpan(_) => {
-                None
-            }
-            RenderSpan::Suggestion(ref suggestion) => {
-                Some(suggestion.splice_lines(&self.cm))
-            }
-        }
-    }
-}
-
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
deleted file mode 100644 (file)
index f06672f..0000000
+++ /dev/null
@@ -1,711 +0,0 @@
-// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-pub use errors::emitter::ColorConfig;
-
-use self::Level::*;
-use self::RenderSpan::*;
-
-use codemap::{self, CodeMap, MultiSpan, NO_EXPANSION, Span};
-use diagnostics;
-use errors::emitter::{Emitter, EmitterWriter};
-
-use std::cell::{RefCell, Cell};
-use std::{error, fmt};
-use std::rc::Rc;
-use std::thread::panicking;
-use term;
-
-pub mod emitter;
-pub mod json;
-pub mod snippet;
-
-#[derive(Clone)]
-pub enum RenderSpan {
-    /// A FullSpan renders with both with an initial line for the
-    /// message, prefixed by file:linenum, followed by a summary of
-    /// the source code covered by the span.
-    FullSpan(MultiSpan),
-
-    /// A suggestion renders with both with an initial line for the
-    /// message, prefixed by file:linenum, followed by a summary
-    /// of hypothetical source code, where each `String` is spliced
-    /// into the lines in place of the code covered by each span.
-    Suggestion(CodeSuggestion),
-}
-
-#[derive(Clone)]
-pub struct CodeSuggestion {
-    msp: MultiSpan,
-    substitutes: Vec<String>,
-}
-
-impl RenderSpan {
-    fn span(&self) -> &MultiSpan {
-        match *self {
-            FullSpan(ref msp) |
-            Suggestion(CodeSuggestion { ref msp, .. }) =>
-                msp
-        }
-    }
-}
-
-impl CodeSuggestion {
-    /// Returns the assembled code suggestion.
-    pub fn splice_lines(&self, cm: &CodeMap) -> String {
-        use codemap::{CharPos, Loc, Pos};
-
-        fn push_trailing(buf: &mut String, line_opt: Option<&str>,
-                         lo: &Loc, hi_opt: Option<&Loc>) {
-            let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi|hi.col.to_usize()));
-            if let Some(line) = line_opt {
-                if line.len() > lo {
-                    buf.push_str(match hi_opt {
-                        Some(hi) => &line[lo..hi],
-                        None => &line[lo..],
-                    });
-                }
-                if let None = hi_opt {
-                    buf.push('\n');
-                }
-            }
-        }
-
-        let mut primary_spans = self.msp.primary_spans().to_owned();
-
-        assert_eq!(primary_spans.len(), self.substitutes.len());
-        if primary_spans.is_empty() {
-            return format!("");
-        }
-
-        // Assumption: all spans are in the same file, and all spans
-        // are disjoint. Sort in ascending order.
-        primary_spans.sort_by_key(|sp| sp.lo);
-
-        // Find the bounding span.
-        let lo = primary_spans.iter().map(|sp| sp.lo).min().unwrap();
-        let hi = primary_spans.iter().map(|sp| sp.hi).min().unwrap();
-        let bounding_span = Span { lo: lo, hi: hi, expn_id: NO_EXPANSION };
-        let lines = cm.span_to_lines(bounding_span).unwrap();
-        assert!(!lines.lines.is_empty());
-
-        // To build up the result, we do this for each span:
-        // - push the line segment trailing the previous span
-        //   (at the beginning a "phantom" span pointing at the start of the line)
-        // - push lines between the previous and current span (if any)
-        // - if the previous and current span are not on the same line
-        //   push the line segment leading up to the current span
-        // - splice in the span substitution
-        //
-        // Finally push the trailing line segment of the last span
-        let fm = &lines.file;
-        let mut prev_hi = cm.lookup_char_pos(bounding_span.lo);
-        prev_hi.col = CharPos::from_usize(0);
-
-        let mut prev_line = fm.get_line(lines.lines[0].line_index);
-        let mut buf = String::new();
-
-        for (sp, substitute) in primary_spans.iter().zip(self.substitutes.iter()) {
-            let cur_lo = cm.lookup_char_pos(sp.lo);
-            if prev_hi.line == cur_lo.line {
-                push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo));
-            } else {
-                push_trailing(&mut buf, prev_line, &prev_hi, None);
-                // push lines between the previous and current span (if any)
-                for idx in prev_hi.line..(cur_lo.line - 1) {
-                    if let Some(line) = fm.get_line(idx) {
-                        buf.push_str(line);
-                        buf.push('\n');
-                    }
-                }
-                if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
-                    buf.push_str(&cur_line[.. cur_lo.col.to_usize()]);
-                }
-            }
-            buf.push_str(substitute);
-            prev_hi = cm.lookup_char_pos(sp.hi);
-            prev_line = fm.get_line(prev_hi.line - 1);
-        }
-        push_trailing(&mut buf, prev_line, &prev_hi, None);
-        // remove trailing newline
-        buf.pop();
-        buf
-    }
-}
-
-/// Used as a return value to signify a fatal error occurred. (It is also
-/// used as the argument to panic at the moment, but that will eventually
-/// not be true.)
-#[derive(Copy, Clone, Debug)]
-#[must_use]
-pub struct FatalError;
-
-impl fmt::Display for FatalError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        write!(f, "parser fatal error")
-    }
-}
-
-impl error::Error for FatalError {
-    fn description(&self) -> &str {
-        "The parser has encountered a fatal error"
-    }
-}
-
-/// Signifies that the compiler died with an explicit call to `.bug`
-/// or `.span_bug` rather than a failed assertion, etc.
-#[derive(Copy, Clone, Debug)]
-pub struct ExplicitBug;
-
-impl fmt::Display for ExplicitBug {
-    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        write!(f, "parser internal bug")
-    }
-}
-
-impl error::Error for ExplicitBug {
-    fn description(&self) -> &str {
-        "The parser has encountered an internal bug"
-    }
-}
-
-/// Used for emitting structured error messages and other diagnostic information.
-#[must_use]
-#[derive(Clone)]
-pub struct DiagnosticBuilder<'a> {
-    handler: &'a Handler,
-    level: Level,
-    message: String,
-    code: Option<String>,
-    span: MultiSpan,
-    children: Vec<SubDiagnostic>,
-}
-
-/// For example a note attached to an error.
-#[derive(Clone)]
-struct SubDiagnostic {
-    level: Level,
-    message: String,
-    span: MultiSpan,
-    render_span: Option<RenderSpan>,
-}
-
-impl<'a> DiagnosticBuilder<'a> {
-    /// Emit the diagnostic.
-    pub fn emit(&mut self) {
-        if self.cancelled() {
-            return;
-        }
-
-        self.handler.emit.borrow_mut().emit_struct(&self);
-        self.cancel();
-        self.handler.panic_if_treat_err_as_bug();
-
-        // if self.is_fatal() {
-        //     panic!(FatalError);
-        // }
-    }
-
-    /// Cancel the diagnostic (a structured diagnostic must either be emitted or
-    /// cancelled or it will panic when dropped).
-    /// BEWARE: if this DiagnosticBuilder is an error, then creating it will
-    /// bump the error count on the Handler and cancelling it won't undo that.
-    /// If you want to decrement the error count you should use `Handler::cancel`.
-    pub fn cancel(&mut self) {
-        self.level = Level::Cancelled;
-    }
-
-    pub fn cancelled(&self) -> bool {
-        self.level == Level::Cancelled
-    }
-
-    pub fn is_fatal(&self) -> bool {
-        self.level == Level::Fatal
-    }
-
-    /// Add a span/label to be included in the resulting snippet.
-    /// This is pushed onto the `MultiSpan` that was created when the
-    /// diagnostic was first built. If you don't call this function at
-    /// all, and you just supplied a `Span` to create the diagnostic,
-    /// then the snippet will just include that `Span`, which is
-    /// called the primary span.
-    pub fn span_label(&mut self, span: Span, label: &fmt::Display)
-                      -> &mut DiagnosticBuilder<'a> {
-        self.span.push_span_label(span, format!("{}", label));
-        self
-    }
-
-    pub fn note_expected_found(&mut self,
-                               label: &fmt::Display,
-                               expected: &fmt::Display,
-                               found: &fmt::Display)
-                               -> &mut DiagnosticBuilder<'a>
-    {
-        // For now, just attach these as notes
-        self.note(&format!("expected {} `{}`", label, expected));
-        self.note(&format!("   found {} `{}`", label, found));
-        self
-    }
-
-    pub fn note(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Note, msg, MultiSpan::new(), None);
-        self
-    }
-    pub fn span_note<S: Into<MultiSpan>>(&mut self,
-                                         sp: S,
-                                         msg: &str)
-                                         -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Note, msg, sp.into(), None);
-        self
-    }
-    pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Warning, msg, MultiSpan::new(), None);
-        self
-    }
-    pub fn span_warn<S: Into<MultiSpan>>(&mut self,
-                                         sp: S,
-                                         msg: &str)
-                                         -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Warning, msg, sp.into(), None);
-        self
-    }
-    pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Help, msg, MultiSpan::new(), None);
-        self
-    }
-    pub fn span_help<S: Into<MultiSpan>>(&mut self,
-                                         sp: S,
-                                         msg: &str)
-                                         -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Help, msg, sp.into(), None);
-        self
-    }
-    /// Prints out a message with a suggested edit of the code.
-    ///
-    /// See `diagnostic::RenderSpan::Suggestion` for more information.
-    pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
-                                               sp: S,
-                                               msg: &str,
-                                               suggestion: String)
-                                               -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Help, msg, MultiSpan::new(), Some(Suggestion(CodeSuggestion {
-            msp: sp.into(),
-            substitutes: vec![suggestion],
-        })));
-        self
-    }
-
-    pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
-        self.span = sp.into();
-        self
-    }
-
-    pub fn code(&mut self, s: String) -> &mut Self {
-        self.code = Some(s);
-        self
-    }
-
-    pub fn message(&self) -> &str {
-        &self.message
-    }
-
-    pub fn level(&self) -> Level {
-        self.level
-    }
-
-    /// Convenience function for internal use, clients should use one of the
-    /// struct_* methods on Handler.
-    fn new(handler: &'a Handler,
-           level: Level,
-           message: &str) -> DiagnosticBuilder<'a> {
-        DiagnosticBuilder {
-            handler: handler,
-            level: level,
-            message: message.to_owned(),
-            code: None,
-            span: MultiSpan::new(),
-            children: vec![],
-        }
-    }
-
-    /// Convenience function for internal use, clients should use one of the
-    /// public methods above.
-    fn sub(&mut self,
-           level: Level,
-           message: &str,
-           span: MultiSpan,
-           render_span: Option<RenderSpan>) {
-        let sub = SubDiagnostic {
-            level: level,
-            message: message.to_owned(),
-            span: span,
-            render_span: render_span,
-        };
-        self.children.push(sub);
-    }
-}
-
-impl<'a> fmt::Debug for DiagnosticBuilder<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        self.message.fmt(f)
-    }
-}
-
-/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or
-/// we emit a bug.
-impl<'a> Drop for DiagnosticBuilder<'a> {
-    fn drop(&mut self) {
-        if !panicking() && !self.cancelled() {
-            self.handler.emit.borrow_mut().emit(&MultiSpan::new(),
-                                                "Error constructed but not emitted",
-                                                None,
-                                                Bug);
-            panic!();
-        }
-    }
-}
-
-/// A handler deals with errors; certain errors
-/// (fatal, bug, unimpl) may cause immediate exit,
-/// others log errors for later reporting.
-pub struct Handler {
-    err_count: Cell<usize>,
-    emit: RefCell<Box<Emitter>>,
-    pub can_emit_warnings: bool,
-    treat_err_as_bug: bool,
-    continue_after_error: Cell<bool>,
-    delayed_span_bug: RefCell<Option<(MultiSpan, String)>>,
-}
-
-impl Handler {
-    pub fn with_tty_emitter(color_config: ColorConfig,
-                            registry: Option<diagnostics::registry::Registry>,
-                            can_emit_warnings: bool,
-                            treat_err_as_bug: bool,
-                            cm: Rc<codemap::CodeMap>)
-                            -> Handler {
-        let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm));
-        Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter)
-    }
-
-    pub fn with_emitter(can_emit_warnings: bool,
-                        treat_err_as_bug: bool,
-                        e: Box<Emitter>) -> Handler {
-        Handler {
-            err_count: Cell::new(0),
-            emit: RefCell::new(e),
-            can_emit_warnings: can_emit_warnings,
-            treat_err_as_bug: treat_err_as_bug,
-            continue_after_error: Cell::new(true),
-            delayed_span_bug: RefCell::new(None),
-        }
-    }
-
-    pub fn set_continue_after_error(&self, continue_after_error: bool) {
-        self.continue_after_error.set(continue_after_error);
-    }
-
-    pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> {
-        DiagnosticBuilder::new(self, Level::Cancelled, "")
-    }
-
-    pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self,
-                                                    sp: S,
-                                                    msg: &str)
-                                                    -> DiagnosticBuilder<'a> {
-        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
-        result.set_span(sp);
-        if !self.can_emit_warnings {
-            result.cancel();
-        }
-        result
-    }
-    pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(&'a self,
-                                                              sp: S,
-                                                              msg: &str,
-                                                              code: &str)
-                                                              -> DiagnosticBuilder<'a> {
-        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
-        result.set_span(sp);
-        result.code(code.to_owned());
-        if !self.can_emit_warnings {
-            result.cancel();
-        }
-        result
-    }
-    pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
-        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
-        if !self.can_emit_warnings {
-            result.cancel();
-        }
-        result
-    }
-    pub fn struct_span_err<'a, S: Into<MultiSpan>>(&'a self,
-                                                   sp: S,
-                                                   msg: &str)
-                                                   -> DiagnosticBuilder<'a> {
-        self.bump_err_count();
-        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
-        result.set_span(sp);
-        result
-    }
-    pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
-                                                             sp: S,
-                                                             msg: &str,
-                                                             code: &str)
-                                                             -> DiagnosticBuilder<'a> {
-        self.bump_err_count();
-        let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
-        result.set_span(sp);
-        result.code(code.to_owned());
-        result
-    }
-    pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
-        self.bump_err_count();
-        DiagnosticBuilder::new(self, Level::Error, msg)
-    }
-    pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(&'a self,
-                                                     sp: S,
-                                                     msg: &str)
-                                                     -> DiagnosticBuilder<'a> {
-        self.bump_err_count();
-        let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
-        result.set_span(sp);
-        result
-    }
-    pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self,
-                                                               sp: S,
-                                                               msg: &str,
-                                                               code: &str)
-                                                               -> DiagnosticBuilder<'a> {
-        self.bump_err_count();
-        let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
-        result.set_span(sp);
-        result.code(code.to_owned());
-        result
-    }
-    pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
-        self.bump_err_count();
-        DiagnosticBuilder::new(self, Level::Fatal, msg)
-    }
-
-    pub fn cancel(&mut self, err: &mut DiagnosticBuilder) {
-        if err.level == Level::Error || err.level == Level::Fatal {
-            assert!(self.has_errors());
-            self.err_count.set(self.err_count.get() + 1);
-        }
-        err.cancel();
-    }
-
-    fn panic_if_treat_err_as_bug(&self) {
-        if self.treat_err_as_bug {
-            panic!("encountered error with `-Z treat_err_as_bug");
-        }
-    }
-
-    pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str)
-                                          -> FatalError {
-        self.emit(&sp.into(), msg, Fatal);
-        self.bump_err_count();
-        self.panic_if_treat_err_as_bug();
-        return FatalError;
-    }
-    pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str)
-                                                    -> FatalError {
-        self.emit_with_code(&sp.into(), msg, code, Fatal);
-        self.bump_err_count();
-        self.panic_if_treat_err_as_bug();
-        return FatalError;
-    }
-    pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        self.emit(&sp.into(), msg, Error);
-        self.bump_err_count();
-        self.panic_if_treat_err_as_bug();
-    }
-    pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
-        self.emit_with_code(&sp.into(), msg, code, Error);
-        self.bump_err_count();
-        self.panic_if_treat_err_as_bug();
-    }
-    pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        self.emit(&sp.into(), msg, Warning);
-    }
-    pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
-        self.emit_with_code(&sp.into(), msg, code, Warning);
-    }
-    pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
-        self.emit(&sp.into(), msg, Bug);
-        panic!(ExplicitBug);
-    }
-    pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        let mut delayed = self.delayed_span_bug.borrow_mut();
-        *delayed = Some((sp.into(), msg.to_string()));
-    }
-    pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        self.emit(&sp.into(), msg, Bug);
-        self.bump_err_count();
-    }
-    pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        self.emit.borrow_mut().emit(&sp.into(), msg, None, Note);
-    }
-    pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
-        self.span_bug(sp, &format!("unimplemented {}", msg));
-    }
-    pub fn fatal(&self, msg: &str) -> FatalError {
-        if self.treat_err_as_bug {
-            self.bug(msg);
-        }
-        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Fatal);
-        self.bump_err_count();
-        FatalError
-    }
-    pub fn err(&self, msg: &str) {
-        if self.treat_err_as_bug {
-            self.bug(msg);
-        }
-        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Error);
-        self.bump_err_count();
-    }
-    pub fn warn(&self, msg: &str) {
-        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Warning);
-    }
-    pub fn note_without_error(&self, msg: &str) {
-        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Note);
-    }
-    pub fn bug(&self, msg: &str) -> ! {
-        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Bug);
-        panic!(ExplicitBug);
-    }
-    pub fn unimpl(&self, msg: &str) -> ! {
-        self.bug(&format!("unimplemented {}", msg));
-    }
-
-    pub fn bump_err_count(&self) {
-        self.err_count.set(self.err_count.get() + 1);
-    }
-
-    pub fn err_count(&self) -> usize {
-        self.err_count.get()
-    }
-
-    pub fn has_errors(&self) -> bool {
-        self.err_count.get() > 0
-    }
-    pub fn abort_if_errors(&self) {
-        let s;
-        match self.err_count.get() {
-            0 => {
-                let delayed_bug = self.delayed_span_bug.borrow();
-                match *delayed_bug {
-                    Some((ref span, ref errmsg)) => {
-                        self.span_bug(span.clone(), errmsg);
-                    },
-                    _ => {}
-                }
-
-                return;
-            }
-            1 => s = "aborting due to previous error".to_string(),
-            _  => {
-                s = format!("aborting due to {} previous errors",
-                            self.err_count.get());
-            }
-        }
-
-        panic!(self.fatal(&s));
-    }
-    pub fn emit(&self,
-                msp: &MultiSpan,
-                msg: &str,
-                lvl: Level) {
-        if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().emit(&msp, msg, None, lvl);
-        if !self.continue_after_error.get() { self.abort_if_errors(); }
-    }
-    pub fn emit_with_code(&self,
-                          msp: &MultiSpan,
-                          msg: &str,
-                          code: &str,
-                          lvl: Level) {
-        if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().emit(&msp, msg, Some(code), lvl);
-        if !self.continue_after_error.get() { self.abort_if_errors(); }
-    }
-}
-
-
-#[derive(Copy, PartialEq, Clone, Debug)]
-pub enum Level {
-    Bug,
-    Fatal,
-    // An error which while not immediately fatal, should stop the compiler
-    // progressing beyond the current phase.
-    PhaseFatal,
-    Error,
-    Warning,
-    Note,
-    Help,
-    Cancelled,
-}
-
-impl fmt::Display for Level {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        self.to_str().fmt(f)
-    }
-}
-
-impl Level {
-    fn color(self) -> term::color::Color {
-        match self {
-            Bug | Fatal | PhaseFatal | Error => term::color::BRIGHT_RED,
-            Warning => term::color::YELLOW,
-            Note => term::color::BRIGHT_GREEN,
-            Help => term::color::BRIGHT_CYAN,
-            Cancelled => unreachable!(),
-        }
-    }
-
-    fn to_str(self) -> &'static str {
-        match self {
-            Bug => "error: internal compiler error",
-            Fatal | PhaseFatal | Error => "error",
-            Warning => "warning",
-            Note => "note",
-            Help => "help",
-            Cancelled => panic!("Shouldn't call on cancelled error"),
-        }
-    }
-}
-
-pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where
-    M: FnOnce() -> String,
-{
-    match opt {
-        Some(t) => t,
-        None => diag.bug(&msg()),
-    }
-}
-
-/// True if we should use the old-skool error format style. This is
-/// the default setting until the new errors are deemed stable enough
-/// for general use.
-///
-/// FIXME(#33240)
-#[cfg(not(test))]
-pub fn check_old_skool() -> bool {
-    use std::env;
-    env::var("RUST_NEW_ERROR_FORMAT").is_err()
-}
-
-/// For unit tests, use the new format.
-#[cfg(test)]
-pub fn check_old_skool() -> bool {
-    false
-}
diff --git a/src/libsyntax/errors/snippet/mod.rs b/src/libsyntax/errors/snippet/mod.rs
deleted file mode 100644 (file)
index 188e676..0000000
+++ /dev/null
@@ -1,874 +0,0 @@
-// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Code for annotating snippets.
-
-use codemap::{CharPos, CodeMap, FileMap, LineInfo, Span};
-use errors::check_old_skool;
-use std::cmp;
-use std::rc::Rc;
-use std::mem;
-
-mod test;
-
-#[derive(Clone)]
-pub struct SnippetData {
-    codemap: Rc<CodeMap>,
-    files: Vec<FileInfo>,
-}
-
-#[derive(Clone)]
-pub struct FileInfo {
-    file: Rc<FileMap>,
-
-    /// The "primary file", if any, gets a `-->` marker instead of
-    /// `>>>`, and has a line-number/column printed and not just a
-    /// filename.  It appears first in the listing. It is known to
-    /// contain at least one primary span, though primary spans (which
-    /// are designated with `^^^`) may also occur in other files.
-    primary_span: Option<Span>,
-
-    lines: Vec<Line>,
-}
-
-#[derive(Clone, Debug)]
-struct Line {
-    line_index: usize,
-    annotations: Vec<Annotation>,
-}
-
-#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
-struct Annotation {
-    /// Start column, 0-based indexing -- counting *characters*, not
-    /// utf-8 bytes. Note that it is important that this field goes
-    /// first, so that when we sort, we sort orderings by start
-    /// column.
-    start_col: usize,
-
-    /// End column within the line (exclusive)
-    end_col: usize,
-
-    /// Is this annotation derived from primary span
-    is_primary: bool,
-
-    /// Is this a large span minimized down to a smaller span
-    is_minimized: bool,
-
-    /// Optional label to display adjacent to the annotation.
-    label: Option<String>,
-}
-
-#[derive(Debug)]
-pub struct RenderedLine {
-    pub text: Vec<StyledString>,
-    pub kind: RenderedLineKind,
-}
-
-#[derive(Debug)]
-pub struct StyledString {
-    pub text: String,
-    pub style: Style,
-}
-
-#[derive(Debug)]
-pub struct StyledBuffer {
-    text: Vec<Vec<char>>,
-    styles: Vec<Vec<Style>>
-}
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Style {
-    FileNameStyle,
-    LineAndColumn,
-    LineNumber,
-    Quotation,
-    UnderlinePrimary,
-    UnderlineSecondary,
-    LabelPrimary,
-    LabelSecondary,
-    OldSkoolNoteText,
-    OldSkoolNote,
-    NoStyle,
-}
-
-#[derive(Debug, Clone)]
-pub enum RenderedLineKind {
-    PrimaryFileName,
-    OtherFileName,
-    SourceText {
-        file: Rc<FileMap>,
-        line_index: usize,
-    },
-    Annotations,
-    Elision,
-}
-
-impl SnippetData {
-    pub fn new(codemap: Rc<CodeMap>,
-               primary_span: Option<Span>) // (*)
-               -> Self {
-        // (*) The primary span indicates the file that must appear
-        // first, and which will have a line number etc in its
-        // name. Outside of tests, this is always `Some`, but for many
-        // tests it's not relevant to test this portion of the logic,
-        // and it's tedious to pick a primary span (read: tedious to
-        // port older tests that predate the existence of a primary
-        // span).
-
-        debug!("SnippetData::new(primary_span={:?})", primary_span);
-
-        let mut data = SnippetData {
-            codemap: codemap.clone(),
-            files: vec![]
-        };
-        if let Some(primary_span) = primary_span {
-            let lo = codemap.lookup_char_pos(primary_span.lo);
-            data.files.push(
-                FileInfo {
-                    file: lo.file,
-                    primary_span: Some(primary_span),
-                    lines: vec![],
-                });
-        }
-        data
-    }
-
-    pub fn push(&mut self, span: Span, is_primary: bool, label: Option<String>) {
-        debug!("SnippetData::push(span={:?}, is_primary={}, label={:?})",
-               span, is_primary, label);
-
-        let file_lines = match self.codemap.span_to_lines(span) {
-            Ok(file_lines) => file_lines,
-            Err(_) => {
-                // ignore unprintable spans completely.
-                return;
-            }
-        };
-
-        self.file(&file_lines.file)
-            .push_lines(&file_lines.lines, is_primary, label);
-    }
-
-    fn file(&mut self, file_map: &Rc<FileMap>) -> &mut FileInfo {
-        let index = self.files.iter().position(|f| f.file.name == file_map.name);
-        if let Some(index) = index {
-            return &mut self.files[index];
-        }
-
-        self.files.push(
-            FileInfo {
-                file: file_map.clone(),
-                lines: vec![],
-                primary_span: None,
-            });
-        self.files.last_mut().unwrap()
-    }
-
-    pub fn render_lines(&self) -> Vec<RenderedLine> {
-        debug!("SnippetData::render_lines()");
-
-        let mut rendered_lines: Vec<_> =
-            self.files.iter()
-                      .flat_map(|f| f.render_file_lines(&self.codemap))
-                      .collect();
-        prepend_prefixes(&mut rendered_lines);
-        trim_lines(&mut rendered_lines);
-        rendered_lines
-    }
-}
-
-pub trait StringSource {
-    fn make_string(self) -> String;
-}
-
-impl StringSource for String {
-    fn make_string(self) -> String {
-        self
-    }
-}
-
-impl StringSource for Vec<char> {
-    fn make_string(self) -> String {
-        self.into_iter().collect()
-    }
-}
-
-impl<S> From<(S, Style, RenderedLineKind)> for RenderedLine
-    where S: StringSource
-{
-    fn from((text, style, kind): (S, Style, RenderedLineKind)) -> Self {
-        RenderedLine {
-            text: vec![StyledString {
-                text: text.make_string(),
-                style: style,
-            }],
-            kind: kind,
-        }
-    }
-}
-
-impl<S1,S2> From<(S1, Style, S2, Style, RenderedLineKind)> for RenderedLine
-    where S1: StringSource, S2: StringSource
-{
-    fn from(tuple: (S1, Style, S2, Style, RenderedLineKind)) -> Self {
-        let (text1, style1, text2, style2, kind) = tuple;
-        RenderedLine {
-            text: vec![
-                StyledString {
-                    text: text1.make_string(),
-                    style: style1,
-                },
-                StyledString {
-                    text: text2.make_string(),
-                    style: style2,
-                }
-            ],
-            kind: kind,
-        }
-    }
-}
-
-impl RenderedLine {
-    fn trim_last(&mut self) {
-        if let Some(last_text) = self.text.last_mut() {
-            let len = last_text.text.trim_right().len();
-            last_text.text.truncate(len);
-        }
-    }
-}
-
-impl RenderedLineKind {
-    fn prefix(&self) -> StyledString {
-        match *self {
-            RenderedLineKind::SourceText { file: _, line_index } =>
-                StyledString {
-                    text: format!("{}", line_index + 1),
-                    style: Style::LineNumber,
-                },
-            RenderedLineKind::Elision =>
-                StyledString {
-                    text: String::from("..."),
-                    style: Style::LineNumber,
-                },
-            RenderedLineKind::PrimaryFileName |
-            RenderedLineKind::OtherFileName |
-            RenderedLineKind::Annotations =>
-                StyledString {
-                    text: String::from(""),
-                    style: Style::LineNumber,
-                },
-        }
-    }
-}
-
-impl StyledBuffer {
-    fn new() -> StyledBuffer {
-        StyledBuffer { text: vec![], styles: vec![] }
-    }
-
-    fn render(&self, source_kind: RenderedLineKind) -> Vec<RenderedLine> {
-        let mut output: Vec<RenderedLine> = vec![];
-        let mut styled_vec: Vec<StyledString> = vec![];
-
-        for (row, row_style) in self.text.iter().zip(&self.styles) {
-            let mut current_style = Style::NoStyle;
-            let mut current_text = String::new();
-
-            for (&c, &s) in row.iter().zip(row_style) {
-                if s != current_style {
-                    if !current_text.is_empty() {
-                        styled_vec.push(StyledString { text: current_text, style: current_style });
-                    }
-                    current_style = s;
-                    current_text = String::new();
-                }
-                current_text.push(c);
-            }
-            if !current_text.is_empty() {
-                styled_vec.push(StyledString { text: current_text, style: current_style });
-            }
-
-            if output.is_empty() {
-                //We know our first output line is source and the rest are highlights and labels
-                output.push(RenderedLine { text: styled_vec, kind: source_kind.clone() });
-            } else {
-                output.push(RenderedLine { text: styled_vec, kind: RenderedLineKind::Annotations });
-            }
-            styled_vec = vec![];
-        }
-
-        output
-    }
-
-    fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
-        while line >= self.text.len() {
-            self.text.push(vec![]);
-            self.styles.push(vec![]);
-        }
-
-        if col < self.text[line].len() {
-            self.text[line][col] = chr;
-            self.styles[line][col] = style;
-        } else {
-            let mut i = self.text[line].len();
-            while i < col {
-                let s = match self.text[0].get(i) {
-                    Some(&'\t') => '\t',
-                    _ => ' '
-                };
-                self.text[line].push(s);
-                self.styles[line].push(Style::NoStyle);
-                i += 1;
-            }
-            self.text[line].push(chr);
-            self.styles[line].push(style);
-        }
-    }
-
-    fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
-        let mut n = col;
-        for c in string.chars() {
-            self.putc(line, n, c, style);
-            n += 1;
-        }
-    }
-
-    fn set_style(&mut self, line: usize, col: usize, style: Style) {
-        if self.styles.len() > line && self.styles[line].len() > col {
-            self.styles[line][col] = style;
-        }
-    }
-
-    fn append(&mut self, line: usize, string: &str, style: Style) {
-        if line >= self.text.len() {
-            self.puts(line, 0, string, style);
-        } else {
-            let col = self.text[line].len();
-            self.puts(line, col, string, style);
-        }
-    }
-}
-
-impl FileInfo {
-    fn push_lines(&mut self,
-                  lines: &[LineInfo],
-                  is_primary: bool,
-                  label: Option<String>) {
-        assert!(lines.len() > 0);
-
-        // If a span covers multiple lines, we reduce it to a single
-        // point at the start of the span. This means that instead
-        // of producing output like this:
-        //
-        // ```
-        // --> foo.rs:2:1
-        // 2   |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
-        //     |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-        // 3   |>                               -> Set<LR0Item<'grammar>>
-        //     |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-        // (and so on)
-        // ```
-        //
-        // we produce:
-        //
-        // ```
-        // --> foo.rs:2:1
-        // 2   |> fn conflicting_items<'grammar>(state: &LR0State<'grammar>)
-        //        ^
-        // ```
-        //
-        // Basically, although this loses information, multi-line spans just
-        // never look good.
-
-        let (line, start_col, mut end_col, is_minimized) = if lines.len() == 1 {
-            (lines[0].line_index, lines[0].start_col, lines[0].end_col, false)
-        } else {
-            (lines[0].line_index, lines[0].start_col, CharPos(lines[0].start_col.0 + 1), true)
-        };
-
-        // Watch out for "empty spans". If we get a span like 6..6, we
-        // want to just display a `^` at 6, so convert that to
-        // 6..7. This is degenerate input, but it's best to degrade
-        // gracefully -- and the parser likes to suply a span like
-        // that for EOF, in particular.
-        if start_col == end_col {
-            end_col.0 += 1;
-        }
-
-        let index = self.ensure_source_line(line);
-        self.lines[index].push_annotation(start_col,
-                                          end_col,
-                                          is_primary,
-                                          is_minimized,
-                                          label);
-    }
-
-    /// Ensure that we have a `Line` struct corresponding to
-    /// `line_index` in the file. If we already have some other lines,
-    /// then this will add the intervening lines to ensure that we
-    /// have a complete snippet. (Note that when we finally display,
-    /// some of those lines may be elided.)
-    fn ensure_source_line(&mut self, line_index: usize) -> usize {
-        if self.lines.is_empty() {
-            self.lines.push(Line::new(line_index));
-            return 0;
-        }
-
-        // Find the range of lines we have thus far.
-        let first_line_index = self.lines.first().unwrap().line_index;
-        let last_line_index = self.lines.last().unwrap().line_index;
-        assert!(first_line_index <= last_line_index);
-
-        // If the new line is lower than all the lines we have thus
-        // far, then insert the new line and any intervening lines at
-        // the front. In a silly attempt at micro-optimization, we
-        // don't just call `insert` repeatedly, but instead make a new
-        // (empty) vector, pushing the new lines onto it, and then
-        // appending the old vector.
-        if line_index < first_line_index {
-            let lines = mem::replace(&mut self.lines, vec![]);
-            self.lines.extend(
-                (line_index .. first_line_index)
-                    .map(|line| Line::new(line))
-                    .chain(lines));
-            return 0;
-        }
-
-        // If the new line comes after the ones we have so far, insert
-        // lines for it.
-        if line_index > last_line_index {
-            self.lines.extend(
-                (last_line_index+1 .. line_index+1)
-                    .map(|line| Line::new(line)));
-            return self.lines.len() - 1;
-        }
-
-        // Otherwise it should already exist.
-        return line_index - first_line_index;
-    }
-
-    fn render_file_lines(&self, codemap: &Rc<CodeMap>) -> Vec<RenderedLine> {
-        let old_school = check_old_skool();
-
-        // As a first step, we elide any instance of more than one
-        // continuous unannotated line.
-
-        let mut lines_iter = self.lines.iter();
-        let mut output = vec![];
-
-        // First insert the name of the file.
-        if !old_school {
-            match self.primary_span {
-                Some(span) => {
-                    let lo = codemap.lookup_char_pos(span.lo);
-                    output.push(RenderedLine {
-                        text: vec![StyledString {
-                            text: lo.file.name.clone(),
-                            style: Style::FileNameStyle,
-                        }, StyledString {
-                            text: format!(":{}:{}", lo.line, lo.col.0 + 1),
-                            style: Style::LineAndColumn,
-                        }],
-                        kind: RenderedLineKind::PrimaryFileName,
-                    });
-                }
-                None => {
-                    output.push(RenderedLine {
-                        text: vec![StyledString {
-                            text: self.file.name.clone(),
-                            style: Style::FileNameStyle,
-                        }],
-                        kind: RenderedLineKind::OtherFileName,
-                    });
-                }
-            }
-        }
-
-        let mut next_line = lines_iter.next();
-        while next_line.is_some() {
-            // Consume lines with annotations.
-            while let Some(line) = next_line {
-                if line.annotations.is_empty() { break; }
-
-                let mut rendered_lines = self.render_line(line);
-                assert!(!rendered_lines.is_empty());
-                if old_school {
-                    match self.primary_span {
-                        Some(span) => {
-                            let lo = codemap.lookup_char_pos(span.lo);
-                            let hi = codemap.lookup_char_pos(span.hi);
-                            //Before each secondary line in old skool-mode, print the label
-                            //as an old-style note
-                            if !line.annotations[0].is_primary {
-                                if let Some(ann) = line.annotations[0].label.clone() {
-                                    output.push(RenderedLine {
-                                        text: vec![StyledString {
-                                            text: lo.file.name.clone(),
-                                            style: Style::FileNameStyle,
-                                        }, StyledString {
-                                            text: format!(":{}:{}: {}:{} ", lo.line, lo.col.0 + 1,
-                                                hi.line, hi.col.0+1),
-                                            style: Style::LineAndColumn,
-                                        }, StyledString {
-                                            text: format!("note: "),
-                                            style: Style::OldSkoolNote,
-                                        }, StyledString {
-                                            text: format!("{}", ann),
-                                            style: Style::OldSkoolNoteText,
-                                        }],
-                                        kind: RenderedLineKind::Annotations,
-                                    });
-                                }
-                            }
-                            rendered_lines[0].text.insert(0, StyledString {
-                                text: format!(":{} ", lo.line),
-                                style: Style::LineAndColumn,
-                            });
-                            rendered_lines[0].text.insert(0, StyledString {
-                                text: lo.file.name.clone(),
-                                style: Style::FileNameStyle,
-                            });
-                            let gap_amount =
-                                rendered_lines[0].text[0].text.len() +
-                                rendered_lines[0].text[1].text.len();
-                            assert!(rendered_lines.len() >= 2,
-                                    "no annotations resulted from: {:?}",
-                                    line);
-                            for i in 1..rendered_lines.len() {
-                                rendered_lines[i].text.insert(0, StyledString {
-                                    text: vec![" "; gap_amount].join(""),
-                                    style: Style::NoStyle
-                                });
-                            }
-                        }
-                        _ =>()
-                    }
-                }
-                output.append(&mut rendered_lines);
-                next_line = lines_iter.next();
-            }
-
-            // Emit lines without annotations, but only if they are
-            // followed by a line with an annotation.
-            let unannotated_line = next_line;
-            let mut unannotated_lines = 0;
-            while let Some(line) = next_line {
-                if !line.annotations.is_empty() { break; }
-                unannotated_lines += 1;
-                next_line = lines_iter.next();
-            }
-            if unannotated_lines > 1 {
-                output.push(RenderedLine::from((String::new(),
-                                                Style::NoStyle,
-                                                RenderedLineKind::Elision)));
-            } else if let Some(line) = unannotated_line {
-                output.append(&mut self.render_line(line));
-            }
-        }
-
-        output
-    }
-
-    fn render_line(&self, line: &Line) -> Vec<RenderedLine> {
-        let old_school = check_old_skool();
-        let source_string = self.file.get_line(line.line_index)
-                                     .unwrap_or("");
-        let source_kind = RenderedLineKind::SourceText {
-            file: self.file.clone(),
-            line_index: line.line_index,
-        };
-
-        let mut styled_buffer = StyledBuffer::new();
-
-        // First create the source line we will highlight.
-        styled_buffer.append(0, &source_string, Style::Quotation);
-
-        if line.annotations.is_empty() {
-            return styled_buffer.render(source_kind);
-        }
-
-        // We want to display like this:
-        //
-        //      vec.push(vec.pop().unwrap());
-        //      ---      ^^^               _ previous borrow ends here
-        //      |        |
-        //      |        error occurs here
-        //      previous borrow of `vec` occurs here
-        //
-        // But there are some weird edge cases to be aware of:
-        //
-        //      vec.push(vec.pop().unwrap());
-        //      --------                    - previous borrow ends here
-        //      ||
-        //      |this makes no sense
-        //      previous borrow of `vec` occurs here
-        //
-        // For this reason, we group the lines into "highlight lines"
-        // and "annotations lines", where the highlight lines have the `~`.
-
-        //let mut highlight_line = Self::whitespace(&source_string);
-
-        // Sort the annotations by (start, end col)
-        let mut annotations = line.annotations.clone();
-        annotations.sort();
-
-        // Next, create the highlight line.
-        for annotation in &annotations {
-            if old_school {
-                for p in annotation.start_col .. annotation.end_col {
-                    if p == annotation.start_col {
-                        styled_buffer.putc(1, p, '^',
-                            if annotation.is_primary {
-                                Style::UnderlinePrimary
-                            } else {
-                                Style::OldSkoolNote
-                            });
-                    }
-                    else {
-                        styled_buffer.putc(1, p, '~',
-                            if annotation.is_primary {
-                                Style::UnderlinePrimary
-                            } else {
-                                Style::OldSkoolNote
-                            });
-                    }
-                }
-            }
-            else {
-                for p in annotation.start_col .. annotation.end_col {
-                    if annotation.is_primary {
-                        styled_buffer.putc(1, p, '^', Style::UnderlinePrimary);
-                        if !annotation.is_minimized {
-                            styled_buffer.set_style(0, p, Style::UnderlinePrimary);
-                        }
-                    } else {
-                        styled_buffer.putc(1, p, '-', Style::UnderlineSecondary);
-                        if !annotation.is_minimized {
-                            styled_buffer.set_style(0, p, Style::UnderlineSecondary);
-                        }
-                    }
-                }
-            }
-        }
-
-        // Now we are going to write labels in. To start, we'll exclude
-        // the annotations with no labels.
-        let (labeled_annotations, unlabeled_annotations): (Vec<_>, _) =
-            annotations.into_iter()
-                       .partition(|a| a.label.is_some());
-
-        // If there are no annotations that need text, we're done.
-        if labeled_annotations.is_empty() {
-            return styled_buffer.render(source_kind);
-        }
-        if old_school {
-            return styled_buffer.render(source_kind);
-        }
-
-        // Now add the text labels. We try, when possible, to stick the rightmost
-        // annotation at the end of the highlight line:
-        //
-        //      vec.push(vec.pop().unwrap());
-        //      ---      ---               - previous borrow ends here
-        //
-        // But sometimes that's not possible because one of the other
-        // annotations overlaps it. For example, from the test
-        // `span_overlap_label`, we have the following annotations
-        // (written on distinct lines for clarity):
-        //
-        //      fn foo(x: u32) {
-        //      --------------
-        //             -
-        //
-        // In this case, we can't stick the rightmost-most label on
-        // the highlight line, or we would get:
-        //
-        //      fn foo(x: u32) {
-        //      -------- x_span
-        //      |
-        //      fn_span
-        //
-        // which is totally weird. Instead we want:
-        //
-        //      fn foo(x: u32) {
-        //      --------------
-        //      |      |
-        //      |      x_span
-        //      fn_span
-        //
-        // which is...less weird, at least. In fact, in general, if
-        // the rightmost span overlaps with any other span, we should
-        // use the "hang below" version, so we can at least make it
-        // clear where the span *starts*.
-        let mut labeled_annotations = &labeled_annotations[..];
-        match labeled_annotations.split_last().unwrap() {
-            (last, previous) => {
-                if previous.iter()
-                           .chain(&unlabeled_annotations)
-                           .all(|a| !overlaps(a, last))
-                {
-                    // append the label afterwards; we keep it in a separate
-                    // string
-                    let highlight_label: String = format!(" {}", last.label.as_ref().unwrap());
-                    if last.is_primary {
-                        styled_buffer.append(1, &highlight_label, Style::LabelPrimary);
-                    } else {
-                        styled_buffer.append(1, &highlight_label, Style::LabelSecondary);
-                    }
-                    labeled_annotations = previous;
-                }
-            }
-        }
-
-        // If that's the last annotation, we're done
-        if labeled_annotations.is_empty() {
-            return styled_buffer.render(source_kind);
-        }
-
-        for (index, annotation) in labeled_annotations.iter().enumerate() {
-            // Leave:
-            // - 1 extra line
-            // - One line for each thing that comes after
-            let comes_after = labeled_annotations.len() - index - 1;
-            let blank_lines = 3 + comes_after;
-
-            // For each blank line, draw a `|` at our column. The
-            // text ought to be long enough for this.
-            for index in 2..blank_lines {
-                if annotation.is_primary {
-                    styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlinePrimary);
-                } else {
-                    styled_buffer.putc(index, annotation.start_col, '|', Style::UnderlineSecondary);
-                }
-            }
-
-            if annotation.is_primary {
-                styled_buffer.puts(blank_lines, annotation.start_col,
-                    annotation.label.as_ref().unwrap(), Style::LabelPrimary);
-            } else {
-                styled_buffer.puts(blank_lines, annotation.start_col,
-                    annotation.label.as_ref().unwrap(), Style::LabelSecondary);
-            }
-        }
-
-        styled_buffer.render(source_kind)
-    }
-}
-
-fn prepend_prefixes(rendered_lines: &mut [RenderedLine]) {
-    let old_school = check_old_skool();
-    if old_school {
-        return;
-    }
-
-    let prefixes: Vec<_> =
-        rendered_lines.iter()
-                      .map(|rl| rl.kind.prefix())
-                      .collect();
-
-    // find the max amount of spacing we need; add 1 to
-    // p.text.len() to leave space between the prefix and the
-    // source text
-    let padding_len =
-        prefixes.iter()
-                .map(|p| if p.text.len() == 0 { 0 } else { p.text.len() + 1 })
-                .max()
-                .unwrap_or(0);
-
-    // Ensure we insert at least one character of padding, so that the
-    // `-->` arrows can fit etc.
-    let padding_len = cmp::max(padding_len, 1);
-
-    for (mut prefix, line) in prefixes.into_iter().zip(rendered_lines) {
-        let extra_spaces = (prefix.text.len() .. padding_len).map(|_| ' ');
-        prefix.text.extend(extra_spaces);
-        match line.kind {
-            RenderedLineKind::Elision => {
-                line.text.insert(0, prefix);
-            }
-            RenderedLineKind::PrimaryFileName => {
-                //   --> filename
-                // 22 |>
-                //   ^
-                //   padding_len
-                let dashes = (0..padding_len - 1).map(|_| ' ')
-                                                 .chain(Some('-'))
-                                                 .chain(Some('-'))
-                                                 .chain(Some('>'))
-                                                 .chain(Some(' '));
-                line.text.insert(0, StyledString {text: dashes.collect(),
-                                                  style: Style::LineNumber})
-            }
-            RenderedLineKind::OtherFileName => {
-                //   ::: filename
-                // 22 |>
-                //   ^
-                //   padding_len
-                let dashes = (0..padding_len - 1).map(|_| ' ')
-                                                 .chain(Some(':'))
-                                                 .chain(Some(':'))
-                                                 .chain(Some(':'))
-                                                 .chain(Some(' '));
-                line.text.insert(0, StyledString {text: dashes.collect(),
-                                                  style: Style::LineNumber})
-            }
-            _ => {
-                line.text.insert(0, prefix);
-                line.text.insert(1, StyledString {text: String::from("|> "),
-                                                  style: Style::LineNumber})
-            }
-        }
-    }
-}
-
-fn trim_lines(rendered_lines: &mut [RenderedLine]) {
-    for line in rendered_lines {
-        while !line.text.is_empty() {
-            line.trim_last();
-            if line.text.last().unwrap().text.is_empty() {
-                line.text.pop();
-            } else {
-                break;
-            }
-        }
-    }
-}
-
-impl Line {
-    fn new(line_index: usize) -> Line {
-        Line {
-            line_index: line_index,
-            annotations: vec![]
-        }
-    }
-
-    fn push_annotation(&mut self,
-                       start: CharPos,
-                       end: CharPos,
-                       is_primary: bool,
-                       is_minimized: bool,
-                       label: Option<String>) {
-        self.annotations.push(Annotation {
-            start_col: start.0,
-            end_col: end.0,
-            is_primary: is_primary,
-            is_minimized: is_minimized,
-            label: label,
-        });
-    }
-}
-
-fn overlaps(a1: &Annotation,
-            a2: &Annotation)
-            -> bool
-{
-    (a2.start_col .. a2.end_col).contains(a1.start_col) ||
-        (a1.start_col .. a1.end_col).contains(a2.start_col)
-}
diff --git a/src/libsyntax/errors/snippet/test.rs b/src/libsyntax/errors/snippet/test.rs
deleted file mode 100644 (file)
index 62ce3fa..0000000
+++ /dev/null
@@ -1,583 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Code for testing annotated snippets.
-
-#![cfg(test)]
-
-use codemap::{BytePos, CodeMap, FileMap, NO_EXPANSION, Span};
-use std::rc::Rc;
-use super::{RenderedLine, SnippetData};
-
-/// Returns the span corresponding to the `n`th occurrence of
-/// `substring` in `source_text`.
-trait CodeMapExtension {
-    fn span_substr(&self,
-                   file: &Rc<FileMap>,
-                   source_text: &str,
-                   substring: &str,
-                   n: usize)
-                   -> Span;
-}
-
-impl CodeMapExtension for CodeMap {
-    fn span_substr(&self,
-                   file: &Rc<FileMap>,
-                   source_text: &str,
-                   substring: &str,
-                   n: usize)
-                   -> Span
-    {
-        println!("span_substr(file={:?}/{:?}, substring={:?}, n={})",
-                 file.name, file.start_pos, substring, n);
-        let mut i = 0;
-        let mut hi = 0;
-        loop {
-            let offset = source_text[hi..].find(substring).unwrap_or_else(|| {
-                panic!("source_text `{}` does not have {} occurrences of `{}`, only {}",
-                       source_text, n, substring, i);
-            });
-            let lo = hi + offset;
-            hi = lo + substring.len();
-            if i == n {
-                let span = Span {
-                    lo: BytePos(lo as u32 + file.start_pos.0),
-                    hi: BytePos(hi as u32 + file.start_pos.0),
-                    expn_id: NO_EXPANSION,
-                };
-                assert_eq!(&self.span_to_snippet(span).unwrap()[..],
-                           substring);
-                return span;
-            }
-            i += 1;
-        }
-    }
-}
-
-fn splice(start: Span, end: Span) -> Span {
-    Span {
-        lo: start.lo,
-        hi: end.hi,
-        expn_id: NO_EXPANSION,
-    }
-}
-
-fn make_string(lines: &[RenderedLine]) -> String {
-    lines.iter()
-         .flat_map(|rl| {
-             rl.text.iter()
-                    .map(|s| &s.text[..])
-                    .chain(Some("\n"))
-         })
-         .collect()
-}
-
-#[test]
-fn tab() {
-    let file_text = "
-fn foo() {
-\tbar;
-}
-";
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-    let span_bar = cm.span_substr(&foo, file_text, "bar", 0);
-
-    let mut snippet = SnippetData::new(cm, Some(span_bar));
-    snippet.push(span_bar, true, None);
-
-    let lines = snippet.render_lines();
-    let text = make_string(&lines);
-    assert_eq!(&text[..], &"
- --> foo.rs:3:2
-3 |> \tbar;
-  |> \t^^^
-"[1..]);
-}
-
-#[test]
-fn one_line() {
-    let file_text = r#"
-fn foo() {
-    vec.push(vec.pop().unwrap());
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-    let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
-    let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
-    let span_semi = cm.span_substr(&foo, file_text, ";", 0);
-
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
-    snippet.push(span_vec1, false, Some(format!("error occurs here")));
-    snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-
-    let text: String = make_string(&lines);
-
-    println!("text=\n{}", text);
-    assert_eq!(&text[..], &r#"
- ::: foo.rs
-3 |>     vec.push(vec.pop().unwrap());
-  |>     ---      ---                - previous borrow ends here
-  |>     |        |
-  |>     |        error occurs here
-  |>     previous borrow of `vec` occurs here
-"#[1..]);
-}
-
-#[test]
-fn two_files() {
-    let file_text_foo = r#"
-fn foo() {
-    vec.push(vec.pop().unwrap());
-}
-"#;
-
-    let file_text_bar = r#"
-fn bar() {
-    // these blank links here
-    // serve to ensure that the line numbers
-    // from bar.rs
-    // require more digits
-
-
-
-
-
-
-
-
-
-
-    vec.push();
-
-    // this line will get elided
-
-    vec.pop().unwrap());
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo_map = cm.new_filemap_and_lines("foo.rs", file_text_foo);
-    let span_foo_vec0 = cm.span_substr(&foo_map, file_text_foo, "vec", 0);
-    let span_foo_vec1 = cm.span_substr(&foo_map, file_text_foo, "vec", 1);
-    let span_foo_semi = cm.span_substr(&foo_map, file_text_foo, ";", 0);
-
-    let bar_map = cm.new_filemap_and_lines("bar.rs", file_text_bar);
-    let span_bar_vec0 = cm.span_substr(&bar_map, file_text_bar, "vec", 0);
-    let span_bar_vec1 = cm.span_substr(&bar_map, file_text_bar, "vec", 1);
-    let span_bar_semi = cm.span_substr(&bar_map, file_text_bar, ";", 0);
-
-    let mut snippet = SnippetData::new(cm, Some(span_foo_vec1));
-    snippet.push(span_foo_vec0, false, Some(format!("a")));
-    snippet.push(span_foo_vec1, true, Some(format!("b")));
-    snippet.push(span_foo_semi, false, Some(format!("c")));
-    snippet.push(span_bar_vec0, false, Some(format!("d")));
-    snippet.push(span_bar_vec1, false, Some(format!("e")));
-    snippet.push(span_bar_semi, false, Some(format!("f")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-
-    let text: String = make_string(&lines);
-
-    println!("text=\n{}", text);
-
-    // Note that the `|>` remain aligned across both files:
-    assert_eq!(&text[..], &r#"
-   --> foo.rs:3:14
-3   |>     vec.push(vec.pop().unwrap());
-    |>     ---      ^^^                - c
-    |>     |        |
-    |>     |        b
-    |>     a
-   ::: bar.rs
-17  |>     vec.push();
-    |>     ---       - f
-    |>     |
-    |>     d
-...
-21  |>     vec.pop().unwrap());
-    |>     --- e
-"#[1..]);
-}
-
-#[test]
-fn multi_line() {
-    let file_text = r#"
-fn foo() {
-    let name = find_id(&data, 22).unwrap();
-
-    // Add one more item we forgot to the vector. Silly us.
-    data.push(Data { name: format!("Hera"), id: 66 });
-
-    // Print everything out.
-    println!("Name: {:?}", name);
-    println!("Data: {:?}", data);
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-    let span_data0 = cm.span_substr(&foo, file_text, "data", 0);
-    let span_data1 = cm.span_substr(&foo, file_text, "data", 1);
-    let span_rbrace = cm.span_substr(&foo, file_text, "}", 3);
-
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span_data0, false, Some(format!("immutable borrow begins here")));
-    snippet.push(span_data1, false, Some(format!("mutable borrow occurs here")));
-    snippet.push(span_rbrace, false, Some(format!("immutable borrow ends here")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-
-    let text: String = make_string(&lines);
-
-    println!("text=\n{}", text);
-    assert_eq!(&text[..], &r#"
-   ::: foo.rs
-3   |>     let name = find_id(&data, 22).unwrap();
-    |>                         ---- immutable borrow begins here
-...
-6   |>     data.push(Data { name: format!("Hera"), id: 66 });
-    |>     ---- mutable borrow occurs here
-...
-11  |> }
-    |> - immutable borrow ends here
-"#[1..]);
-}
-
-#[test]
-fn overlapping() {
-    let file_text = r#"
-fn foo() {
-    vec.push(vec.pop().unwrap());
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-    let span0 = cm.span_substr(&foo, file_text, "vec.push", 0);
-    let span1 = cm.span_substr(&foo, file_text, "vec", 0);
-    let span2 = cm.span_substr(&foo, file_text, "ec.push", 0);
-    let span3 = cm.span_substr(&foo, file_text, "unwrap", 0);
-
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span0, false, Some(format!("A")));
-    snippet.push(span1, false, Some(format!("B")));
-    snippet.push(span2, false, Some(format!("C")));
-    snippet.push(span3, false, Some(format!("D")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-    let text: String = make_string(&lines);
-
-    println!("text=r#\"\n{}\".trim_left()", text);
-    assert_eq!(&text[..], &r#"
- ::: foo.rs
-3 |>     vec.push(vec.pop().unwrap());
-  |>     --------           ------ D
-  |>     ||
-  |>     |C
-  |>     A
-  |>     B
-"#[1..]);
-}
-
-#[test]
-fn one_line_out_of_order() {
-    let file_text = r#"
-fn foo() {
-    vec.push(vec.pop().unwrap());
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-    let span_vec0 = cm.span_substr(&foo, file_text, "vec", 0);
-    let span_vec1 = cm.span_substr(&foo, file_text, "vec", 1);
-    let span_semi = cm.span_substr(&foo, file_text, ";", 0);
-
-    // intentionally don't push the snippets left to right
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span_vec1, false, Some(format!("error occurs here")));
-    snippet.push(span_vec0, false, Some(format!("previous borrow of `vec` occurs here")));
-    snippet.push(span_semi, false, Some(format!("previous borrow ends here")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-    let text: String = make_string(&lines);
-
-    println!("text=r#\"\n{}\".trim_left()", text);
-    assert_eq!(&text[..], &r#"
- ::: foo.rs
-3 |>     vec.push(vec.pop().unwrap());
-  |>     ---      ---                - previous borrow ends here
-  |>     |        |
-  |>     |        error occurs here
-  |>     previous borrow of `vec` occurs here
-"#[1..]);
-}
-
-#[test]
-fn elide_unnecessary_lines() {
-    let file_text = r#"
-fn foo() {
-    let mut vec = vec![0, 1, 2];
-    let mut vec2 = vec;
-    vec2.push(3);
-    vec2.push(4);
-    vec2.push(5);
-    vec2.push(6);
-    vec.push(7);
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-    let span_vec0 = cm.span_substr(&foo, file_text, "vec", 3);
-    let span_vec1 = cm.span_substr(&foo, file_text, "vec", 8);
-
-    let mut snippet = SnippetData::new(cm, None);
-    snippet.push(span_vec0, false, Some(format!("`vec` moved here because it \
-        has type `collections::vec::Vec<i32>`")));
-    snippet.push(span_vec1, false, Some(format!("use of moved value: `vec`")));
-
-    let lines = snippet.render_lines();
-    println!("{:#?}", lines);
-    let text: String = make_string(&lines);
-    println!("text=r#\"\n{}\".trim_left()", text);
-    assert_eq!(&text[..], &r#"
-   ::: foo.rs
-4   |>     let mut vec2 = vec;
-    |>                    --- `vec` moved here because it has type `collections::vec::Vec<i32>`
-...
-9   |>     vec.push(7);
-    |>     --- use of moved value: `vec`
-"#[1..]);
-}
-
-#[test]
-fn spans_without_labels() {
-    let file_text = r#"
-fn foo() {
-    let mut vec = vec![0, 1, 2];
-    let mut vec2 = vec;
-    vec2.push(3);
-    vec2.push(4);
-    vec2.push(5);
-    vec2.push(6);
-    vec.push(7);
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-    for i in 0..4 {
-        let span_veci = cm.span_substr(&foo, file_text, "vec", i);
-        snippet.push(span_veci, false, None);
-    }
-
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("text=&r#\"\n{}\n\"#[1..]", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-3 |>     let mut vec = vec![0, 1, 2];
-  |>             ---   ---
-4 |>     let mut vec2 = vec;
-  |>             ---    ---
-"#[1..]);
-}
-
-#[test]
-fn span_long_selection() {
-    let file_text = r#"
-impl SomeTrait for () {
-    fn foo(x: u32) {
-        // impl 1
-        // impl 2
-        // impl 3
-    }
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-    let fn_span = cm.span_substr(&foo, file_text, "fn", 0);
-    let rbrace_span = cm.span_substr(&foo, file_text, "}", 0);
-    snippet.push(splice(fn_span, rbrace_span), false, None);
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-3 |>     fn foo(x: u32) {
-  |>     -
-"#[1..]);
-}
-
-#[test]
-fn span_overlap_label() {
-    // Test that we don't put `x_span` to the right of its highlight,
-    // since there is another highlight that overlaps it.
-
-    let file_text = r#"
-    fn foo(x: u32) {
-    }
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-    let fn_span = cm.span_substr(&foo, file_text, "fn foo(x: u32)", 0);
-    let x_span = cm.span_substr(&foo, file_text, "x", 0);
-    snippet.push(fn_span, false, Some(format!("fn_span")));
-    snippet.push(x_span, false, Some(format!("x_span")));
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-2 |>     fn foo(x: u32) {
-  |>     --------------
-  |>     |      |
-  |>     |      x_span
-  |>     fn_span
-"#[1..]);
-}
-
-#[test]
-fn span_overlap_label2() {
-    // Test that we don't put `x_span` to the right of its highlight,
-    // since there is another highlight that overlaps it. In this
-    // case, the overlap is only at the beginning, but it's still
-    // better to show the beginning more clearly.
-
-    let file_text = r#"
-    fn foo(x: u32) {
-    }
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-    let fn_span = cm.span_substr(&foo, file_text, "fn foo(x", 0);
-    let x_span = cm.span_substr(&foo, file_text, "x: u32)", 0);
-    snippet.push(fn_span, false, Some(format!("fn_span")));
-    snippet.push(x_span, false, Some(format!("x_span")));
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-2 |>     fn foo(x: u32) {
-  |>     --------------
-  |>     |      |
-  |>     |      x_span
-  |>     fn_span
-"#[1..]);
-}
-
-#[test]
-fn span_overlap_label3() {
-    // Test that we don't put `x_span` to the right of its highlight,
-    // since there is another highlight that overlaps it. In this
-    // case, the overlap is only at the beginning, but it's still
-    // better to show the beginning more clearly.
-
-    let file_text = r#"
-    fn foo() {
-       let closure = || {
-           inner
-       };
-    }
-}
-"#;
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-
-    let mut snippet = SnippetData::new(cm.clone(), None);
-
-    let closure_span = {
-        let closure_start_span = cm.span_substr(&foo, file_text, "||", 0);
-        let closure_end_span = cm.span_substr(&foo, file_text, "}", 0);
-        splice(closure_start_span, closure_end_span)
-    };
-
-    let inner_span = cm.span_substr(&foo, file_text, "inner", 0);
-
-    snippet.push(closure_span, false, Some(format!("foo")));
-    snippet.push(inner_span, false, Some(format!("bar")));
-
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
- ::: foo.rs
-3 |>        let closure = || {
-  |>                      - foo
-4 |>            inner
-  |>            ----- bar
-"#[1..]);
-}
-
-#[test]
-fn span_empty() {
-    // In one of the unit tests, we found that the parser sometimes
-    // gives empty spans, and in particular it supplied an EOF span
-    // like this one, which points at the very end. We want to
-    // fallback gracefully in this case.
-
-    let file_text = r#"
-fn main() {
-    struct Foo;
-
-    impl !Sync for Foo {}
-
-    unsafe impl Send for &'static Foo {
-    // error: cross-crate traits with a default impl, like `core::marker::Send`,
-    //        can only be implemented for a struct/enum type, not
-    //        `&'static Foo`
-}"#;
-
-
-    let cm = Rc::new(CodeMap::new());
-    let foo = cm.new_filemap_and_lines("foo.rs", file_text);
-
-    let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1);
-    rbrace_span.lo = rbrace_span.hi;
-
-    let mut snippet = SnippetData::new(cm.clone(), Some(rbrace_span));
-    snippet.push(rbrace_span, false, None);
-    let lines = snippet.render_lines();
-    let text: String = make_string(&lines);
-    println!("r#\"\n{}\"", text);
-    assert_eq!(text, &r#"
-  --> foo.rs:11:2
-11 |> }
-   |>  -
-"#[1..]);
-}
index 303187aeba87dda35452dd5491d01e4abf334c4c..70d924cf46d06096c4147c57d03f2073933ba2c2 100644 (file)
@@ -12,13 +12,13 @@ pub use self::SyntaxExtension::*;
 
 use ast;
 use ast::{Name, PatKind};
-use codemap;
-use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION};
+use attr::HasAttrs;
+use codemap::{self, CodeMap, ExpnInfo};
+use syntax_pos::{Span, ExpnId, NO_EXPANSION};
 use errors::DiagnosticBuilder;
 use ext;
 use ext::expand;
 use ext::tt::macro_rules;
-use feature_gate::GatedCfgAttr;
 use parse;
 use parse::parser;
 use parse::token;
@@ -32,6 +32,7 @@ use fold::Folder;
 use std::collections::{HashMap, HashSet};
 use std::rc::Rc;
 use std::default::Default;
+use tokenstream;
 
 
 #[derive(Debug,Clone)]
@@ -41,29 +42,31 @@ pub enum Annotatable {
     ImplItem(P<ast::ImplItem>),
 }
 
-impl Annotatable {
-    pub fn attrs(&self) -> &[ast::Attribute] {
+impl HasAttrs for Annotatable {
+    fn attrs(&self) -> &[ast::Attribute] {
         match *self {
-            Annotatable::Item(ref i) => &i.attrs,
-            Annotatable::TraitItem(ref ti) => &ti.attrs,
-            Annotatable::ImplItem(ref ii) => &ii.attrs,
+            Annotatable::Item(ref item) => &item.attrs,
+            Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
+            Annotatable::ImplItem(ref impl_item) => &impl_item.attrs,
         }
     }
 
-    pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
+    fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
         match self {
-            Annotatable::Item(i) => Annotatable::Item(i.map(|i| ast::Item {
-                attrs: attrs,
-                ..i
-            })),
-            Annotatable::TraitItem(i) => Annotatable::TraitItem(i.map(|ti| {
-                ast::TraitItem { attrs: attrs, ..ti }
-            })),
-            Annotatable::ImplItem(i) => Annotatable::ImplItem(i.map(|ii| {
-                ast::ImplItem { attrs: attrs, ..ii }
-            })),
+            Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)),
+            Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)),
+            Annotatable::ImplItem(impl_item) => Annotatable::ImplItem(impl_item.map_attrs(f)),
         }
     }
+}
+
+impl Annotatable {
+    pub fn attrs(&self) -> &[ast::Attribute] {
+        HasAttrs::attrs(self)
+    }
+    pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
+        self.map_attrs(|_| attrs)
+    }
 
     pub fn expect_item(self) -> P<ast::Item> {
         match self {
@@ -95,6 +98,16 @@ impl Annotatable {
             _ => panic!("expected Item")
         }
     }
+
+    pub fn fold_with<F: Folder>(self, folder: &mut F) -> SmallVector<Self> {
+        match self {
+            Annotatable::Item(item) => folder.fold_item(item).map(Annotatable::Item),
+            Annotatable::ImplItem(item) =>
+                folder.fold_impl_item(item.unwrap()).map(|item| Annotatable::ImplItem(P(item))),
+            Annotatable::TraitItem(item) =>
+                folder.fold_trait_item(item.unwrap()).map(|item| Annotatable::TraitItem(P(item))),
+        }
+    }
 }
 
 // A more flexible ItemDecorator.
@@ -120,9 +133,7 @@ impl<F> MultiItemDecorator for F
     }
 }
 
-// A more flexible ItemKind::Modifier (ItemKind::Modifier should go away, eventually, FIXME).
-// meta_item is the annotation, item is the item being modified, parent_item
-// is the impl or trait item is declared in if item is part of such a thing.
+// `meta_item` is the annotation, and `item` is the item being modified.
 // FIXME Decorators should follow the same pattern too.
 pub trait MultiItemModifier {
     fn expand(&self,
@@ -130,22 +141,26 @@ pub trait MultiItemModifier {
               span: Span,
               meta_item: &ast::MetaItem,
               item: Annotatable)
-              -> Annotatable;
+              -> Vec<Annotatable>;
 }
 
-impl<F> MultiItemModifier for F
-    where F: Fn(&mut ExtCtxt,
-                Span,
-                &ast::MetaItem,
-                Annotatable) -> Annotatable
+impl<F, T> MultiItemModifier for F
+    where F: Fn(&mut ExtCtxt, Span, &ast::MetaItem, Annotatable) -> T,
+          T: Into<Vec<Annotatable>>,
 {
     fn expand(&self,
               ecx: &mut ExtCtxt,
               span: Span,
               meta_item: &ast::MetaItem,
               item: Annotatable)
-              -> Annotatable {
-        (*self)(ecx, span, meta_item, item)
+              -> Vec<Annotatable> {
+        (*self)(ecx, span, meta_item, item).into()
+    }
+}
+
+impl Into<Vec<Annotatable>> for Annotatable {
+    fn into(self) -> Vec<Annotatable> {
+        vec![self]
     }
 }
 
@@ -154,20 +169,22 @@ pub trait TTMacroExpander {
     fn expand<'cx>(&self,
                    ecx: &'cx mut ExtCtxt,
                    span: Span,
-                   token_tree: &[ast::TokenTree])
+                   token_tree: &[tokenstream::TokenTree])
                    -> Box<MacResult+'cx>;
 }
 
 pub type MacroExpanderFn =
-    for<'cx> fn(&'cx mut ExtCtxt, Span, &[ast::TokenTree]) -> Box<MacResult+'cx>;
+    for<'cx> fn(&'cx mut ExtCtxt, Span, &[tokenstream::TokenTree])
+                -> Box<MacResult+'cx>;
 
 impl<F> TTMacroExpander for F
-    where F : for<'cx> Fn(&'cx mut ExtCtxt, Span, &[ast::TokenTree]) -> Box<MacResult+'cx>
+    where F : for<'cx> Fn(&'cx mut ExtCtxt, Span, &[tokenstream::TokenTree])
+                          -> Box<MacResult+'cx>
 {
     fn expand<'cx>(&self,
                    ecx: &'cx mut ExtCtxt,
                    span: Span,
-                   token_tree: &[ast::TokenTree])
+                   token_tree: &[tokenstream::TokenTree])
                    -> Box<MacResult+'cx> {
         (*self)(ecx, span, token_tree)
     }
@@ -178,22 +195,23 @@ pub trait IdentMacroExpander {
                    cx: &'cx mut ExtCtxt,
                    sp: Span,
                    ident: ast::Ident,
-                   token_tree: Vec<ast::TokenTree> )
+                   token_tree: Vec<tokenstream::TokenTree> )
                    -> Box<MacResult+'cx>;
 }
 
 pub type IdentMacroExpanderFn =
-    for<'cx> fn(&'cx mut ExtCtxt, Span, ast::Ident, Vec<ast::TokenTree>) -> Box<MacResult+'cx>;
+    for<'cx> fn(&'cx mut ExtCtxt, Span, ast::Ident, Vec<tokenstream::TokenTree>)
+                -> Box<MacResult+'cx>;
 
 impl<F> IdentMacroExpander for F
     where F : for<'cx> Fn(&'cx mut ExtCtxt, Span, ast::Ident,
-                          Vec<ast::TokenTree>) -> Box<MacResult+'cx>
+                          Vec<tokenstream::TokenTree>) -> Box<MacResult+'cx>
 {
     fn expand<'cx>(&self,
                    cx: &'cx mut ExtCtxt,
                    sp: Span,
                    ident: ast::Ident,
-                   token_tree: Vec<ast::TokenTree> )
+                   token_tree: Vec<tokenstream::TokenTree> )
                    -> Box<MacResult+'cx>
     {
         (*self)(cx, sp, ident, token_tree)
@@ -203,10 +221,11 @@ impl<F> IdentMacroExpander for F
 // Use a macro because forwarding to a simple function has type system issues
 macro_rules! make_stmts_default {
     ($me:expr) => {
-        $me.make_expr().map(|e| {
-            SmallVector::one(codemap::respan(
-                e.span, ast::StmtKind::Expr(e, ast::DUMMY_NODE_ID)))
-        })
+        $me.make_expr().map(|e| SmallVector::one(ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            span: e.span,
+            node: ast::StmtKind::Expr(e),
+        }))
     }
 }
 
@@ -227,6 +246,11 @@ pub trait MacResult {
         None
     }
 
+    /// Create zero or more trait items.
+    fn make_trait_items(self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> {
+        None
+    }
+
     /// Create a pattern.
     fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> {
         None
@@ -274,6 +298,7 @@ make_MacEager! {
     pat: P<ast::Pat>,
     items: SmallVector<P<ast::Item>>,
     impl_items: SmallVector<ast::ImplItem>,
+    trait_items: SmallVector<ast::TraitItem>,
     stmts: SmallVector<ast::Stmt>,
     ty: P<ast::Ty>,
 }
@@ -291,6 +316,10 @@ impl MacResult for MacEager {
         self.impl_items
     }
 
+    fn make_trait_items(self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> {
+        self.trait_items
+    }
+
     fn make_stmts(self: Box<Self>) -> Option<SmallVector<ast::Stmt>> {
         match self.stmts.as_ref().map_or(0, |s| s.len()) {
             0 => make_stmts_default!(self),
@@ -351,7 +380,7 @@ impl DummyResult {
             id: ast::DUMMY_NODE_ID,
             node: ast::ExprKind::Lit(P(codemap::respan(sp, ast::LitKind::Bool(false)))),
             span: sp,
-            attrs: None,
+            attrs: ast::ThinVec::new(),
         })
     }
 
@@ -399,11 +428,24 @@ impl MacResult for DummyResult {
         }
     }
 
+    fn make_trait_items(self: Box<DummyResult>) -> Option<SmallVector<ast::TraitItem>> {
+        if self.expr_only {
+            None
+        } else {
+            Some(SmallVector::zero())
+        }
+    }
+
     fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<ast::Stmt>> {
-        Some(SmallVector::one(
-            codemap::respan(self.span,
-                            ast::StmtKind::Expr(DummyResult::raw_expr(self.span),
-                                                ast::DUMMY_NODE_ID))))
+        Some(SmallVector::one(ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            node: ast::StmtKind::Expr(DummyResult::raw_expr(self.span)),
+            span: self.span,
+        }))
+    }
+
+    fn make_ty(self: Box<DummyResult>) -> Option<P<ast::Ty>> {
+        Some(DummyResult::raw_ty(self.span))
     }
 }
 
@@ -536,6 +578,17 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
     syntax_expanders
 }
 
+pub trait MacroLoader {
+    fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<ast::MacroDef>;
+}
+
+pub struct DummyMacroLoader;
+impl MacroLoader for DummyMacroLoader {
+    fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec<ast::MacroDef> {
+        Vec::new()
+    }
+}
+
 /// One of these is made during expansion and incrementally updated as we go;
 /// when a macro expansion occurs, the resulting nodes have the backtrace()
 /// -> expn_info of their expansion context stored into their span.
@@ -545,7 +598,7 @@ pub struct ExtCtxt<'a> {
     pub backtrace: ExpnId,
     pub ecfg: expand::ExpansionConfig<'a>,
     pub crate_root: Option<&'static str>,
-    pub feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>,
+    pub loader: &'a mut MacroLoader,
 
     pub mod_path: Vec<ast::Ident> ,
     pub exported_macros: Vec<ast::MacroDef>,
@@ -561,7 +614,8 @@ pub struct ExtCtxt<'a> {
 impl<'a> ExtCtxt<'a> {
     pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
                ecfg: expand::ExpansionConfig<'a>,
-               feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>) -> ExtCtxt<'a> {
+               loader: &'a mut MacroLoader)
+               -> ExtCtxt<'a> {
         let env = initial_syntax_expander_table(&ecfg);
         ExtCtxt {
             parse_sess: parse_sess,
@@ -570,8 +624,8 @@ impl<'a> ExtCtxt<'a> {
             mod_path: Vec::new(),
             ecfg: ecfg,
             crate_root: None,
-            feature_gated_cfgs: feature_gated_cfgs,
             exported_macros: Vec::new(),
+            loader: loader,
             syntax_env: env,
             recursion_count: 0,
 
@@ -586,7 +640,7 @@ impl<'a> ExtCtxt<'a> {
         expand::MacroExpander::new(self)
     }
 
-    pub fn new_parser_from_tts(&self, tts: &[ast::TokenTree])
+    pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree])
         -> parser::Parser<'a> {
         parse::tts_to_parser(self.parse_sess, tts.to_vec(), self.cfg())
     }
@@ -602,22 +656,6 @@ impl<'a> ExtCtxt<'a> {
     }
     pub fn backtrace(&self) -> ExpnId { self.backtrace }
 
-    /// Original span that caused the current exapnsion to happen.
-    pub fn original_span(&self) -> Span {
-        let mut expn_id = self.backtrace;
-        let mut call_site = None;
-        loop {
-            match self.codemap().with_expn_info(expn_id, |ei| ei.map(|ei| ei.call_site)) {
-                None => break,
-                Some(cs) => {
-                    call_site = Some(cs);
-                    expn_id = cs.expn_id;
-                }
-            }
-        }
-        call_site.expect("missing expansion backtrace")
-    }
-
     /// Returns span for the macro which originally caused the current expansion to happen.
     ///
     /// Stops backtracing at include! boundary.
@@ -782,6 +820,12 @@ impl<'a> ExtCtxt<'a> {
 /// compilation on error, merely emits a non-fatal error and returns None.
 pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
                       -> Option<(InternedString, ast::StrStyle)> {
+    // Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation.
+    let expr = expr.map(|mut expr| {
+        expr.span.expn_id = cx.backtrace;
+        expr
+    });
+
     // we want to be able to handle e.g. concat("foo", "bar")
     let expr = cx.expander().fold_expr(expr);
     match expr.node {
@@ -801,7 +845,7 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
 /// done as rarely as possible).
 pub fn check_zero_tts(cx: &ExtCtxt,
                       sp: Span,
-                      tts: &[ast::TokenTree],
+                      tts: &[tokenstream::TokenTree],
                       name: &str) {
     if !tts.is_empty() {
         cx.span_err(sp, &format!("{} takes no arguments", name));
@@ -812,7 +856,7 @@ pub fn check_zero_tts(cx: &ExtCtxt,
 /// is not a string literal, emit an error and return None.
 pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
                                sp: Span,
-                               tts: &[ast::TokenTree],
+                               tts: &[tokenstream::TokenTree],
                                name: &str)
                                -> Option<String> {
     let mut p = cx.new_parser_from_tts(tts);
@@ -833,7 +877,7 @@ pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
 /// parsing error, emit a non-fatal error and return None.
 pub fn get_exprs_from_tts(cx: &mut ExtCtxt,
                           sp: Span,
-                          tts: &[ast::TokenTree]) -> Option<Vec<P<ast::Expr>>> {
+                          tts: &[tokenstream::TokenTree]) -> Option<Vec<P<ast::Expr>>> {
     let mut p = cx.new_parser_from_tts(tts);
     let mut es = Vec::new();
     while p.token != token::Eof {
@@ -906,9 +950,8 @@ impl SyntaxEnv {
 
     pub fn find(&self, k: Name) -> Option<Rc<SyntaxExtension>> {
         for frame in self.chain.iter().rev() {
-            match frame.map.get(&k) {
-                Some(v) => return Some(v.clone()),
-                None => {}
+            if let Some(v) = frame.map.get(&k) {
+                return Some(v.clone());
             }
         }
         None
@@ -925,4 +968,10 @@ impl SyntaxEnv {
         let last_chain_index = self.chain.len() - 1;
         &mut self.chain[last_chain_index].info
     }
+
+    pub fn is_crate_root(&mut self) -> bool {
+        // The first frame is pushed in `SyntaxEnv::new()` and the second frame is
+        // pushed when folding the crate root pseudo-module (c.f. noop_fold_crate).
+        self.chain.len() <= 2
+    }
 }
index 7958162986cfc558f2fbd7ddf0daaf829d7ec6b0..435241f426ec6f4329e90a2dc6dea8c093e3e723 100644 (file)
@@ -11,7 +11,8 @@
 use abi::Abi;
 use ast::{self, Ident, Generics, Expr, BlockCheckMode, UnOp, PatKind};
 use attr;
-use codemap::{Span, respan, Spanned, DUMMY_SP, Pos};
+use syntax_pos::{Span, DUMMY_SP, Pos};
+use codemap::{respan, Spanned};
 use ext::base::ExtCtxt;
 use parse::token::{self, keywords, InternedString};
 use ptr::P;
@@ -87,6 +88,7 @@ pub trait AstBuilder {
 
     // statements
     fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt;
+    fn stmt_semi(&self, expr: P<ast::Expr>) -> ast::Stmt;
     fn stmt_let(&self, sp: Span, mutbl: bool, ident: ast::Ident, ex: P<ast::Expr>) -> ast::Stmt;
     fn stmt_let_typed(&self,
                       sp: Span,
@@ -98,12 +100,8 @@ pub trait AstBuilder {
     fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt;
 
     // blocks
-    fn block(&self, span: Span, stmts: Vec<ast::Stmt>,
-             expr: Option<P<ast::Expr>>) -> P<ast::Block>;
+    fn block(&self, span: Span, stmts: Vec<ast::Stmt>) -> P<ast::Block>;
     fn block_expr(&self, expr: P<ast::Expr>) -> P<ast::Block>;
-    fn block_all(&self, span: Span,
-                 stmts: Vec<ast::Stmt>,
-                 expr: Option<P<ast::Expr>>) -> P<ast::Block>;
 
     // expressions
     fn expr(&self, span: Span, node: ast::ExprKind) -> P<ast::Expr>;
@@ -508,7 +506,19 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     }
 
     fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
-        respan(expr.span, ast::StmtKind::Semi(expr, ast::DUMMY_NODE_ID))
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            span: expr.span,
+            node: ast::StmtKind::Expr(expr),
+        }
+    }
+
+    fn stmt_semi(&self, expr: P<ast::Expr>) -> ast::Stmt {
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            span: expr.span,
+            node: ast::StmtKind::Semi(expr),
+        }
     }
 
     fn stmt_let(&self, sp: Span, mutbl: bool, ident: ast::Ident,
@@ -525,10 +535,13 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             init: Some(ex),
             id: ast::DUMMY_NODE_ID,
             span: sp,
-            attrs: None,
+            attrs: ast::ThinVec::new(),
         });
-        let decl = respan(sp, ast::DeclKind::Local(local));
-        respan(sp, ast::StmtKind::Decl(P(decl), ast::DUMMY_NODE_ID))
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            node: ast::StmtKind::Local(local),
+            span: sp,
+        }
     }
 
     fn stmt_let_typed(&self,
@@ -550,36 +563,37 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             init: Some(ex),
             id: ast::DUMMY_NODE_ID,
             span: sp,
-            attrs: None,
+            attrs: ast::ThinVec::new(),
         });
-        let decl = respan(sp, ast::DeclKind::Local(local));
-        P(respan(sp, ast::StmtKind::Decl(P(decl), ast::DUMMY_NODE_ID)))
-    }
-
-    fn block(&self, span: Span, stmts: Vec<ast::Stmt>,
-             expr: Option<P<Expr>>) -> P<ast::Block> {
-        self.block_all(span, stmts, expr)
+        P(ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            node: ast::StmtKind::Local(local),
+            span: sp,
+        })
     }
 
     fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {
-        let decl = respan(sp, ast::DeclKind::Item(item));
-        respan(sp, ast::StmtKind::Decl(P(decl), ast::DUMMY_NODE_ID))
+        ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            node: ast::StmtKind::Item(item),
+            span: sp,
+        }
     }
 
     fn block_expr(&self, expr: P<ast::Expr>) -> P<ast::Block> {
-        self.block_all(expr.span, Vec::new(), Some(expr))
-    }
-    fn block_all(&self,
-                 span: Span,
-                 stmts: Vec<ast::Stmt>,
-                 expr: Option<P<ast::Expr>>) -> P<ast::Block> {
-            P(ast::Block {
-               stmts: stmts,
-               expr: expr,
-               id: ast::DUMMY_NODE_ID,
-               rules: BlockCheckMode::Default,
-               span: span,
-            })
+        self.block(expr.span, vec![ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            span: expr.span,
+            node: ast::StmtKind::Expr(expr),
+        }])
+    }
+    fn block(&self, span: Span, stmts: Vec<ast::Stmt>) -> P<ast::Block> {
+        P(ast::Block {
+           stmts: stmts,
+           id: ast::DUMMY_NODE_ID,
+           rules: BlockCheckMode::Default,
+           span: span,
+        })
     }
 
     fn expr(&self, span: Span, node: ast::ExprKind) -> P<ast::Expr> {
@@ -587,7 +601,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             id: ast::DUMMY_NODE_ID,
             node: node,
             span: span,
-            attrs: None,
+            attrs: ast::ThinVec::new(),
         })
     }
 
@@ -830,9 +844,9 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     }
     fn pat_enum(&self, span: Span, path: ast::Path, subpats: Vec<P<ast::Pat>>) -> P<ast::Pat> {
         let pat = if subpats.is_empty() {
-            PatKind::Path(path)
+            PatKind::Path(None, path)
         } else {
-            PatKind::TupleStruct(path, Some(subpats))
+            PatKind::TupleStruct(path, subpats, None)
         };
         self.pat(span, pat)
     }
@@ -842,7 +856,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         self.pat(span, pat)
     }
     fn pat_tuple(&self, span: Span, pats: Vec<P<ast::Pat>>) -> P<ast::Pat> {
-        self.pat(span, PatKind::Tup(pats))
+        self.pat(span, PatKind::Tuple(pats, None))
     }
 
     fn pat_some(&self, span: Span, pat: P<ast::Pat>) -> P<ast::Pat> {
@@ -948,14 +962,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
                     ids: Vec<ast::Ident>,
                     stmts: Vec<ast::Stmt>)
                     -> P<ast::Expr> {
-        self.lambda(span, ids, self.block(span, stmts, None))
+        self.lambda(span, ids, self.block(span, stmts))
     }
     fn lambda_stmts_0(&self, span: Span, stmts: Vec<ast::Stmt>) -> P<ast::Expr> {
-        self.lambda0(span, self.block(span, stmts, None))
+        self.lambda0(span, self.block(span, stmts))
     }
     fn lambda_stmts_1(&self, span: Span, stmts: Vec<ast::Stmt>,
                       ident: ast::Ident) -> P<ast::Expr> {
-        self.lambda1(span, self.block(span, stmts, None), ident)
+        self.lambda1(span, self.block(span, stmts), ident)
     }
 
     fn arg(&self, span: Span, ident: ast::Ident, ty: P<ast::Ty>) -> ast::Arg {
index f243706eecb8036334960f80ea07e6a46aa0c6bd..c670283e559d9b8c60451dc964c617302ccada20 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast::{Block, Crate, DeclKind, PatKind};
-use ast::{Local, Ident, Mac_, Name};
+use ast::{Block, Crate, PatKind};
+use ast::{Local, Ident, Mac_, Name, SpannedIdent};
 use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
-use ast::TokenTree;
 use ast;
+use attr::HasAttrs;
 use ext::mtwt;
-use ext::build::AstBuilder;
 use attr;
-use attr::{AttrMetaMethods, WithAttrs};
-use codemap;
-use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
+use attr::AttrMetaMethods;
+use codemap::{Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
+use syntax_pos::{self, Span, ExpnId};
+use config::StripUnconfigured;
 use ext::base::*;
 use feature_gate::{self, Features};
 use fold;
 use fold::*;
 use util::move_map::MoveMap;
-use parse;
 use parse::token::{fresh_mark, fresh_name, intern, keywords};
 use ptr::P;
+use tokenstream::TokenTree;
 use util::small_vector::SmallVector;
 use visit;
 use visit::Visitor;
 use std_inject;
 
 use std::collections::HashSet;
-use std::env;
 
-// this function is called to detect use of feature-gated or invalid attributes
-// on macro invoations since they will not be detected after macro expansion
-fn check_attributes(attrs: &[ast::Attribute], fld: &MacroExpander) {
-    for attr in attrs.iter() {
-        feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic,
-                                      &fld.cx.parse_sess.codemap(),
-                                      &fld.cx.ecfg.features.unwrap());
-    }
-}
+// A trait for AST nodes and AST node lists into which macro invocations may expand.
+trait MacroGenerable: Sized {
+    // Expand the given MacResult using its appropriate `make_*` method.
+    fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self>;
 
-pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
-    let expr_span = e.span;
-    return e.and_then(|ast::Expr {id, node, span, attrs}| match node {
+    // Fold this node or list of nodes using the given folder.
+    fn fold_with<F: Folder>(self, folder: &mut F) -> Self;
+    fn visit_with<V: Visitor>(&self, visitor: &mut V);
 
-        // expr_mac should really be expr_ext or something; it's the
-        // entry-point for all syntax extensions.
-        ast::ExprKind::Mac(mac) => {
-            if let Some(ref attrs) = attrs {
-                check_attributes(attrs, fld);
-            }
-
-            // Assert that we drop any macro attributes on the floor here
-            drop(attrs);
-
-            let expanded_expr = match expand_mac_invoc(mac, span,
-                                                       |r| r.make_expr(),
-                                                       mark_expr, fld) {
-                Some(expr) => expr,
-                None => {
-                    return DummyResult::raw_expr(span);
-                }
-            };
+    // The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics.
+    fn kind_name() -> &'static str;
 
-            // Keep going, outside-in.
-            let fully_expanded = fld.fold_expr(expanded_expr);
-            fld.cx.bt_pop();
+    // Return a placeholder expansion to allow compilation to continue after an erroring expansion.
+    fn dummy(span: Span) -> Self {
+        Self::make_with(DummyResult::any(span)).unwrap()
+    }
+}
 
-            fully_expanded
+macro_rules! impl_macro_generable {
+    ($($ty:ty: $kind_name:expr, .$make:ident,
+               $(.$fold:ident)*  $(lift .$fold_elt:ident)*,
+               $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { $(
+        impl MacroGenerable for $ty {
+            fn kind_name() -> &'static str { $kind_name }
+            fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> { result.$make() }
+            fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
+                $( folder.$fold(self) )*
+                $( self.into_iter().flat_map(|item| folder. $fold_elt (item)).collect() )*
+            }
+            fn visit_with<V: Visitor>(&self, visitor: &mut V) {
+                $( visitor.$visit(self) )*
+                $( for item in self.as_slice() { visitor. $visit_elt (item) } )*
+            }
         }
+    )* }
+}
 
-        ast::ExprKind::InPlace(placer, value_expr) => {
-            // Ensure feature-gate is enabled
-            if !fld.cx.ecfg.features.unwrap().placement_in_syntax {
-                feature_gate::emit_feature_err(
-                    &fld.cx.parse_sess.span_diagnostic, "placement_in_syntax", expr_span,
-                    feature_gate::GateIssue::Language, feature_gate::EXPLAIN_PLACEMENT_IN
-                );
-            }
+impl_macro_generable! {
+    P<ast::Expr>: "expression", .make_expr, .fold_expr, .visit_expr;
+    P<ast::Pat>:  "pattern",    .make_pat,  .fold_pat,  .visit_pat;
+    P<ast::Ty>:   "type",       .make_ty,   .fold_ty,   .visit_ty;
+    SmallVector<ast::Stmt>: "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt;
+    SmallVector<P<ast::Item>>: "item",   .make_items, lift .fold_item, lift .visit_item;
+    SmallVector<ast::TraitItem>:
+        "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item;
+    SmallVector<ast::ImplItem>:
+        "impl item",  .make_impl_items,  lift .fold_impl_item,  lift .visit_impl_item;
+}
 
-            let placer = fld.fold_expr(placer);
-            let value_expr = fld.fold_expr(value_expr);
-            fld.cx.expr(span, ast::ExprKind::InPlace(placer, value_expr))
-                .with_attrs(fold_thin_attrs(attrs, fld))
+impl MacroGenerable for Option<P<ast::Expr>> {
+    fn kind_name() -> &'static str { "expression" }
+    fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> {
+        result.make_expr().map(Some)
+    }
+    fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
+        self.and_then(|expr| folder.fold_opt_expr(expr))
+    }
+    fn visit_with<V: Visitor>(&self, visitor: &mut V) {
+        self.as_ref().map(|expr| visitor.visit_expr(expr));
+    }
+}
+
+pub fn expand_expr(mut expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
+    match expr.node {
+        // expr_mac should really be expr_ext or something; it's the
+        // entry-point for all syntax extensions.
+        ast::ExprKind::Mac(mac) => {
+            return expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, fld);
         }
 
         ast::ExprKind::While(cond, body, opt_ident) => {
             let cond = fld.fold_expr(cond);
             let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
-            fld.cx.expr(span, ast::ExprKind::While(cond, body, opt_ident))
-                .with_attrs(fold_thin_attrs(attrs, fld))
+            expr.node = ast::ExprKind::While(cond, body, opt_ident);
         }
 
-        ast::ExprKind::WhileLet(pat, expr, body, opt_ident) => {
+        ast::ExprKind::WhileLet(pat, cond, body, opt_ident) => {
             let pat = fld.fold_pat(pat);
-            let expr = fld.fold_expr(expr);
+            let cond = fld.fold_expr(cond);
 
             // Hygienic renaming of the body.
             let ((body, opt_ident), mut rewritten_pats) =
@@ -111,14 +124,12 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             });
             assert!(rewritten_pats.len() == 1);
 
-            let wl = ast::ExprKind::WhileLet(rewritten_pats.remove(0), expr, body, opt_ident);
-            fld.cx.expr(span, wl).with_attrs(fold_thin_attrs(attrs, fld))
+            expr.node = ast::ExprKind::WhileLet(rewritten_pats.remove(0), cond, body, opt_ident);
         }
 
         ast::ExprKind::Loop(loop_block, opt_ident) => {
             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
-            fld.cx.expr(span, ast::ExprKind::Loop(loop_block, opt_ident))
-                .with_attrs(fold_thin_attrs(attrs, fld))
+            expr.node = ast::ExprKind::Loop(loop_block, opt_ident);
         }
 
         ast::ExprKind::ForLoop(pat, head, body, opt_ident) => {
@@ -135,8 +146,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             assert!(rewritten_pats.len() == 1);
 
             let head = fld.fold_expr(head);
-            let fl = ast::ExprKind::ForLoop(rewritten_pats.remove(0), head, body, opt_ident);
-            fld.cx.expr(span, fl).with_attrs(fold_thin_attrs(attrs, fld))
+            expr.node = ast::ExprKind::ForLoop(rewritten_pats.remove(0), head, body, opt_ident);
         }
 
         ast::ExprKind::IfLet(pat, sub_expr, body, else_opt) => {
@@ -154,121 +164,167 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
 
             let else_opt = else_opt.map(|else_opt| fld.fold_expr(else_opt));
             let sub_expr = fld.fold_expr(sub_expr);
-            let il = ast::ExprKind::IfLet(rewritten_pats.remove(0), sub_expr, body, else_opt);
-            fld.cx.expr(span, il).with_attrs(fold_thin_attrs(attrs, fld))
+            expr.node = ast::ExprKind::IfLet(rewritten_pats.remove(0), sub_expr, body, else_opt);
         }
 
         ast::ExprKind::Closure(capture_clause, fn_decl, block, fn_decl_span) => {
             let (rewritten_fn_decl, rewritten_block)
                 = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
-            let new_node = ast::ExprKind::Closure(capture_clause,
-                                                  rewritten_fn_decl,
-                                                  rewritten_block,
-                                                  fld.new_span(fn_decl_span));
-            P(ast::Expr{ id:id,
-                         node: new_node,
-                         span: fld.new_span(span),
-                         attrs: fold_thin_attrs(attrs, fld) })
+            expr.node = ast::ExprKind::Closure(capture_clause,
+                                               rewritten_fn_decl,
+                                               rewritten_block,
+                                               fn_decl_span);
         }
 
-        _ => {
-            P(noop_fold_expr(ast::Expr {
-                id: id,
-                node: node,
-                span: span,
-                attrs: attrs
-            }, fld))
-        }
-    });
+        _ => expr = noop_fold_expr(expr, fld),
+    };
+    P(expr)
 }
 
-/// Expand a (not-ident-style) macro invocation. Returns the result
-/// of expansion and the mark which must be applied to the result.
-/// Our current interface doesn't allow us to apply the mark to the
-/// result until after calling make_expr, make_items, etc.
-fn expand_mac_invoc<T, F, G>(mac: ast::Mac,
-                             span: codemap::Span,
-                             parse_thunk: F,
-                             mark_thunk: G,
-                             fld: &mut MacroExpander)
-                             -> Option<T> where
-    F: for<'a> FnOnce(Box<MacResult+'a>) -> Option<T>,
-    G: FnOnce(T, Mrk) -> T,
+/// Expand a macro invocation. Returns the result of expansion.
+fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attribute>, span: Span,
+                       fld: &mut MacroExpander) -> T
+    where T: MacroGenerable,
 {
-    // it would almost certainly be cleaner to pass the whole
-    // macro invocation in, rather than pulling it apart and
-    // marking the tts and the ctxt separately. This also goes
-    // for the other three macro invocation chunks of code
-    // in this file.
-
-    let Mac_ { path: pth, tts, .. } = mac.node;
-    if pth.segments.len() > 1 {
-        fld.cx.span_err(pth.span,
-                        "expected macro name without module \
-                        separators");
-        // let compilation continue
-        return None;
-    }
-    let extname = pth.segments[0].identifier.name;
-    match fld.cx.syntax_env.find(extname) {
-        None => {
-            let mut err = fld.cx.struct_span_err(
-                pth.span,
-                &format!("macro undefined: '{}!'",
-                        &extname));
+    // It would almost certainly be cleaner to pass the whole macro invocation in,
+    // rather than pulling it apart and marking the tts and the ctxt separately.
+    let Mac_ { path, tts, .. } = mac.node;
+    let mark = fresh_mark();
+
+    fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mrk,
+                      attrs: Vec<ast::Attribute>, call_site: Span, fld: &'a mut MacroExpander)
+                      -> Option<Box<MacResult + 'a>> {
+        // Detect use of feature-gated or invalid attributes on macro invoations
+        // since they will not be detected after macro expansion.
+        for attr in attrs.iter() {
+            feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic,
+                                          &fld.cx.parse_sess.codemap(),
+                                          &fld.cx.ecfg.features.unwrap());
+        }
+
+        if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
+            fld.cx.span_err(path.span, "expected macro name without module separators");
+            return None;
+        }
+
+        let extname = path.segments[0].identifier.name;
+        let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) {
+            extension
+        } else {
+            let mut err = fld.cx.struct_span_err(path.span,
+                                                 &format!("macro undefined: '{}!'", &extname));
             fld.cx.suggest_macro_name(&extname.as_str(), &mut err);
             err.emit();
+            return None;
+        };
 
-            // let compilation continue
-            None
-        }
-        Some(rc) => match *rc {
+        let ident = ident.unwrap_or(keywords::Invalid.ident());
+        match *extension {
             NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
+                if ident.name != keywords::Invalid.name() {
+                    let msg =
+                        format!("macro {}! expects no ident argument, given '{}'", extname, ident);
+                    fld.cx.span_err(path.span, &msg);
+                    return None;
+                }
+
                 fld.cx.bt_push(ExpnInfo {
-                        call_site: span,
-                        callee: NameAndSpan {
-                            format: MacroBang(extname),
-                            span: exp_span,
-                            allow_internal_unstable: allow_internal_unstable,
-                        },
-                    });
-                let fm = fresh_mark();
-                let marked_before = mark_tts(&tts[..], fm);
-
-                // The span that we pass to the expanders we want to
-                // be the root of the call stack. That's the most
-                // relevant span and it's the actual invocation of
-                // the macro.
-                let mac_span = fld.cx.original_span();
-
-                let opt_parsed = {
-                    let expanded = expandfun.expand(fld.cx,
-                                                    mac_span,
-                                                    &marked_before[..]);
-                    parse_thunk(expanded)
+                    call_site: call_site,
+                    callee: NameAndSpan {
+                        format: MacroBang(extname),
+                        span: exp_span,
+                        allow_internal_unstable: allow_internal_unstable,
+                    },
+                });
+
+                let marked_tts = mark_tts(tts, mark);
+                Some(expandfun.expand(fld.cx, call_site, &marked_tts))
+            }
+
+            IdentTT(ref expander, tt_span, allow_internal_unstable) => {
+                if ident.name == keywords::Invalid.name() {
+                    fld.cx.span_err(path.span,
+                                    &format!("macro {}! expects an ident argument", extname));
+                    return None;
                 };
-                let parsed = match opt_parsed {
-                    Some(e) => e,
-                    None => {
-                        fld.cx.span_err(
-                            pth.span,
-                            &format!("non-expression macro in expression position: {}",
-                                    extname
-                                    ));
-                        return None;
+
+                fld.cx.bt_push(ExpnInfo {
+                    call_site: call_site,
+                    callee: NameAndSpan {
+                        format: MacroBang(extname),
+                        span: tt_span,
+                        allow_internal_unstable: allow_internal_unstable,
                     }
+                });
+
+                let marked_tts = mark_tts(tts, mark);
+                Some(expander.expand(fld.cx, call_site, ident, marked_tts))
+            }
+
+            MacroRulesTT => {
+                if ident.name == keywords::Invalid.name() {
+                    fld.cx.span_err(path.span,
+                                    &format!("macro {}! expects an ident argument", extname));
+                    return None;
                 };
-                Some(mark_thunk(parsed,fm))
+
+                fld.cx.bt_push(ExpnInfo {
+                    call_site: call_site,
+                    callee: NameAndSpan {
+                        format: MacroBang(extname),
+                        span: None,
+                        // `macro_rules!` doesn't directly allow unstable
+                        // (this is orthogonal to whether the macro it creates allows it)
+                        allow_internal_unstable: false,
+                    }
+                });
+
+                // DON'T mark before expansion.
+                fld.cx.insert_macro(ast::MacroDef {
+                    ident: ident,
+                    id: ast::DUMMY_NODE_ID,
+                    span: call_site,
+                    imported_from: None,
+                    use_locally: true,
+                    body: tts,
+                    export: attr::contains_name(&attrs, "macro_export"),
+                    allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
+                    attrs: attrs,
+                });
+
+                // macro_rules! has a side effect but expands to nothing.
+                fld.cx.bt_pop();
+                None
             }
-            _ => {
-                fld.cx.span_err(
-                    pth.span,
-                    &format!("'{}' is not a tt-style macro",
-                            extname));
+
+            MultiDecorator(..) | MultiModifier(..) => {
+                fld.cx.span_err(path.span,
+                                &format!("`{}` can only be used in attributes", extname));
                 None
             }
         }
     }
+
+    let opt_expanded = T::make_with(match mac_result(&path, ident, tts, mark, attrs, span, fld) {
+        Some(result) => result,
+        None => return T::dummy(span),
+    });
+
+    let expanded = if let Some(expanded) = opt_expanded {
+        expanded
+    } else {
+        let msg = format!("non-{kind} macro in {kind} position: {name}",
+                          name = path.segments[0].identifier.name, kind = T::kind_name());
+        fld.cx.span_err(path.span, &msg);
+        return T::dummy(span);
+    };
+
+    let marked = expanded.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) });
+    let configured = marked.fold_with(&mut fld.strip_unconfigured());
+    fld.load_macros(&configured);
+    let fully_expanded = configured.fold_with(fld);
+    fld.cx.bt_pop();
+    fully_expanded
 }
 
 /// Rename loop label and expand its loop body
@@ -277,12 +333,12 @@ fn expand_mac_invoc<T, F, G>(mac: ast::Mac,
 /// body is in a block enclosed by loop head so the renaming of loop label
 /// must be propagated to the enclosed context.
 fn expand_loop_block(loop_block: P<Block>,
-                     opt_ident: Option<Ident>,
-                     fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
+                     opt_ident: Option<SpannedIdent>,
+                     fld: &mut MacroExpander) -> (P<Block>, Option<SpannedIdent>) {
     match opt_ident {
         Some(label) => {
-            let new_label = fresh_name(label);
-            let rename = (label, new_label);
+            let new_label = fresh_name(label.node);
+            let rename = (label.node, new_label);
 
             // The rename *must not* be added to the pending list of current
             // syntax context otherwise an unrelated `break` or `continue` in
@@ -290,7 +346,7 @@ fn expand_loop_block(loop_block: P<Block>,
             // and be renamed incorrectly.
             let mut rename_list = vec!(rename);
             let mut rename_fld = IdentRenamer{renames: &mut rename_list};
-            let renamed_ident = rename_fld.fold_ident(label);
+            let renamed_ident = rename_fld.fold_ident(label.node);
 
             // The rename *must* be added to the enclosed syntax context for
             // `break` or `continue` to pick up because by definition they are
@@ -300,7 +356,7 @@ fn expand_loop_block(loop_block: P<Block>,
             let expanded_block = expand_block_elts(loop_block, fld);
             fld.cx.syntax_env.pop_frame();
 
-            (expanded_block, Some(renamed_ident))
+            (expanded_block, Some(Spanned { node: renamed_ident, span: label.span }))
         }
         None => (fld.fold_block(loop_block), opt_ident)
     }
@@ -367,141 +423,6 @@ fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool
     false
 }
 
-// Support for item-position macro invocations, exactly the same
-// logic as for expression-position macro invocations.
-pub fn expand_item_mac(it: P<ast::Item>,
-                       fld: &mut MacroExpander) -> SmallVector<P<ast::Item>> {
-    let (extname, path_span, tts, span, attrs, ident) = it.and_then(|it| match it.node {
-        ItemKind::Mac(codemap::Spanned { node: Mac_ { path, tts, .. }, .. }) =>
-            (path.segments[0].identifier.name, path.span, tts, it.span, it.attrs, it.ident),
-        _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
-    });
-
-    check_attributes(&attrs, fld);
-
-    let fm = fresh_mark();
-    let items = {
-        let expanded = match fld.cx.syntax_env.find(extname) {
-            None => {
-                fld.cx.span_err(path_span,
-                                &format!("macro undefined: '{}!'",
-                                        extname));
-                // let compilation continue
-                return SmallVector::zero();
-            }
-
-            Some(rc) => match *rc {
-                NormalTT(ref expander, tt_span, allow_internal_unstable) => {
-                    if ident.name != keywords::Invalid.name() {
-                        fld.cx
-                            .span_err(path_span,
-                                      &format!("macro {}! expects no ident argument, given '{}'",
-                                               extname,
-                                               ident));
-                        return SmallVector::zero();
-                    }
-                    fld.cx.bt_push(ExpnInfo {
-                        call_site: span,
-                        callee: NameAndSpan {
-                            format: MacroBang(extname),
-                            span: tt_span,
-                            allow_internal_unstable: allow_internal_unstable,
-                        }
-                    });
-                    // mark before expansion:
-                    let marked_before = mark_tts(&tts[..], fm);
-                    expander.expand(fld.cx, span, &marked_before[..])
-                }
-                IdentTT(ref expander, tt_span, allow_internal_unstable) => {
-                    if ident.name == keywords::Invalid.name() {
-                        fld.cx.span_err(path_span,
-                                        &format!("macro {}! expects an ident argument",
-                                                extname));
-                        return SmallVector::zero();
-                    }
-                    fld.cx.bt_push(ExpnInfo {
-                        call_site: span,
-                        callee: NameAndSpan {
-                            format: MacroBang(extname),
-                            span: tt_span,
-                            allow_internal_unstable: allow_internal_unstable,
-                        }
-                    });
-                    // mark before expansion:
-                    let marked_tts = mark_tts(&tts[..], fm);
-                    expander.expand(fld.cx, span, ident, marked_tts)
-                }
-                MacroRulesTT => {
-                    if ident.name == keywords::Invalid.name() {
-                        fld.cx.span_err(path_span, "macro_rules! expects an ident argument");
-                        return SmallVector::zero();
-                    }
-
-                    fld.cx.bt_push(ExpnInfo {
-                        call_site: span,
-                        callee: NameAndSpan {
-                            format: MacroBang(extname),
-                            span: None,
-                            // `macro_rules!` doesn't directly allow
-                            // unstable (this is orthogonal to whether
-                            // the macro it creates allows it)
-                            allow_internal_unstable: false,
-                        }
-                    });
-                    // DON'T mark before expansion.
-
-                    let allow_internal_unstable = attr::contains_name(&attrs,
-                                                                      "allow_internal_unstable");
-
-                    let export = attr::contains_name(&attrs, "macro_export");
-                    let def = ast::MacroDef {
-                        ident: ident,
-                        attrs: attrs,
-                        id: ast::DUMMY_NODE_ID,
-                        span: span,
-                        imported_from: None,
-                        export: export,
-                        use_locally: true,
-                        allow_internal_unstable: allow_internal_unstable,
-                        body: tts,
-                    };
-                    fld.cx.insert_macro(def);
-
-                    // macro_rules! has a side effect but expands to nothing.
-                    fld.cx.bt_pop();
-                    return SmallVector::zero();
-                }
-                _ => {
-                    fld.cx.span_err(span,
-                                    &format!("{}! is not legal in item position",
-                                            extname));
-                    return SmallVector::zero();
-                }
-            }
-        };
-
-        expanded.make_items()
-    };
-
-    let items = match items {
-        Some(items) => {
-            items.into_iter()
-                .map(|i| mark_item(i, fm))
-                .flat_map(|i| fld.fold_item(i).into_iter())
-                .collect()
-        }
-        None => {
-            fld.cx.span_err(path_span,
-                            &format!("non-item macro in item position: {}",
-                                    extname));
-            return SmallVector::zero();
-        }
-    };
-
-    fld.cx.bt_pop();
-    items
-}
-
 /// Expand a stmt
 fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
     // perform all pending renames
@@ -512,47 +433,25 @@ fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
     };
 
     let (mac, style, attrs) = match stmt.node {
-        StmtKind::Mac(mac, style, attrs) => (mac, style, attrs),
+        StmtKind::Mac(mac) => mac.unwrap(),
         _ => return expand_non_macro_stmt(stmt, fld)
     };
 
-    if let Some(ref attrs) = attrs {
-        check_attributes(attrs, fld);
-    }
-
-    // Assert that we drop any macro attributes on the floor here
-    drop(attrs);
-
-    let maybe_new_items =
-        expand_mac_invoc(mac.unwrap(), stmt.span,
-                         |r| r.make_stmts(),
-                         |stmts, mark| stmts.move_map(|m| mark_stmt(m, mark)),
-                         fld);
-
-    let mut fully_expanded = match maybe_new_items {
-        Some(stmts) => {
-            // Keep going, outside-in.
-            let new_items = stmts.into_iter().flat_map(|s| {
-                fld.fold_stmt(s).into_iter()
-            }).collect();
-            fld.cx.bt_pop();
-            new_items
-        }
-        None => SmallVector::zero()
-    };
+    let mut fully_expanded: SmallVector<ast::Stmt> =
+        expand_mac_invoc(mac, None, attrs.into(), stmt.span, fld);
 
     // If this is a macro invocation with a semicolon, then apply that
     // semicolon to the final statement produced by expansion.
     if style == MacStmtStyle::Semicolon {
         if let Some(stmt) = fully_expanded.pop() {
-            let new_stmt = Spanned {
+            fully_expanded.push(Stmt {
+                id: stmt.id,
                 node: match stmt.node {
-                    StmtKind::Expr(e, stmt_id) => StmtKind::Semi(e, stmt_id),
+                    StmtKind::Expr(expr) => StmtKind::Semi(expr),
                     _ => stmt.node /* might already have a semi */
                 },
-                span: stmt.span
-            };
-            fully_expanded.push(new_stmt);
+                span: stmt.span,
+            });
         }
     }
 
@@ -561,73 +460,53 @@ fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
 
 // expand a non-macro stmt. this is essentially the fallthrough for
 // expand_stmt, above.
-fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroExpander)
+fn expand_non_macro_stmt(stmt: Stmt, fld: &mut MacroExpander)
                          -> SmallVector<Stmt> {
     // is it a let?
-    match node {
-        StmtKind::Decl(decl, node_id) => decl.and_then(|Spanned {node: decl, span}| match decl {
-            DeclKind::Local(local) => {
-                // take it apart:
-                let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| {
-                    // expand the ty since TyKind::FixedLengthVec contains an Expr
-                    // and thus may have a macro use
-                    let expanded_ty = ty.map(|t| fld.fold_ty(t));
-                    // expand the pat (it might contain macro uses):
-                    let expanded_pat = fld.fold_pat(pat);
-                    // find the PatIdents in the pattern:
-                    // oh dear heaven... this is going to include the enum
-                    // names, as well... but that should be okay, as long as
-                    // the new names are gensyms for the old ones.
-                    // generate fresh names, push them to a new pending list
-                    let idents = pattern_bindings(&expanded_pat);
-                    let mut new_pending_renames =
-                        idents.iter().map(|ident| (*ident, fresh_name(*ident))).collect();
-                    // rewrite the pattern using the new names (the old
-                    // ones have already been applied):
-                    let rewritten_pat = {
-                        // nested binding to allow borrow to expire:
-                        let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
-                        rename_fld.fold_pat(expanded_pat)
-                    };
-                    // add them to the existing pending renames:
-                    fld.cx.syntax_env.info().pending_renames
-                          .extend(new_pending_renames);
-                    Local {
-                        id: id,
-                        ty: expanded_ty,
-                        pat: rewritten_pat,
-                        // also, don't forget to expand the init:
-                        init: init.map(|e| fld.fold_expr(e)),
-                        span: span,
-                        attrs: fold::fold_thin_attrs(attrs, fld),
-                    }
-                });
-                SmallVector::one(Spanned {
-                    node: StmtKind::Decl(P(Spanned {
-                            node: DeclKind::Local(rewritten_local),
-                            span: span
-                        }),
-                        node_id),
-                    span: stmt_span
-                })
-            }
-            _ => {
-                noop_fold_stmt(Spanned {
-                    node: StmtKind::Decl(P(Spanned {
-                            node: decl,
-                            span: span
-                        }),
-                        node_id),
-                    span: stmt_span
-                }, fld)
-            }
-        }),
-        _ => {
-            noop_fold_stmt(Spanned {
-                node: node,
-                span: stmt_span
-            }, fld)
+    match stmt.node {
+        StmtKind::Local(local) => {
+            // take it apart:
+            let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| {
+                // expand the ty since TyKind::FixedLengthVec contains an Expr
+                // and thus may have a macro use
+                let expanded_ty = ty.map(|t| fld.fold_ty(t));
+                // expand the pat (it might contain macro uses):
+                let expanded_pat = fld.fold_pat(pat);
+                // find the PatIdents in the pattern:
+                // oh dear heaven... this is going to include the enum
+                // names, as well... but that should be okay, as long as
+                // the new names are gensyms for the old ones.
+                // generate fresh names, push them to a new pending list
+                let idents = pattern_bindings(&expanded_pat);
+                let mut new_pending_renames =
+                    idents.iter().map(|ident| (*ident, fresh_name(*ident))).collect();
+                // rewrite the pattern using the new names (the old
+                // ones have already been applied):
+                let rewritten_pat = {
+                    // nested binding to allow borrow to expire:
+                    let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
+                    rename_fld.fold_pat(expanded_pat)
+                };
+                // add them to the existing pending renames:
+                fld.cx.syntax_env.info().pending_renames
+                      .extend(new_pending_renames);
+                Local {
+                    id: id,
+                    ty: expanded_ty,
+                    pat: rewritten_pat,
+                    // also, don't forget to expand the init:
+                    init: init.map(|e| fld.fold_expr(e)),
+                    span: span,
+                    attrs: fold::fold_thin_attrs(attrs, fld),
+                }
+            });
+            SmallVector::one(Stmt {
+                id: stmt.id,
+                node: StmtKind::Local(rewritten_local),
+                span: stmt.span,
+            })
         }
+        _ => noop_fold_stmt(stmt, fld),
     }
 }
 
@@ -685,7 +564,7 @@ struct PatIdentFinder {
     ident_accumulator: Vec<ast::Ident>
 }
 
-impl<'v> Visitor<'v> for PatIdentFinder {
+impl Visitor for PatIdentFinder {
     fn visit_pat(&mut self, pattern: &ast::Pat) {
         match *pattern {
             ast::Pat { id: _, node: PatKind::Ident(_, ref path1, ref inner), span: _ } => {
@@ -726,23 +605,14 @@ pub fn expand_block(blk: P<Block>, fld: &mut MacroExpander) -> P<Block> {
 
 // expand the elements of a block.
 pub fn expand_block_elts(b: P<Block>, fld: &mut MacroExpander) -> P<Block> {
-    b.map(|Block {id, stmts, expr, rules, span}| {
+    b.map(|Block {id, stmts, rules, span}| {
         let new_stmts = stmts.into_iter().flat_map(|x| {
             // perform pending renames and expand macros in the statement
             fld.fold_stmt(x).into_iter()
         }).collect();
-        let new_expr = expr.map(|x| {
-            let expr = {
-                let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
-                let mut rename_fld = IdentRenamer{renames:pending_renames};
-                rename_fld.fold_expr(x)
-            };
-            fld.fold_expr(expr)
-        });
         Block {
             id: fld.new_id(id),
             stmts: new_stmts,
-            expr: new_expr,
             rules: rules,
             span: span
         }
@@ -754,76 +624,10 @@ fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
         PatKind::Mac(_) => {}
         _ => return noop_fold_pat(p, fld)
     }
-    p.map(|ast::Pat {node, span, ..}| {
-        let (pth, tts) = match node {
-            PatKind::Mac(mac) => (mac.node.path, mac.node.tts),
+    p.and_then(|ast::Pat {node, span, ..}| {
+        match node {
+            PatKind::Mac(mac) => expand_mac_invoc(mac, None, Vec::new(), span, fld),
             _ => unreachable!()
-        };
-        if pth.segments.len() > 1 {
-            fld.cx.span_err(pth.span, "expected macro name without module separators");
-            return DummyResult::raw_pat(span);
-        }
-        let extname = pth.segments[0].identifier.name;
-        let marked_after = match fld.cx.syntax_env.find(extname) {
-            None => {
-                fld.cx.span_err(pth.span,
-                                &format!("macro undefined: '{}!'",
-                                        extname));
-                // let compilation continue
-                return DummyResult::raw_pat(span);
-            }
-
-            Some(rc) => match *rc {
-                NormalTT(ref expander, tt_span, allow_internal_unstable) => {
-                    fld.cx.bt_push(ExpnInfo {
-                        call_site: span,
-                        callee: NameAndSpan {
-                            format: MacroBang(extname),
-                            span: tt_span,
-                            allow_internal_unstable: allow_internal_unstable,
-                        }
-                    });
-
-                    let fm = fresh_mark();
-                    let marked_before = mark_tts(&tts[..], fm);
-                    let mac_span = fld.cx.original_span();
-                    let pat = expander.expand(fld.cx,
-                                              mac_span,
-                                              &marked_before[..]).make_pat();
-                    let expanded = match pat {
-                        Some(e) => e,
-                        None => {
-                            fld.cx.span_err(
-                                pth.span,
-                                &format!(
-                                    "non-pattern macro in pattern position: {}",
-                                    extname
-                                    )
-                            );
-                            return DummyResult::raw_pat(span);
-                        }
-                    };
-
-                    // mark after:
-                    mark_pat(expanded,fm)
-                }
-                _ => {
-                    fld.cx.span_err(span,
-                                    &format!("{}! is not legal in pattern position",
-                                            extname));
-                    return DummyResult::raw_pat(span);
-                }
-            }
-        };
-
-        let fully_expanded =
-            fld.fold_pat(marked_after).node.clone();
-        fld.cx.bt_pop();
-
-        ast::Pat {
-            id: ast::DUMMY_NODE_ID,
-            node: fully_expanded,
-            span: span
         }
     })
 }
@@ -837,7 +641,7 @@ pub struct IdentRenamer<'a> {
 
 impl<'a> Folder for IdentRenamer<'a> {
     fn fold_ident(&mut self, id: Ident) -> Ident {
-        Ident::new(id.name, mtwt::apply_renames(self.renames, id.ctxt))
+        mtwt::apply_renames(self.renames, id)
     }
     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
         fold::noop_fold_mac(mac, self)
@@ -861,16 +665,15 @@ impl<'a> Folder for PatIdentRenamer<'a> {
 
         pat.map(|ast::Pat {id, node, span}| match node {
             PatKind::Ident(binding_mode, Spanned{span: sp, node: ident}, sub) => {
-                let new_ident = Ident::new(ident.name,
-                                           mtwt::apply_renames(self.renames, ident.ctxt));
+                let new_ident = mtwt::apply_renames(self.renames, ident);
                 let new_node =
                     PatKind::Ident(binding_mode,
-                                  Spanned{span: self.new_span(sp), node: new_ident},
+                                  Spanned{span: sp, node: new_ident},
                                   sub.map(|p| self.fold_pat(p)));
                 ast::Pat {
                     id: id,
                     node: new_node,
-                    span: self.new_span(span)
+                    span: span,
                 }
             },
             _ => unreachable!()
@@ -881,19 +684,15 @@ impl<'a> Folder for PatIdentRenamer<'a> {
     }
 }
 
-fn expand_annotatable(a: Annotatable,
-                      fld: &mut MacroExpander)
-                      -> SmallVector<Annotatable> {
-    let a = expand_item_multi_modifier(a, fld);
-
-    let mut decorator_items = SmallVector::zero();
-    let mut new_attrs = Vec::new();
-    expand_decorators(a.clone(), fld, &mut decorator_items, &mut new_attrs);
-
-    let mut new_items: SmallVector<Annotatable> = match a {
+fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
+    match a {
         Annotatable::Item(it) => match it.node {
             ast::ItemKind::Mac(..) => {
-                expand_item_mac(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
+                it.and_then(|it| match it.node {
+                    ItemKind::Mac(mac) =>
+                        expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld),
+                    _ => unreachable!(),
+                })
             }
             ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => {
                 let valid_ident =
@@ -902,160 +701,77 @@ fn expand_annotatable(a: Annotatable,
                 if valid_ident {
                     fld.cx.mod_push(it.ident);
                 }
-                let macro_use = contains_macro_use(fld, &new_attrs[..]);
+                let macro_use = contains_macro_use(fld, &it.attrs);
                 let result = with_exts_frame!(fld.cx.syntax_env,
                                               macro_use,
                                               noop_fold_item(it, fld));
                 if valid_ident {
                     fld.cx.mod_pop();
                 }
-                result.into_iter().map(|i| Annotatable::Item(i)).collect()
+                result
             },
-            _ => {
-                let it = P(ast::Item {
-                    attrs: new_attrs,
-                    ..(*it).clone()
-                });
-                noop_fold_item(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
-            }
-        },
-
-        Annotatable::TraitItem(it) => match it.node {
-            ast::TraitItemKind::Method(_, Some(_)) => {
-                let ti = it.unwrap();
-                SmallVector::one(ast::TraitItem {
-                    id: ti.id,
-                    ident: ti.ident,
-                    attrs: ti.attrs,
-                    node: match ti.node  {
-                        ast::TraitItemKind::Method(sig, Some(body)) => {
-                            let (sig, body) = expand_and_rename_method(sig, body, fld);
-                            ast::TraitItemKind::Method(sig, Some(body))
-                        }
-                        _ => unreachable!()
-                    },
-                    span: fld.new_span(ti.span)
-                })
-            }
-            _ => fold::noop_fold_trait_item(it.unwrap(), fld)
-        }.into_iter().map(|ti| Annotatable::TraitItem(P(ti))).collect(),
+            _ => noop_fold_item(it, fld),
+        }.into_iter().map(|i| Annotatable::Item(i)).collect(),
+
+        Annotatable::TraitItem(it) => {
+            expand_trait_item(it.unwrap(), fld).into_iter().
+                map(|it| Annotatable::TraitItem(P(it))).collect()
+        }
 
         Annotatable::ImplItem(ii) => {
             expand_impl_item(ii.unwrap(), fld).into_iter().
                 map(|ii| Annotatable::ImplItem(P(ii))).collect()
         }
-    };
-
-    new_items.push_all(decorator_items);
-    new_items
+    }
 }
 
-// Partition a set of attributes into one kind of attribute, and other kinds.
-macro_rules! partition {
-    ($fn_name: ident, $variant: ident) => {
-        #[allow(deprecated)] // The `allow` is needed because the `Modifier` variant might be used.
-        fn $fn_name(attrs: &[ast::Attribute],
-                    fld: &MacroExpander)
-                     -> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
-            attrs.iter().cloned().partition(|attr| {
-                match fld.cx.syntax_env.find(intern(&attr.name())) {
-                    Some(rc) => match *rc {
-                        $variant(..) => true,
-                        _ => false
-                    },
-                    _ => false
+fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
+    let mut multi_modifier = None;
+    item = item.map_attrs(|mut attrs| {
+        for i in 0..attrs.len() {
+            if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) {
+                match *extension {
+                    MultiModifier(..) | MultiDecorator(..) => {
+                        multi_modifier = Some((attrs.remove(i), extension));
+                        break;
+                    }
+                    _ => {}
                 }
-            })
+            }
         }
-    }
-}
-
-partition!(multi_modifiers, MultiModifier);
-
+        attrs
+    });
 
-fn expand_decorators(a: Annotatable,
-                     fld: &mut MacroExpander,
-                     decorator_items: &mut SmallVector<Annotatable>,
-                     new_attrs: &mut Vec<ast::Attribute>)
-{
-    for attr in a.attrs() {
-        let mname = intern(&attr.name());
-        match fld.cx.syntax_env.find(mname) {
-            Some(rc) => match *rc {
-                MultiDecorator(ref dec) => {
-                    attr::mark_used(&attr);
-
-                    fld.cx.bt_push(ExpnInfo {
-                        call_site: attr.span,
-                        callee: NameAndSpan {
-                            format: MacroAttribute(mname),
-                            span: Some(attr.span),
-                            // attributes can do whatever they like,
-                            // for now.
-                            allow_internal_unstable: true,
-                        }
-                    });
-
-                    // we'd ideally decorator_items.push_all(expand_annotatable(ann, fld)),
-                    // but that double-mut-borrows fld
-                    let mut items: SmallVector<Annotatable> = SmallVector::zero();
-                    dec.expand(fld.cx,
-                               attr.span,
-                               &attr.node.value,
-                               &a,
-                               &mut |ann| items.push(ann));
-                    decorator_items.extend(items.into_iter()
-                        .flat_map(|ann| expand_annotatable(ann, fld).into_iter()));
-
-                    fld.cx.bt_pop();
+    match multi_modifier {
+        None => expand_multi_modified(item, fld),
+        Some((attr, extension)) => {
+            attr::mark_used(&attr);
+            fld.cx.bt_push(ExpnInfo {
+                call_site: attr.span,
+                callee: NameAndSpan {
+                    format: MacroAttribute(intern(&attr.name())),
+                    span: Some(attr.span),
+                    // attributes can do whatever they like, for now
+                    allow_internal_unstable: true,
                 }
-                _ => new_attrs.push((*attr).clone()),
-            },
-            _ => new_attrs.push((*attr).clone()),
-        }
-    }
-}
+            });
 
-fn expand_item_multi_modifier(mut it: Annotatable,
-                              fld: &mut MacroExpander)
-                              -> Annotatable {
-    let (modifiers, other_attrs) = multi_modifiers(it.attrs(), fld);
-
-    // Update the attrs, leave everything else alone. Is this mutation really a good idea?
-    it = it.fold_attrs(other_attrs);
-
-    if modifiers.is_empty() {
-        return it
-    }
-
-    for attr in &modifiers {
-        let mname = intern(&attr.name());
-
-        match fld.cx.syntax_env.find(mname) {
-            Some(rc) => match *rc {
-                MultiModifier(ref mac) => {
-                    attr::mark_used(attr);
-                    fld.cx.bt_push(ExpnInfo {
-                        call_site: attr.span,
-                        callee: NameAndSpan {
-                            format: MacroAttribute(mname),
-                            span: Some(attr.span),
-                            // attributes can do whatever they like,
-                            // for now
-                            allow_internal_unstable: true,
-                        }
-                    });
-                    it = mac.expand(fld.cx, attr.span, &attr.node.value, it);
-                    fld.cx.bt_pop();
+            let modified = match *extension {
+                MultiModifier(ref mac) => mac.expand(fld.cx, attr.span, &attr.node.value, item),
+                MultiDecorator(ref mac) => {
+                    let mut items = Vec::new();
+                    mac.expand(fld.cx, attr.span, &attr.node.value, &item,
+                               &mut |item| items.push(item));
+                    items.push(item);
+                    items
                 }
-                _ => unreachable!()
-            },
-            _ => unreachable!()
+                _ => unreachable!(),
+            };
+
+            fld.cx.bt_pop();
+            modified.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
         }
     }
-
-    // Expansion may have added new ItemKind::Modifiers.
-    expand_item_multi_modifier(it, fld)
 }
 
 fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
@@ -1074,33 +790,40 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
                 }
                 _ => unreachable!()
             },
-            span: fld.new_span(ii.span)
+            span: ii.span,
         }),
         ast::ImplItemKind::Macro(mac) => {
-            check_attributes(&ii.attrs, fld);
-
-            let maybe_new_items =
-                expand_mac_invoc(mac, ii.span,
-                                 |r| r.make_impl_items(),
-                                 |meths, mark| meths.move_map(|m| mark_impl_item(m, mark)),
-                                 fld);
-
-            match maybe_new_items {
-                Some(impl_items) => {
-                    // expand again if necessary
-                    let new_items = impl_items.into_iter().flat_map(|ii| {
-                        expand_impl_item(ii, fld).into_iter()
-                    }).collect();
-                    fld.cx.bt_pop();
-                    new_items
-                }
-                None => SmallVector::zero()
-            }
+            expand_mac_invoc(mac, None, ii.attrs, ii.span, fld)
         }
         _ => fold::noop_fold_impl_item(ii, fld)
     }
 }
 
+fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
+                     -> SmallVector<ast::TraitItem> {
+    match ti.node {
+        ast::TraitItemKind::Method(_, Some(_)) => {
+            SmallVector::one(ast::TraitItem {
+                id: ti.id,
+                ident: ti.ident,
+                attrs: ti.attrs,
+                node: match ti.node  {
+                    ast::TraitItemKind::Method(sig, Some(body)) => {
+                        let (sig, body) = expand_and_rename_method(sig, body, fld);
+                        ast::TraitItemKind::Method(sig, Some(body))
+                    }
+                    _ => unreachable!()
+                },
+                span: ti.span,
+            })
+        }
+        ast::TraitItemKind::Macro(mac) => {
+            expand_mac_invoc(mac, None, ti.attrs, ti.span, fld)
+        }
+        _ => fold::noop_fold_trait_item(ti, fld)
+    }
+}
+
 /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
 /// PatIdents in its arguments to perform renaming in the FnDecl and
 /// the block, returning both the new FnDecl and the new Block.
@@ -1128,7 +851,6 @@ fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>,
     (ast::MethodSig {
         generics: fld.fold_generics(sig.generics),
         abi: sig.abi,
-        explicit_self: fld.fold_explicit_self(sig.explicit_self),
         unsafety: sig.unsafety,
         constness: sig.constness,
         decl: rewritten_fn_decl
@@ -1139,25 +861,7 @@ pub fn expand_type(t: P<ast::Ty>, fld: &mut MacroExpander) -> P<ast::Ty> {
     let t = match t.node.clone() {
         ast::TyKind::Mac(mac) => {
             if fld.cx.ecfg.features.unwrap().type_macros {
-                let expanded_ty = match expand_mac_invoc(mac, t.span,
-                                                         |r| r.make_ty(),
-                                                         mark_ty,
-                                                         fld) {
-                    Some(ty) => ty,
-                    None => {
-                        return DummyResult::raw_ty(t.span);
-                    }
-                };
-
-                // Keep going, outside-in.
-                let fully_expanded = fld.fold_ty(expanded_ty);
-                fld.cx.bt_pop();
-
-                fully_expanded.map(|t| ast::Ty {
-                    id: ast::DUMMY_NODE_ID,
-                    node: t.node,
-                    span: t.span,
-                    })
+                expand_mac_invoc(mac, None, Vec::new(), t.span, fld)
             } else {
                 feature_gate::emit_feature_err(
                     &fld.cx.parse_sess.span_diagnostic,
@@ -1184,6 +888,49 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
         MacroExpander { cx: cx }
     }
+
+    fn strip_unconfigured(&mut self) -> StripUnconfigured {
+        StripUnconfigured {
+            config: &self.cx.cfg,
+            should_test: self.cx.ecfg.should_test,
+            sess: self.cx.parse_sess,
+            features: self.cx.ecfg.features,
+        }
+    }
+
+    fn load_macros<T: MacroGenerable>(&mut self, node: &T) {
+        struct MacroLoadingVisitor<'a, 'b: 'a>{
+            cx: &'a mut ExtCtxt<'b>,
+            at_crate_root: bool,
+        }
+
+        impl<'a, 'b> Visitor for MacroLoadingVisitor<'a, 'b> {
+            fn visit_mac(&mut self, _: &ast::Mac) {}
+            fn visit_item(&mut self, item: &ast::Item) {
+                if let ast::ItemKind::ExternCrate(..) = item.node {
+                    // We need to error on `#[macro_use] extern crate` when it isn't at the
+                    // crate root, because `$crate` won't work properly.
+                    for def in self.cx.loader.load_crate(item, self.at_crate_root) {
+                        self.cx.insert_macro(def);
+                    }
+                } else {
+                    let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
+                    visit::walk_item(self, item);
+                    self.at_crate_root = at_crate_root;
+                }
+            }
+            fn visit_block(&mut self, block: &ast::Block) {
+                let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
+                visit::walk_block(self, block);
+                self.at_crate_root = at_crate_root;
+            }
+        }
+
+        node.visit_with(&mut MacroLoadingVisitor {
+            at_crate_root: self.cx.syntax_env.is_crate_root(),
+            cx: self.cx,
+        });
+    }
 }
 
 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
@@ -1193,7 +940,15 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
     }
 
     fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
-        expand_expr(expr, self)
+        expr.and_then(|expr| expand_expr(expr, self))
+    }
+
+    fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
+        expr.and_then(|expr| match expr.node {
+            ast::ExprKind::Mac(mac) =>
+                expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, self),
+            _ => Some(expand_expr(expr, self)),
+        })
     }
 
     fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
@@ -1209,7 +964,7 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
                 result = expand_item(item, self);
                 self.pop_mod_path();
             } else {
-                let filename = if inner != codemap::DUMMY_SP {
+                let filename = if inner != syntax_pos::DUMMY_SP {
                     Some(self.cx.parse_sess.codemap().span_to_filename(inner))
                 } else { None };
                 let orig_filename = replace(&mut self.cx.filename, filename);
@@ -1256,10 +1011,6 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
     fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
         expand_type(ty, self)
     }
-
-    fn new_span(&mut self, span: Span) -> Span {
-        new_span(self.cx, span)
-    }
 }
 
 impl<'a, 'b> MacroExpander<'a, 'b> {
@@ -1277,50 +1028,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     }
 }
 
-fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
-    debug!("new_span(sp={:?})", sp);
-
-    if cx.codemap().more_specific_trace(sp.expn_id, cx.backtrace()) {
-        // If the span we are looking at has a backtrace that has more
-        // detail than our current backtrace, then we keep that
-        // backtrace.  Honestly, I have no idea if this makes sense,
-        // because I have no idea why we are stripping the backtrace
-        // below. But the reason I made this change is because, in
-        // deriving, we were generating attributes with a specific
-        // backtrace, which was essential for `#[structural_match]` to
-        // be properly supported, but these backtraces were being
-        // stripped and replaced with a null backtrace. Sort of
-        // unclear why this is the case. --nmatsakis
-        debug!("new_span: keeping trace from {:?} because it is more specific",
-               sp.expn_id);
-        sp
-    } else {
-        // This discards information in the case of macro-defining macros.
-        //
-        // The comment above was originally added in
-        // b7ec2488ff2f29681fe28691d20fd2c260a9e454 in Feb 2012. I
-        // *THINK* the reason we are doing this is because we want to
-        // replace the backtrace of the macro contents with the
-        // backtrace that contains the macro use. But it's pretty
-        // unclear to me. --nmatsakis
-        let sp1 = Span {
-            lo: sp.lo,
-            hi: sp.hi,
-            expn_id: cx.backtrace(),
-        };
-        debug!("new_span({:?}) = {:?}", sp, sp1);
-        if sp.expn_id.into_u32() == 0 && env::var_os("NDM").is_some() {
-            panic!("NDM");
-        }
-        sp1
-    }
-}
-
 pub struct ExpansionConfig<'feat> {
     pub crate_name: String,
     pub features: Option<&'feat Features>,
     pub recursion_limit: usize,
     pub trace_mac: bool,
+    pub should_test: bool, // If false, strip `#[test]` nodes
 }
 
 macro_rules! feature_tests {
@@ -1343,6 +1056,7 @@ impl<'feat> ExpansionConfig<'feat> {
             features: None,
             recursion_limit: 64,
             trace_mac: false,
+            should_test: false,
         }
     }
 
@@ -1359,10 +1073,8 @@ impl<'feat> ExpansionConfig<'feat> {
 }
 
 pub fn expand_crate(mut cx: ExtCtxt,
-                    // these are the macros being imported to this crate:
-                    imported_macros: Vec<ast::MacroDef>,
                     user_exts: Vec<NamedSyntaxExtension>,
-                    c: Crate) -> (Crate, HashSet<Name>) {
+                    mut c: Crate) -> (Crate, HashSet<Name>) {
     if std_inject::no_core(&c) {
         cx.crate_root = None;
     } else if std_inject::no_std(&c) {
@@ -1373,14 +1085,14 @@ pub fn expand_crate(mut cx: ExtCtxt,
     let ret = {
         let mut expander = MacroExpander::new(&mut cx);
 
-        for def in imported_macros {
-            expander.cx.insert_macro(def);
-        }
-
         for (name, extension) in user_exts {
             expander.cx.syntax_env.insert(name, extension);
         }
 
+        let items = SmallVector::many(c.module.items);
+        expander.load_macros(&items);
+        c.module.items = items.into();
+
         let err_count = cx.parse_sess.span_diagnostic.err_count();
         let mut ret = expander.fold_crate(c);
         ret.exported_macros = expander.cx.exported_macros.clone();
@@ -1402,8 +1114,9 @@ pub fn expand_crate(mut cx: ExtCtxt,
 // the ones defined here include:
 // Marker - add a mark to a context
 
-// A Marker adds the given mark to the syntax context
-struct Marker { mark: Mrk }
+// A Marker adds the given mark to the syntax context and
+// sets spans' `expn_id` to the given expn_id (unless it is `None`).
+struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
 
 impl Folder for Marker {
     fn fold_ident(&mut self, id: Ident) -> Ident {
@@ -1413,67 +1126,23 @@ impl Folder for Marker {
         Spanned {
             node: Mac_ {
                 path: self.fold_path(node.path),
-                tts: self.fold_tts(&node.tts),
-                ctxt: mtwt::apply_mark(self.mark, node.ctxt),
+                tts: self.fold_tts(node.tts),
             },
-            span: span,
+            span: self.new_span(span),
         }
     }
-}
-
-// apply a given mark to the given token trees. Used prior to expansion of a macro.
-fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
-    noop_fold_tts(tts, &mut Marker{mark:m})
-}
-
-// apply a given mark to the given expr. Used following the expansion of a macro.
-fn mark_expr(expr: P<ast::Expr>, m: Mrk) -> P<ast::Expr> {
-    Marker{mark:m}.fold_expr(expr)
-}
-
-// apply a given mark to the given pattern. Used following the expansion of a macro.
-fn mark_pat(pat: P<ast::Pat>, m: Mrk) -> P<ast::Pat> {
-    Marker{mark:m}.fold_pat(pat)
-}
-
-// apply a given mark to the given stmt. Used following the expansion of a macro.
-fn mark_stmt(stmt: ast::Stmt, m: Mrk) -> ast::Stmt {
-    Marker{mark:m}.fold_stmt(stmt)
-        .expect_one("marking a stmt didn't return exactly one stmt")
-}
-
-// apply a given mark to the given item. Used following the expansion of a macro.
-fn mark_item(expr: P<ast::Item>, m: Mrk) -> P<ast::Item> {
-    Marker{mark:m}.fold_item(expr)
-        .expect_one("marking an item didn't return exactly one item")
-}
-
-// apply a given mark to the given item. Used following the expansion of a macro.
-fn mark_impl_item(ii: ast::ImplItem, m: Mrk) -> ast::ImplItem {
-    Marker{mark:m}.fold_impl_item(ii)
-        .expect_one("marking an impl item didn't return exactly one impl item")
-}
-
-fn mark_ty(ty: P<ast::Ty>, m: Mrk) -> P<ast::Ty> {
-    Marker { mark: m }.fold_ty(ty)
-}
-
-/// Check that there are no macro invocations left in the AST:
-pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) {
-    visit::walk_crate(&mut MacroExterminator{sess:sess}, krate);
-}
 
-/// A visitor that ensures that no macro invocations remain in an AST.
-struct MacroExterminator<'a>{
-    sess: &'a parse::ParseSess
+    fn new_span(&mut self, mut span: Span) -> Span {
+        if let Some(expn_id) = self.expn_id {
+            span.expn_id = expn_id;
+        }
+        span
+    }
 }
 
-impl<'a, 'v> Visitor<'v> for MacroExterminator<'a> {
-    fn visit_mac(&mut self, mac: &ast::Mac) {
-        self.sess.span_diagnostic.span_bug(mac.span,
-                                           "macro exterminator: expected AST \
-                                           with no macro invocations");
-    }
+// apply a given mark to the given token trees. Used prior to expansion of a macro.
+fn mark_tts(tts: Vec<TokenTree>, m: Mrk) -> Vec<TokenTree> {
+    noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
 }
 
 
@@ -1483,12 +1152,12 @@ mod tests {
     use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig};
     use ast;
     use ast::Name;
-    use codemap;
-    use ext::base::ExtCtxt;
+    use syntax_pos;
+    use ext::base::{ExtCtxt, DummyMacroLoader};
     use ext::mtwt;
     use fold::Folder;
     use parse;
-    use parse::token::{self, keywords};
+    use parse::token;
     use util::parser_testing::{string_to_parser};
     use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
     use visit;
@@ -1502,7 +1171,7 @@ mod tests {
         path_accumulator: Vec<ast::Path> ,
     }
 
-    impl<'v> Visitor<'v> for PathExprFinderContext {
+    impl Visitor for PathExprFinderContext {
         fn visit_expr(&mut self, expr: &ast::Expr) {
             if let ast::ExprKind::Path(None, ref p) = expr.node {
                 self.path_accumulator.push(p.clone());
@@ -1524,8 +1193,8 @@ mod tests {
         ident_accumulator: Vec<ast::Ident>
     }
 
-    impl<'v> Visitor<'v> for IdentFinder {
-        fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident){
+    impl Visitor for IdentFinder {
+        fn visit_ident(&mut self, _: syntax_pos::Span, id: ast::Ident){
             self.ident_accumulator.push(id);
         }
     }
@@ -1555,9 +1224,9 @@ mod tests {
             src,
             Vec::new(), &sess).unwrap();
         // should fail:
-        let mut gated_cfgs = vec![];
-        let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs);
-        expand_crate(ecx, vec![], vec![], crate_ast);
+        let mut loader = DummyMacroLoader;
+        let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
+        expand_crate(ecx, vec![], crate_ast);
     }
 
     // make sure that macros can't escape modules
@@ -1570,9 +1239,9 @@ mod tests {
             "<test>".to_string(),
             src,
             Vec::new(), &sess).unwrap();
-        let mut gated_cfgs = vec![];
-        let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs);
-        expand_crate(ecx, vec![], vec![], crate_ast);
+        let mut loader = DummyMacroLoader;
+        let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
+        expand_crate(ecx, vec![], crate_ast);
     }
 
     // macro_use modules should allow macros to escape
@@ -1584,18 +1253,18 @@ mod tests {
             "<test>".to_string(),
             src,
             Vec::new(), &sess).unwrap();
-        let mut gated_cfgs = vec![];
-        let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut gated_cfgs);
-        expand_crate(ecx, vec![], vec![], crate_ast);
+        let mut loader = DummyMacroLoader;
+        let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
+        expand_crate(ecx, vec![], crate_ast);
     }
 
     fn expand_crate_str(crate_str: String) -> ast::Crate {
         let ps = parse::ParseSess::new();
         let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
         // the cfg argument actually does matter, here...
-        let mut gated_cfgs = vec![];
-        let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut gated_cfgs);
-        expand_crate(ecx, vec![], vec![], crate_ast).0
+        let mut loader = DummyMacroLoader;
+        let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader);
+        expand_crate(ecx, vec![], crate_ast).0
     }
 
     // find the pat_ident paths in a crate
@@ -1618,267 +1287,10 @@ mod tests {
             );
     }
 
-    // renaming tests expand a crate and then check that the bindings match
-    // the right varrefs. The specification of the test case includes the
-    // text of the crate, and also an array of arrays.  Each element in the
-    // outer array corresponds to a binding in the traversal of the AST
-    // induced by visit.  Each of these arrays contains a list of indexes,
-    // interpreted as the varrefs in the varref traversal that this binding
-    // should match.  So, for instance, in a program with two bindings and
-    // three varrefs, the array [[1, 2], [0]] would indicate that the first
-    // binding should match the second two varrefs, and the second binding
-    // should match the first varref.
-    //
-    // Put differently; this is a sparse representation of a boolean matrix
-    // indicating which bindings capture which identifiers.
-    //
-    // Note also that this matrix is dependent on the implicit ordering of
-    // the bindings and the varrefs discovered by the name-finder and the path-finder.
-    //
-    // The comparisons are done post-mtwt-resolve, so we're comparing renamed
-    // names; differences in marks don't matter any more.
-    //
-    // oog... I also want tests that check "bound-identifier-=?". That is,
-    // not just "do these have the same name", but "do they have the same
-    // name *and* the same marks"? Understanding this is really pretty painful.
-    // in principle, you might want to control this boolean on a per-varref basis,
-    // but that would make things even harder to understand, and might not be
-    // necessary for thorough testing.
-    type RenamingTest = (&'static str, Vec<Vec<usize>>, bool);
-
-    #[test]
-    fn automatic_renaming () {
-        let tests: Vec<RenamingTest> =
-            vec!(// b & c should get new names throughout, in the expr too:
-                ("fn a() -> i32 { let b = 13; let c = b; b+c }",
-                 vec!(vec!(0,1),vec!(2)), false),
-                // both x's should be renamed (how is this causing a bug?)
-                ("fn main () {let x: i32 = 13;x;}",
-                 vec!(vec!(0)), false),
-                // the use of b after the + should be renamed, the other one not:
-                ("macro_rules! f (($x:ident) => (b + $x)); fn a() -> i32 { let b = 13; f!(b)}",
-                 vec!(vec!(1)), false),
-                // the b before the plus should not be renamed (requires marks)
-                ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> i32 { f!(b)}",
-                 vec!(vec!(1)), false),
-                // the marks going in and out of letty should cancel, allowing that $x to
-                // capture the one following the semicolon.
-                // this was an awesome test case, and caught a *lot* of bugs.
-                ("macro_rules! letty(($x:ident) => (let $x = 15;));
-                  macro_rules! user(($x:ident) => ({letty!($x); $x}));
-                  fn main() -> i32 {user!(z)}",
-                 vec!(vec!(0)), false)
-                );
-        for (idx,s) in tests.iter().enumerate() {
-            run_renaming_test(s,idx);
-        }
-    }
-
-    // no longer a fixme #8062: this test exposes a *potential* bug; our system does
-    // not behave exactly like MTWT, but a conversation with Matthew Flatt
-    // suggests that this can only occur in the presence of local-expand, which
-    // we have no plans to support. ... unless it's needed for item hygiene....
-    #[ignore]
-    #[test]
-    fn issue_8062(){
-        run_renaming_test(
-            &("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
-              vec!(vec!(0)), true), 0)
-    }
-
-    // FIXME #6994:
-    // the z flows into and out of two macros (g & f) along one path, and one
-    // (just g) along the other, so the result of the whole thing should
-    // be "let z_123 = 3; z_123"
-    #[ignore]
-    #[test]
-    fn issue_6994(){
-        run_renaming_test(
-            &("macro_rules! g (($x:ident) =>
-              ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}));
-              fn a(){g!(z)}",
-              vec!(vec!(0)),false),
-            0)
-    }
-
-    // match variable hygiene. Should expand into
-    // fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
-    #[test]
-    fn issue_9384(){
-        run_renaming_test(
-            &("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}));
-              fn z() {match 8 {x => bad_macro!(x)}}",
-              // NB: the third "binding" is the repeat of the second one.
-              vec!(vec!(1,3),vec!(0,2),vec!(0,2)),
-              true),
-            0)
-    }
-
-    // interpolated nodes weren't getting labeled.
-    // should expand into
-    // fn main(){let g1_1 = 13; g1_1}}
-    #[test]
-    fn pat_expand_issue_15221(){
-        run_renaming_test(
-            &("macro_rules! inner ( ($e:pat ) => ($e));
-              macro_rules! outer ( ($e:pat ) => (inner!($e)));
-              fn main() { let outer!(g) = 13; g;}",
-              vec!(vec!(0)),
-              true),
-            0)
-    }
-
     // create a really evil test case where a $x appears inside a binding of $x
     // but *shouldn't* bind because it was inserted by a different macro....
     // can't write this test case until we have macro-generating macros.
 
-    // method arg hygiene
-    // method expands to fn get_x(&self_0, x_1: i32) {self_0 + self_2 + x_3 + x_1}
-    #[test]
-    fn method_arg_hygiene(){
-        run_renaming_test(
-            &("macro_rules! inject_x (()=>(x));
-              macro_rules! inject_self (()=>(self));
-              struct A;
-              impl A{fn get_x(&self, x: i32) {self + inject_self!() + inject_x!() + x;} }",
-              vec!(vec!(0),vec!(3)),
-              true),
-            0)
-    }
-
-    // ooh, got another bite?
-    // expands to struct A; impl A {fn thingy(&self_1) {self_1;}}
-    #[test]
-    fn method_arg_hygiene_2(){
-        run_renaming_test(
-            &("struct A;
-              macro_rules! add_method (($T:ty) =>
-              (impl $T {  fn thingy(&self) {self;} }));
-              add_method!(A);",
-              vec!(vec!(0)),
-              true),
-            0)
-    }
-
-    // item fn hygiene
-    // expands to fn q(x_1: i32){fn g(x_2: i32){x_2 + x_1};}
-    #[test]
-    fn issue_9383(){
-        run_renaming_test(
-            &("macro_rules! bad_macro (($ex:expr) => (fn g(x: i32){ x + $ex }));
-              fn q(x: i32) { bad_macro!(x); }",
-              vec!(vec!(1),vec!(0)),true),
-            0)
-    }
-
-    // closure arg hygiene (ExprKind::Closure)
-    // expands to fn f(){(|x_1 : i32| {(x_2 + x_1)})(3);}
-    #[test]
-    fn closure_arg_hygiene(){
-        run_renaming_test(
-            &("macro_rules! inject_x (()=>(x));
-            fn f(){(|x : i32| {(inject_x!() + x)})(3);}",
-              vec!(vec!(1)),
-              true),
-            0)
-    }
-
-    // macro_rules in method position. Sadly, unimplemented.
-    #[test]
-    fn macro_in_method_posn(){
-        expand_crate_str(
-            "macro_rules! my_method (() => (fn thirteen(&self) -> i32 {13}));
-            struct A;
-            impl A{ my_method!(); }
-            fn f(){A.thirteen;}".to_string());
-    }
-
-    // another nested macro
-    // expands to impl Entries {fn size_hint(&self_1) {self_1;}
-    #[test]
-    fn item_macro_workaround(){
-        run_renaming_test(
-            &("macro_rules! item { ($i:item) => {$i}}
-              struct Entries;
-              macro_rules! iterator_impl {
-              () => { item!( impl Entries { fn size_hint(&self) { self;}});}}
-              iterator_impl! { }",
-              vec!(vec!(0)), true),
-            0)
-    }
-
-    // run one of the renaming tests
-    fn run_renaming_test(t: &RenamingTest, test_idx: usize) {
-        let invalid_name = keywords::Invalid.name();
-        let (teststr, bound_connections, bound_ident_check) = match *t {
-            (ref str,ref conns, bic) => (str.to_string(), conns.clone(), bic)
-        };
-        let cr = expand_crate_str(teststr.to_string());
-        let bindings = crate_bindings(&cr);
-        let varrefs = crate_varrefs(&cr);
-
-        // must be one check clause for each binding:
-        assert_eq!(bindings.len(),bound_connections.len());
-        for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
-            let binding_name = mtwt::resolve(bindings[binding_idx]);
-            let binding_marks = mtwt::marksof(bindings[binding_idx].ctxt, invalid_name);
-            // shouldmatch can't name varrefs that don't exist:
-            assert!((shouldmatch.is_empty()) ||
-                    (varrefs.len() > *shouldmatch.iter().max().unwrap()));
-            for (idx,varref) in varrefs.iter().enumerate() {
-                let print_hygiene_debug_info = || {
-                    // good lord, you can't make a path with 0 segments, can you?
-                    let final_varref_ident = match varref.segments.last() {
-                        Some(pathsegment) => pathsegment.identifier,
-                        None => panic!("varref with 0 path segments?")
-                    };
-                    let varref_name = mtwt::resolve(final_varref_ident);
-                    let varref_idents : Vec<ast::Ident>
-                        = varref.segments.iter().map(|s| s.identifier)
-                        .collect();
-                    println!("varref #{}: {:?}, resolves to {}",idx, varref_idents, varref_name);
-                    println!("varref's first segment's string: \"{}\"", final_varref_ident);
-                    println!("binding #{}: {}, resolves to {}",
-                             binding_idx, bindings[binding_idx], binding_name);
-                    mtwt::with_sctable(|x| mtwt::display_sctable(x));
-                };
-                if shouldmatch.contains(&idx) {
-                    // it should be a path of length 1, and it should
-                    // be free-identifier=? or bound-identifier=? to the given binding
-                    assert_eq!(varref.segments.len(),1);
-                    let varref_name = mtwt::resolve(varref.segments[0].identifier);
-                    let varref_marks = mtwt::marksof(varref.segments[0]
-                                                           .identifier
-                                                           .ctxt,
-                                                     invalid_name);
-                    if !(varref_name==binding_name) {
-                        println!("uh oh, should match but doesn't:");
-                        print_hygiene_debug_info();
-                    }
-                    assert_eq!(varref_name,binding_name);
-                    if bound_ident_check {
-                        // we're checking bound-identifier=?, and the marks
-                        // should be the same, too:
-                        assert_eq!(varref_marks,binding_marks.clone());
-                    }
-                } else {
-                    let varref_name = mtwt::resolve(varref.segments[0].identifier);
-                    let fail = (varref.segments.len() == 1)
-                        && (varref_name == binding_name);
-                    // temp debugging:
-                    if fail {
-                        println!("failure on test {}",test_idx);
-                        println!("text of test case: \"{}\"", teststr);
-                        println!("");
-                        println!("uh oh, matches but shouldn't:");
-                        print_hygiene_debug_info();
-                    }
-                    assert!(!fail);
-                }
-            }
-        }
-    }
-
     #[test]
     fn fmt_in_macro_used_inside_module_macro() {
         let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
index 7ac0e8c64c27554844a2809c8bfe8fc58f458692..c9e8715dda6a8a288ae2cebb61284268dca6f507 100644 (file)
@@ -25,34 +25,22 @@ use std::collections::HashMap;
 /// The SCTable contains a table of SyntaxContext_'s. It
 /// represents a flattened tree structure, to avoid having
 /// managed pointers everywhere (that caused an ICE).
-/// the mark_memo and rename_memo fields are side-tables
+/// the `marks` and `renames` fields are side-tables
 /// that ensure that adding the same mark to the same context
-/// gives you back the same context as before. This shouldn't
-/// change the semantics--everything here is immutable--but
-/// it should cut down on memory use *a lot*; applying a mark
-/// to a tree containing 50 identifiers would otherwise generate
-/// 50 new contexts
+/// gives you back the same context as before. This should cut
+/// down on memory use *a lot*; applying a mark to a tree containing
+/// 50 identifiers would otherwise generate 50 new contexts.
 pub struct SCTable {
     table: RefCell<Vec<SyntaxContext_>>,
-    mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
-    // The pair (Name,SyntaxContext) is actually one Ident, but it needs to be hashed and
-    // compared as pair (name, ctxt) and not as an Ident
-    rename_memo: RefCell<HashMap<(SyntaxContext,(Name,SyntaxContext),Name),SyntaxContext>>,
+    marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
+    renames: RefCell<HashMap<Name,SyntaxContext>>,
 }
 
 #[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
 pub enum SyntaxContext_ {
     EmptyCtxt,
     Mark (Mrk,SyntaxContext),
-    /// flattening the name and syntaxcontext into the rename...
-    /// HIDDEN INVARIANTS:
-    /// 1) the first name in a Rename node
-    /// can only be a programmer-supplied name.
-    /// 2) Every Rename node with a given Name in the
-    /// "to" slot must have the same name and context
-    /// in the "from" slot. In essence, they're all
-    /// pointers to a single "rename" event node.
-    Rename (Ident,Name,SyntaxContext),
+    Rename (Name),
     /// actually, IllegalCtxt may not be necessary.
     IllegalCtxt
 }
@@ -67,37 +55,39 @@ pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
 
 /// Extend a syntax context with a given mark and sctable (explicit memoization)
 fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
-    let key = (ctxt, m);
-    *table.mark_memo.borrow_mut().entry(key).or_insert_with(|| {
-        SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Mark(m, ctxt)))
-    })
+    let ctxts = &mut *table.table.borrow_mut();
+    match ctxts[ctxt.0 as usize] {
+        // Applying the same mark twice is a no-op.
+        Mark(outer_mark, prev_ctxt) if outer_mark == m => return prev_ctxt,
+        _ => *table.marks.borrow_mut().entry((ctxt, m)).or_insert_with(|| {
+            SyntaxContext(idx_push(ctxts, Mark(m, ctxt)))
+        }),
+    }
 }
 
 /// Extend a syntax context with a given rename
-pub fn apply_rename(id: Ident, to:Name,
-                  ctxt: SyntaxContext) -> SyntaxContext {
-    with_sctable(|table| apply_rename_internal(id, to, ctxt, table))
+pub fn apply_rename(from: Ident, to: Name, ident: Ident) -> Ident {
+    with_sctable(|table| apply_rename_internal(from, to, ident, table))
 }
 
 /// Extend a syntax context with a given rename and sctable (explicit memoization)
-fn apply_rename_internal(id: Ident,
-                       to: Name,
-                       ctxt: SyntaxContext,
-                       table: &SCTable) -> SyntaxContext {
-    let key = (ctxt, (id.name, id.ctxt), to);
-
-    *table.rename_memo.borrow_mut().entry(key).or_insert_with(|| {
-            SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Rename(id, to, ctxt)))
-    })
+fn apply_rename_internal(from: Ident, to: Name, ident: Ident, table: &SCTable) -> Ident {
+    if (ident.name, ident.ctxt) != (from.name, from.ctxt) {
+        return ident;
+    }
+    let ctxt = *table.renames.borrow_mut().entry(to).or_insert_with(|| {
+        SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Rename(to)))
+    });
+    Ident { ctxt: ctxt, ..ident }
 }
 
 /// Apply a list of renamings to a context
 // if these rename lists get long, it would make sense
 // to consider memoizing this fold. This may come up
 // when we add hygiene to item names.
-pub fn apply_renames(renames: &RenameList, ctxt: SyntaxContext) -> SyntaxContext {
-    renames.iter().fold(ctxt, |ctxt, &(from, to)| {
-        apply_rename(from, to, ctxt)
+pub fn apply_renames(renames: &RenameList, ident: Ident) -> Ident {
+    renames.iter().fold(ident, |ident, &(from, to)| {
+        apply_rename(from, to, ident)
     })
 }
 
@@ -114,8 +104,8 @@ pub fn with_sctable<T, F>(op: F) -> T where
 fn new_sctable_internal() -> SCTable {
     SCTable {
         table: RefCell::new(vec!(EmptyCtxt, IllegalCtxt)),
-        mark_memo: RefCell::new(HashMap::new()),
-        rename_memo: RefCell::new(HashMap::new()),
+        marks: RefCell::new(HashMap::new()),
+        renames: RefCell::new(HashMap::new()),
     }
 }
 
@@ -131,20 +121,18 @@ pub fn display_sctable(table: &SCTable) {
 pub fn clear_tables() {
     with_sctable(|table| {
         *table.table.borrow_mut() = Vec::new();
-        *table.mark_memo.borrow_mut() = HashMap::new();
-        *table.rename_memo.borrow_mut() = HashMap::new();
+        *table.marks.borrow_mut() = HashMap::new();
+        *table.renames.borrow_mut() = HashMap::new();
     });
-    with_resolve_table_mut(|table| *table = HashMap::new());
 }
 
 /// Reset the tables to their initial state
 pub fn reset_tables() {
     with_sctable(|table| {
         *table.table.borrow_mut() = vec!(EmptyCtxt, IllegalCtxt);
-        *table.mark_memo.borrow_mut() = HashMap::new();
-        *table.rename_memo.borrow_mut() = HashMap::new();
+        *table.marks.borrow_mut() = HashMap::new();
+        *table.renames.borrow_mut() = HashMap::new();
     });
-    with_resolve_table_mut(|table| *table = HashMap::new());
 }
 
 /// Add a value to the end of a vec, return its index
@@ -156,103 +144,19 @@ fn idx_push<T>(vec: &mut Vec<T>, val: T) -> u32 {
 /// Resolve a syntax object to a name, per MTWT.
 pub fn resolve(id: Ident) -> Name {
     with_sctable(|sctable| {
-        with_resolve_table_mut(|resolve_table| {
-            resolve_internal(id, sctable, resolve_table)
-        })
+        resolve_internal(id, sctable)
     })
 }
 
-type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
-
-// okay, I admit, putting this in TLS is not so nice:
-// fetch the SCTable from TLS, create one if it doesn't yet exist.
-fn with_resolve_table_mut<T, F>(op: F) -> T where
-    F: FnOnce(&mut ResolveTable) -> T,
-{
-    thread_local!(static RESOLVE_TABLE_KEY: RefCell<ResolveTable> = {
-        RefCell::new(HashMap::new())
-    });
-
-    RESOLVE_TABLE_KEY.with(move |slot| op(&mut *slot.borrow_mut()))
-}
-
 /// Resolve a syntax object to a name, per MTWT.
 /// adding memoization to resolve 500+ seconds in resolve for librustc (!)
-fn resolve_internal(id: Ident,
-                    table: &SCTable,
-                    resolve_table: &mut ResolveTable) -> Name {
-    let key = (id.name, id.ctxt);
-
-    match resolve_table.get(&key) {
-        Some(&name) => return name,
-        None => {}
-    }
-
-    let resolved = {
-        let result = (*table.table.borrow())[id.ctxt.0 as usize];
-        match result {
-            EmptyCtxt => id.name,
-            // ignore marks here:
-            Mark(_,subctxt) =>
-                resolve_internal(Ident::new(id.name, subctxt),
-                                 table, resolve_table),
-            // do the rename if necessary:
-            Rename(Ident{name, ctxt}, toname, subctxt) => {
-                let resolvedfrom =
-                    resolve_internal(Ident::new(name, ctxt),
-                                     table, resolve_table);
-                let resolvedthis =
-                    resolve_internal(Ident::new(id.name, subctxt),
-                                     table, resolve_table);
-                if (resolvedthis == resolvedfrom)
-                    && (marksof_internal(ctxt, resolvedthis, table)
-                        == marksof_internal(subctxt, resolvedthis, table)) {
-                    toname
-                } else {
-                    resolvedthis
-                }
-            }
-            IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
-        }
-    };
-    resolve_table.insert(key, resolved);
-    resolved
-}
-
-/// Compute the marks associated with a syntax context.
-pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
-    with_sctable(|table| marksof_internal(ctxt, stopname, table))
-}
-
-// the internal function for computing marks
-// it's not clear to me whether it's better to use a [] mutable
-// vector or a cons-list for this.
-fn marksof_internal(ctxt: SyntaxContext,
-                    stopname: Name,
-                    table: &SCTable) -> Vec<Mrk> {
-    let mut result = Vec::new();
-    let mut loopvar = ctxt;
-    loop {
-        let table_entry = (*table.table.borrow())[loopvar.0 as usize];
-        match table_entry {
-            EmptyCtxt => {
-                return result;
-            },
-            Mark(mark, tl) => {
-                xor_push(&mut result, mark);
-                loopvar = tl;
-            },
-            Rename(_,name,tl) => {
-                // see MTWT for details on the purpose of the stopname.
-                // short version: it prevents duplication of effort.
-                if name == stopname {
-                    return result;
-                } else {
-                    loopvar = tl;
-                }
-            }
-            IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
-        }
+fn resolve_internal(id: Ident, table: &SCTable) -> Name {
+    match table.table.borrow()[id.ctxt.0 as usize] {
+        EmptyCtxt => id.name,
+        // ignore marks here:
+        Mark(_, subctxt) => resolve_internal(Ident::new(id.name, subctxt), table),
+        Rename(name) => name,
+        IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
     }
 }
 
@@ -267,103 +171,16 @@ pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
     })
 }
 
-/// Push a name... unless it matches the one on top, in which
-/// case pop and discard (so two of the same marks cancel)
-fn xor_push(marks: &mut Vec<Mrk>, mark: Mrk) {
-    if (!marks.is_empty()) && (*marks.last().unwrap() == mark) {
-        marks.pop().unwrap();
-    } else {
-        marks.push(mark);
-    }
-}
-
 #[cfg(test)]
 mod tests {
-    use self::TestSC::*;
     use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
-    use super::{resolve, xor_push, apply_mark_internal, new_sctable_internal};
-    use super::{apply_rename_internal, apply_renames, marksof_internal, resolve_internal};
-    use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt};
-    use std::collections::HashMap;
-
-    #[test]
-    fn xorpush_test () {
-        let mut s = Vec::new();
-        xor_push(&mut s, 14);
-        assert_eq!(s.clone(), [14]);
-        xor_push(&mut s, 14);
-        assert_eq!(s.clone(), []);
-        xor_push(&mut s, 14);
-        assert_eq!(s.clone(), [14]);
-        xor_push(&mut s, 15);
-        assert_eq!(s.clone(), [14, 15]);
-        xor_push(&mut s, 16);
-        assert_eq!(s.clone(), [14, 15, 16]);
-        xor_push(&mut s, 16);
-        assert_eq!(s.clone(), [14, 15]);
-        xor_push(&mut s, 15);
-        assert_eq!(s.clone(), [14]);
-    }
+    use super::{resolve, apply_mark_internal, new_sctable_internal};
+    use super::{SCTable, Mark};
 
     fn id(n: u32, s: SyntaxContext) -> Ident {
         Ident::new(Name(n), s)
     }
 
-    // because of the SCTable, I now need a tidy way of
-    // creating syntax objects. Sigh.
-    #[derive(Clone, PartialEq, Debug)]
-    enum TestSC {
-        M(Mrk),
-        R(Ident,Name)
-    }
-
-    // unfold a vector of TestSC values into a SCTable,
-    // returning the resulting index
-    fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
-        -> SyntaxContext {
-        tscs.iter().rev().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
-                  {match *tsc {
-                      M(mrk) => apply_mark_internal(mrk,tail,table),
-                      R(ident,name) => apply_rename_internal(ident,name,tail,table)}})
-    }
-
-    // gather a SyntaxContext back into a vector of TestSCs
-    fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> Vec<TestSC> {
-        let mut result = Vec::new();
-        loop {
-            let table = table.table.borrow();
-            match (*table)[sc.0 as usize] {
-                EmptyCtxt => {return result;},
-                Mark(mrk,tail) => {
-                    result.push(M(mrk));
-                    sc = tail;
-                    continue;
-                },
-                Rename(id,name,tail) => {
-                    result.push(R(id,name));
-                    sc = tail;
-                    continue;
-                }
-                IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
-            }
-        }
-    }
-
-    #[test]
-    fn test_unfold_refold(){
-        let mut t = new_sctable_internal();
-
-        let test_sc = vec!(M(3),R(id(101,EMPTY_CTXT),Name(14)),M(9));
-        assert_eq!(unfold_test_sc(test_sc.clone(),EMPTY_CTXT,&mut t),SyntaxContext(4));
-        {
-            let table = t.table.borrow();
-            assert!((*table)[2] == Mark(9,EMPTY_CTXT));
-            assert!((*table)[3] == Rename(id(101,EMPTY_CTXT),Name(14),SyntaxContext(2)));
-            assert!((*table)[4] == Mark(3,SyntaxContext(3)));
-        }
-        assert_eq!(refold_test_sc(SyntaxContext(4),&t),test_sc);
-    }
-
     // extend a syntax context with a sequence of marks given
     // in a vector. v[0] will be the outermost mark.
     fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
@@ -383,98 +200,12 @@ mod tests {
         }
     }
 
-    #[test]
-    fn test_marksof () {
-        let stopname = Name(242);
-        let name1 = Name(243);
-        let mut t = new_sctable_internal();
-        assert_eq!(marksof_internal (EMPTY_CTXT,stopname,&t),Vec::new());
-        // FIXME #5074: ANF'd to dodge nested calls
-        { let ans = unfold_marks(vec!(4,98),EMPTY_CTXT,&mut t);
-         assert_eq! (marksof_internal (ans,stopname,&t), [4, 98]);}
-        // does xoring work?
-        { let ans = unfold_marks(vec!(5,5,16),EMPTY_CTXT,&mut t);
-         assert_eq! (marksof_internal (ans,stopname,&t), [16]);}
-        // does nested xoring work?
-        { let ans = unfold_marks(vec!(5,10,10,5,16),EMPTY_CTXT,&mut t);
-         assert_eq! (marksof_internal (ans, stopname,&t), [16]);}
-        // rename where stop doesn't match:
-        { let chain = vec!(M(9),
-                        R(id(name1.0,
-                             apply_mark_internal (4, EMPTY_CTXT,&mut t)),
-                          Name(100101102)),
-                        M(14));
-         let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
-         assert_eq! (marksof_internal (ans, stopname, &t), [9, 14]);}
-        // rename where stop does match
-        { let name1sc = apply_mark_internal(4, EMPTY_CTXT, &mut t);
-         let chain = vec!(M(9),
-                       R(id(name1.0, name1sc),
-                         stopname),
-                       M(14));
-         let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
-         assert_eq! (marksof_internal (ans, stopname, &t), [9]); }
-    }
-
-
-    #[test]
-    fn resolve_tests () {
-        let a = 40;
-        let mut t = new_sctable_internal();
-        let mut rt = HashMap::new();
-        // - ctxt is MT
-        assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),Name(a));
-        // - simple ignored marks
-        { let sc = unfold_marks(vec!(1,2,3),EMPTY_CTXT,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(a));}
-        // - orthogonal rename where names don't match
-        { let sc = unfold_test_sc(vec!(R(id(50,EMPTY_CTXT),Name(51)),M(12)),EMPTY_CTXT,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(a));}
-        // - rename where names do match, but marks don't
-        { let sc1 = apply_mark_internal(1,EMPTY_CTXT,&mut t);
-         let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50)),
-                                   M(1),
-                                   M(2)),
-                                 EMPTY_CTXT,&mut t);
-        assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(a));}
-        // - rename where names and marks match
-        { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
-         let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50)),M(1),M(2)),EMPTY_CTXT,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(50)); }
-        // - rename where names and marks match by literal sharing
-        { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
-         let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50))),sc1,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(50)); }
-        // - two renames of the same var.. can only happen if you use
-        // local-expand to prevent the inner binding from being renamed
-        // during the rename-pass caused by the first:
-        println!("about to run bad test");
-        { let sc = unfold_test_sc(vec!(R(id(a,EMPTY_CTXT),Name(50)),
-                                    R(id(a,EMPTY_CTXT),Name(51))),
-                                  EMPTY_CTXT,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(51)); }
-        // the simplest double-rename:
-        { let a_to_a50 = apply_rename_internal(id(a,EMPTY_CTXT),Name(50),EMPTY_CTXT,&mut t);
-         let a50_to_a51 = apply_rename_internal(id(a,a_to_a50),Name(51),a_to_a50,&mut t);
-         assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),Name(51));
-         // mark on the outside doesn't stop rename:
-         let sc = apply_mark_internal(9,a50_to_a51,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(51));
-         // but mark on the inside does:
-         let a50_to_a51_b = unfold_test_sc(vec!(R(id(a,a_to_a50),Name(51)),
-                                              M(9)),
-                                           a_to_a50,
-                                           &mut t);
-         assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),Name(50));}
-    }
-
     #[test]
     fn mtwt_resolve_test(){
         let a = 40;
         assert_eq!(resolve(id(a,EMPTY_CTXT)),Name(a));
     }
 
-
     #[test]
     fn hashing_tests () {
         let mut t = new_sctable_internal();
@@ -484,26 +215,4 @@ mod tests {
         assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(2));
         // I'm assuming that the rename table will behave the same....
     }
-
-    #[test]
-    fn resolve_table_hashing_tests() {
-        let mut t = new_sctable_internal();
-        let mut rt = HashMap::new();
-        assert_eq!(rt.len(),0);
-        resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
-        assert_eq!(rt.len(),1);
-        resolve_internal(id(39,EMPTY_CTXT),&mut t, &mut rt);
-        assert_eq!(rt.len(),2);
-        resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
-        assert_eq!(rt.len(),2);
-    }
-
-    #[test]
-    fn new_resolves_test() {
-        let renames = vec!((Ident::with_empty_ctxt(Name(23)),Name(24)),
-                           (Ident::with_empty_ctxt(Name(29)),Name(29)));
-        let new_ctxt1 = apply_renames(&renames,EMPTY_CTXT);
-        assert_eq!(resolve(Ident::new(Name(23),new_ctxt1)),Name(24));
-        assert_eq!(resolve(Ident::new(Name(29),new_ctxt1)),Name(29));
-    }
 }
index ee9a197ce56ccb84cc39167d1c2be5974543d5ff..68527b0797d5b6031da5ca6dcc4a2d9eb2c24341 100644 (file)
@@ -8,8 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast::{self, Arg, Arm, Block, Expr, Item, Pat, Stmt, TokenTree, Ty};
-use codemap::Span;
+use ast::{self, Arg, Arm, Block, Expr, Item, Pat, Stmt, Ty};
+use syntax_pos::Span;
 use ext::base::ExtCtxt;
 use ext::base;
 use ext::build::AstBuilder;
@@ -17,6 +17,7 @@ use parse::parser::{Parser, PathStyle};
 use parse::token::*;
 use parse::token;
 use ptr::P;
+use tokenstream::{self, TokenTree};
 
 /// Quasiquoting works via token trees.
 ///
@@ -31,12 +32,12 @@ pub mod rt {
     use ext::base::ExtCtxt;
     use parse::{self, token, classify};
     use ptr::P;
-    use std::rc::Rc;
 
-    use ast::TokenTree;
+    use tokenstream::{self, TokenTree};
 
     pub use parse::new_parser_from_tts;
-    pub use codemap::{BytePos, Span, dummy_spanned, DUMMY_SP};
+    pub use syntax_pos::{BytePos, Span, DUMMY_SP};
+    pub use codemap::{dummy_spanned};
 
     pub trait ToTokens {
         fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree>;
@@ -214,12 +215,12 @@ pub mod rt {
             if self.node.style == ast::AttrStyle::Inner {
                 r.push(TokenTree::Token(self.span, token::Not));
             }
-            r.push(TokenTree::Delimited(self.span, Rc::new(ast::Delimited {
+            r.push(TokenTree::Delimited(self.span, tokenstream::Delimited {
                 delim: token::Bracket,
                 open_span: self.span,
                 tts: self.node.value.to_tokens(cx),
                 close_span: self.span,
-            })));
+            }));
             r
         }
     }
@@ -234,12 +235,12 @@ pub mod rt {
 
     impl ToTokens for () {
         fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
-            vec![TokenTree::Delimited(DUMMY_SP, Rc::new(ast::Delimited {
+            vec![TokenTree::Delimited(DUMMY_SP, tokenstream::Delimited {
                 delim: token::Paren,
                 open_span: DUMMY_SP,
                 tts: vec![],
                 close_span: DUMMY_SP,
-            }))]
+            })]
         }
     }
 
@@ -250,7 +251,7 @@ pub mod rt {
                 id: ast::DUMMY_NODE_ID,
                 node: ast::ExprKind::Lit(P(self.clone())),
                 span: DUMMY_SP,
-                attrs: None,
+                attrs: ast::ThinVec::new(),
             }).to_tokens(cx)
         }
     }
@@ -281,7 +282,7 @@ pub mod rt {
                         id: ast::DUMMY_NODE_ID,
                         node: ast::ExprKind::Lit(P(dummy_spanned(lit))),
                         span: DUMMY_SP,
-                        attrs: None,
+                        attrs: ast::ThinVec::new(),
                     });
                     if *self >= 0 {
                         return lit.to_tokens(cx);
@@ -290,7 +291,7 @@ pub mod rt {
                         id: ast::DUMMY_NODE_ID,
                         node: ast::ExprKind::Unary(ast::UnOp::Neg, lit),
                         span: DUMMY_SP,
-                        attrs: None,
+                        attrs: ast::ThinVec::new(),
                     }).to_tokens(cx)
                 }
             }
@@ -422,7 +423,7 @@ pub fn expand_quote_expr<'cx>(cx: &'cx mut ExtCtxt,
     base::MacEager::expr(expanded)
 }
 
-pub fn expand_quote_item<'cx>(cx: &mut ExtCtxt,
+pub fn expand_quote_item<'cx>(cx: &'cx mut ExtCtxt,
                               sp: Span,
                               tts: &[TokenTree])
                               -> Box<base::MacResult+'cx> {
@@ -512,10 +513,8 @@ pub fn expand_quote_matcher(cx: &mut ExtCtxt,
     let (cx_expr, tts) = parse_arguments_to_quote(cx, tts);
     let mut vector = mk_stmts_let(cx, sp);
     vector.extend(statements_mk_tts(cx, &tts[..], true));
-    let block = cx.expr_block(
-        cx.block_all(sp,
-                     vector,
-                     Some(cx.expr_ident(sp, id_ext("tt")))));
+    vector.push(cx.stmt_expr(cx.expr_ident(sp, id_ext("tt"))));
+    let block = cx.expr_block(cx.block(sp, vector));
 
     let expanded = expand_wrapper(cx, sp, cx_expr, block, &[&["syntax", "ext", "quote", "rt"]]);
     base::MacEager::expr(expanded)
@@ -548,7 +547,7 @@ fn mk_name(cx: &ExtCtxt, sp: Span, ident: ast::Ident) -> P<ast::Expr> {
 }
 
 fn mk_tt_path(cx: &ExtCtxt, sp: Span, name: &str) -> P<ast::Expr> {
-    let idents = vec!(id_ext("syntax"), id_ext("ast"), id_ext("TokenTree"), id_ext(name));
+    let idents = vec!(id_ext("syntax"), id_ext("tokenstream"), id_ext("TokenTree"), id_ext(name));
     cx.expr_path(cx.path_global(sp, idents))
 }
 
@@ -765,19 +764,20 @@ fn statements_mk_tt(cx: &ExtCtxt, tt: &TokenTree, matcher: bool) -> Vec<ast::Stm
             let stmt_let_tt = cx.stmt_let(sp, true, id_ext("tt"), cx.expr_vec_ng(sp));
             let mut tts_stmts = vec![stmt_let_tt];
             tts_stmts.extend(statements_mk_tts(cx, &seq.tts[..], matcher));
-            let e_tts = cx.expr_block(cx.block(sp, tts_stmts,
-                                                   Some(cx.expr_ident(sp, id_ext("tt")))));
+            tts_stmts.push(cx.stmt_expr(cx.expr_ident(sp, id_ext("tt"))));
+            let e_tts = cx.expr_block(cx.block(sp, tts_stmts));
+
             let e_separator = match seq.separator {
                 Some(ref sep) => cx.expr_some(sp, expr_mk_token(cx, sp, sep)),
                 None => cx.expr_none(sp),
             };
             let e_op = match seq.op {
-                ast::KleeneOp::ZeroOrMore => "ZeroOrMore",
-                ast::KleeneOp::OneOrMore => "OneOrMore",
+                tokenstream::KleeneOp::ZeroOrMore => "ZeroOrMore",
+                tokenstream::KleeneOp::OneOrMore => "OneOrMore",
             };
             let e_op_idents = vec![
                 id_ext("syntax"),
-                id_ext("ast"),
+                id_ext("tokenstream"),
                 id_ext("KleeneOp"),
                 id_ext(e_op),
             ];
@@ -787,16 +787,13 @@ fn statements_mk_tt(cx: &ExtCtxt, tt: &TokenTree, matcher: bool) -> Vec<ast::Stm
                               cx.field_imm(sp, id_ext("op"), e_op),
                               cx.field_imm(sp, id_ext("num_captures"),
                                                cx.expr_usize(sp, seq.num_captures))];
-            let seq_path = vec![id_ext("syntax"), id_ext("ast"), id_ext("SequenceRepetition")];
+            let seq_path = vec![id_ext("syntax"),
+                                id_ext("tokenstream"),
+                                id_ext("SequenceRepetition")];
             let e_seq_struct = cx.expr_struct(sp, cx.path_global(sp, seq_path), fields);
-            let e_rc_new = cx.expr_call_global(sp, vec![id_ext("std"),
-                                                        id_ext("rc"),
-                                                        id_ext("Rc"),
-                                                        id_ext("new")],
-                                                   vec![e_seq_struct]);
             let e_tok = cx.expr_call(sp,
                                      mk_tt_path(cx, sp, "Sequence"),
-                                     vec!(e_sp, e_rc_new));
+                                     vec!(e_sp, e_seq_struct));
             let e_push =
                 cx.expr_method_call(sp,
                                     cx.expr_ident(sp, id_ext("tt")),
@@ -884,10 +881,8 @@ fn expand_tts(cx: &ExtCtxt, sp: Span, tts: &[TokenTree])
 
     let mut vector = mk_stmts_let(cx, sp);
     vector.extend(statements_mk_tts(cx, &tts[..], false));
-    let block = cx.expr_block(
-        cx.block_all(sp,
-                     vector,
-                     Some(cx.expr_ident(sp, id_ext("tt")))));
+    vector.push(cx.stmt_expr(cx.expr_ident(sp, id_ext("tt"))));
+    let block = cx.expr_block(cx.block(sp, vector));
 
     (cx_expr, block)
 }
@@ -901,13 +896,14 @@ fn expand_wrapper(cx: &ExtCtxt,
     let cx_expr_borrow = cx.expr_addr_of(sp, cx.expr_deref(sp, cx_expr));
     let stmt_let_ext_cx = cx.stmt_let(sp, false, id_ext("ext_cx"), cx_expr_borrow);
 
-    let stmts = imports.iter().map(|path| {
+    let mut stmts = imports.iter().map(|path| {
         // make item: `use ...;`
         let path = path.iter().map(|s| s.to_string()).collect();
         cx.stmt_item(sp, cx.item_use_glob(sp, ast::Visibility::Inherited, ids_ext(path)))
-    }).chain(Some(stmt_let_ext_cx)).collect();
+    }).chain(Some(stmt_let_ext_cx)).collect::<Vec<_>>();
+    stmts.push(cx.stmt_expr(expr));
 
-    cx.expr_block(cx.block_all(sp, stmts, Some(expr)))
+    cx.expr_block(cx.block(sp, stmts))
 }
 
 fn expand_parse_call(cx: &ExtCtxt,
index 3e375e1798d8697be3536648f496d58cd1437d1e..97cb09991ec40b953901517cf1726d163aaa39e1 100644 (file)
@@ -9,8 +9,7 @@
 // except according to those terms.
 
 use ast;
-use codemap::{Pos, Span};
-use codemap;
+use syntax_pos::{self, Pos, Span};
 use ext::base::*;
 use ext::base;
 use ext::build::AstBuilder;
@@ -18,6 +17,7 @@ use parse::token;
 use parse;
 use print::pprust;
 use ptr::P;
+use tokenstream;
 use util::small_vector::SmallVector;
 
 use std::fs::File;
@@ -30,7 +30,7 @@ use std::rc::Rc;
 // a given file into the current one.
 
 /// line!(): expands to the current line number
-pub fn expand_line(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_line(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                    -> Box<base::MacResult+'static> {
     base::check_zero_tts(cx, sp, tts, "line!");
 
@@ -41,7 +41,7 @@ pub fn expand_line(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
 }
 
 /* column!(): expands to the current column number */
-pub fn expand_column(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_column(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                   -> Box<base::MacResult+'static> {
     base::check_zero_tts(cx, sp, tts, "column!");
 
@@ -54,7 +54,7 @@ pub fn expand_column(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
 /// file!(): expands to the current filename */
 /// The filemap (`loc.file`) contains a bunch more information we could spit
 /// out if we wanted.
-pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                    -> Box<base::MacResult+'static> {
     base::check_zero_tts(cx, sp, tts, "file!");
 
@@ -64,14 +64,14 @@ pub fn expand_file(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
     base::MacEager::expr(cx.expr_str(topmost, filename))
 }
 
-pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                         -> Box<base::MacResult+'static> {
     let s = pprust::tts_to_string(tts);
     base::MacEager::expr(cx.expr_str(sp,
                                    token::intern_and_get_ident(&s[..])))
 }
 
-pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                   -> Box<base::MacResult+'static> {
     base::check_zero_tts(cx, sp, tts, "module_path!");
     let string = cx.mod_path()
@@ -87,7 +87,7 @@ pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
 /// include! : parse the given file as an expr
 /// This is generally a bad idea because it's going to behave
 /// unhygienically.
-pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                            -> Box<base::MacResult+'cx> {
     let file = match get_single_str_from_tts(cx, sp, tts, "include!") {
         Some(f) => f,
@@ -130,7 +130,7 @@ pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree
 }
 
 // include_str! : read the given file, insert it as a literal string expr
-pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                           -> Box<base::MacResult+'static> {
     let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") {
         Some(f) => f,
@@ -154,7 +154,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
             // dependency information
             let filename = format!("{}", file.display());
             let interned = token::intern_and_get_ident(&src[..]);
-            cx.codemap().new_filemap_and_lines(&filename, &src);
+            cx.codemap().new_filemap_and_lines(&filename, None, &src);
 
             base::MacEager::expr(cx.expr_str(sp, interned))
         }
@@ -167,7 +167,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
     }
 }
 
-pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                             -> Box<base::MacResult+'static> {
     let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") {
         Some(f) => f,
@@ -185,7 +185,7 @@ pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
             // Add this input file to the code map to make it available as
             // dependency information, but don't enter it's contents
             let filename = format!("{}", file.display());
-            cx.codemap().new_filemap_and_lines(&filename, "");
+            cx.codemap().new_filemap_and_lines(&filename, None, "");
 
             base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Rc::new(bytes))))
         }
@@ -194,10 +194,11 @@ pub fn expand_include_bytes(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
 
 // resolve a file-system path to an absolute file-system path (if it
 // isn't already)
-fn res_rel_file(cx: &mut ExtCtxt, sp: codemap::Span, arg: &Path) -> PathBuf {
+fn res_rel_file(cx: &mut ExtCtxt, sp: syntax_pos::Span, arg: &Path) -> PathBuf {
     // NB: relative paths are resolved relative to the compilation unit
     if !arg.is_absolute() {
-        let mut cu = PathBuf::from(&cx.codemap().span_to_filename(sp));
+        let callsite = cx.codemap().source_callsite(sp);
+        let mut cu = PathBuf::from(&cx.codemap().span_to_filename(callsite));
         cu.pop();
         cu.push(arg);
         cu
index 89ecf02ee4c92d6894c806919edba00f3e0951c8..813afb935762e9e7d9a40ac40e8ee6d6fb29882f 100644 (file)
@@ -79,9 +79,9 @@ pub use self::ParseResult::*;
 use self::TokenTreeOrTokenTreeVec::*;
 
 use ast;
-use ast::{TokenTree, Name, Ident};
-use codemap::{BytePos, mk_sp, Span, Spanned};
-use codemap;
+use ast::{Name, Ident};
+use syntax_pos::{self, BytePos, mk_sp, Span};
+use codemap::Spanned;
 use errors::FatalError;
 use parse::lexer::*; //resolve bug?
 use parse::ParseSess;
@@ -91,6 +91,7 @@ use parse::token::{Token, Nonterminal};
 use parse::token;
 use print::pprust;
 use ptr::P;
+use tokenstream::{self, TokenTree};
 
 use std::mem;
 use std::rc::Rc;
@@ -102,8 +103,8 @@ use std::collections::hash_map::Entry::{Vacant, Occupied};
 
 #[derive(Clone)]
 enum TokenTreeOrTokenTreeVec {
-    Tt(ast::TokenTree),
-    TtSeq(Rc<Vec<ast::TokenTree>>),
+    Tt(tokenstream::TokenTree),
+    TtSeq(Rc<Vec<tokenstream::TokenTree>>),
 }
 
 impl TokenTreeOrTokenTreeVec {
@@ -196,7 +197,7 @@ pub fn initial_matcher_pos(ms: Rc<Vec<TokenTree>>, sep: Option<Token>, lo: ByteP
 /// token tree it was derived from.
 
 pub enum NamedMatch {
-    MatchedSeq(Vec<Rc<NamedMatch>>, codemap::Span),
+    MatchedSeq(Vec<Rc<NamedMatch>>, syntax_pos::Span),
     MatchedNonterminal(Nonterminal)
 }
 
@@ -204,7 +205,7 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
             -> ParseResult<HashMap<Name, Rc<NamedMatch>>> {
     fn n_rec(p_s: &ParseSess, m: &TokenTree, res: &[Rc<NamedMatch>],
              ret_val: &mut HashMap<Name, Rc<NamedMatch>>, idx: &mut usize)
-             -> Result<(), (codemap::Span, String)> {
+             -> Result<(), (syntax_pos::Span, String)> {
         match *m {
             TokenTree::Sequence(_, ref seq) => {
                 for next_m in &seq.tts {
@@ -251,9 +252,9 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
 pub enum ParseResult<T> {
     Success(T),
     /// Arm failed to match
-    Failure(codemap::Span, String),
+    Failure(syntax_pos::Span, String),
     /// Fatal error (malformed macro?). Abort compilation.
-    Error(codemap::Span, String)
+    Error(syntax_pos::Span, String)
 }
 
 pub type NamedParseResult = ParseResult<HashMap<Name, Rc<NamedMatch>>>;
@@ -374,7 +375,7 @@ pub fn parse(sess: &ParseSess,
                 match ei.top_elts.get_tt(idx) {
                     /* need to descend into sequence */
                     TokenTree::Sequence(sp, seq) => {
-                        if seq.op == ast::KleeneOp::ZeroOrMore {
+                        if seq.op == tokenstream::KleeneOp::ZeroOrMore {
                             let mut new_ei = ei.clone();
                             new_ei.match_cur += seq.num_captures;
                             new_ei.idx += 1;
@@ -549,13 +550,8 @@ pub fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
             token::NtPath(Box::new(panictry!(p.parse_path(PathStyle::Type))))
         },
         "meta" => token::NtMeta(panictry!(p.parse_meta_item())),
-        _ => {
-            p.span_fatal_help(sp,
-                              &format!("invalid fragment specifier `{}`", name),
-                              "valid fragment specifiers are `ident`, `block`, \
-                               `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
-                               and `item`").emit();
-            panic!(FatalError);
-        }
+        // this is not supposed to happen, since it has been checked
+        // when compiling the macro.
+        _ => p.span_bug(sp, "invalid fragment specifier")
     }
 }
index 41d3991aee809a4feddf47314a6f486544e41dbf..23f0b1fff0ae72b79c28763d4bfab6befbfc8706 100644 (file)
@@ -8,8 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast::{self, TokenTree};
-use codemap::{Span, DUMMY_SP};
+use ast;
+use syntax_pos::{Span, DUMMY_SP};
 use ext::base::{DummyResult, ExtCtxt, MacResult, SyntaxExtension};
 use ext::base::{NormalTT, TTMacroExpander};
 use ext::tt::macro_parser::{Success, Error, Failure};
@@ -21,13 +21,13 @@ use parse::token::{self, gensym_ident, NtTT, Token};
 use parse::token::Token::*;
 use print;
 use ptr::P;
+use tokenstream::{self, TokenTree};
 
 use util::small_vector::SmallVector;
 
 use std::cell::RefCell;
 use std::collections::{HashMap};
 use std::collections::hash_map::{Entry};
-use std::rc::Rc;
 
 struct ParserAnyMacro<'a> {
     parser: RefCell<Parser<'a>>,
@@ -100,6 +100,21 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
         Some(ret)
     }
 
+    fn make_trait_items(self: Box<ParserAnyMacro<'a>>)
+                       -> Option<SmallVector<ast::TraitItem>> {
+        let mut ret = SmallVector::zero();
+        loop {
+            let mut parser = self.parser.borrow_mut();
+            match parser.token {
+                token::Eof => break,
+                _ => ret.push(panictry!(parser.parse_trait_item()))
+            }
+        }
+        self.ensure_complete_parse(false, "item");
+        Some(ret)
+    }
+
+
     fn make_stmts(self: Box<ParserAnyMacro<'a>>)
                  -> Option<SmallVector<ast::Stmt>> {
         let mut ret = SmallVector::zero();
@@ -179,7 +194,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
     for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
         let lhs_tt = match *lhs {
             TokenTree::Delimited(_, ref delim) => &delim.tts[..],
-            _ => cx.span_fatal(sp, "malformed macro lhs")
+            _ => cx.span_bug(sp, "malformed macro lhs")
         };
 
         match TokenTree::parse(cx, lhs_tt, arg) {
@@ -187,7 +202,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
                 let rhs = match rhses[i] {
                     // ignore delimiters
                     TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(),
-                    _ => cx.span_fatal(sp, "malformed macro rhs"),
+                    _ => cx.span_bug(sp, "malformed macro rhs"),
                 };
                 // rhs has holes ( `$id` and `$(...)` that need filled)
                 let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
@@ -246,27 +261,25 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
     // These spans won't matter, anyways
     let match_lhs_tok = MatchNt(lhs_nm, token::str_to_ident("tt"));
     let match_rhs_tok = MatchNt(rhs_nm, token::str_to_ident("tt"));
-    let argument_gram = vec!(
-        TokenTree::Sequence(DUMMY_SP,
-                   Rc::new(ast::SequenceRepetition {
-                       tts: vec![
-                           TokenTree::Token(DUMMY_SP, match_lhs_tok),
-                           TokenTree::Token(DUMMY_SP, token::FatArrow),
-                           TokenTree::Token(DUMMY_SP, match_rhs_tok)],
-                       separator: Some(token::Semi),
-                       op: ast::KleeneOp::OneOrMore,
-                       num_captures: 2
-                   })),
-        //to phase into semicolon-termination instead of
-        //semicolon-separation
-        TokenTree::Sequence(DUMMY_SP,
-                   Rc::new(ast::SequenceRepetition {
-                       tts: vec![TokenTree::Token(DUMMY_SP, token::Semi)],
-                       separator: None,
-                       op: ast::KleeneOp::ZeroOrMore,
-                       num_captures: 0
-                   })));
-
+    let argument_gram = vec![
+        TokenTree::Sequence(DUMMY_SP, tokenstream::SequenceRepetition {
+            tts: vec![
+                TokenTree::Token(DUMMY_SP, match_lhs_tok),
+                TokenTree::Token(DUMMY_SP, token::FatArrow),
+                TokenTree::Token(DUMMY_SP, match_rhs_tok),
+            ],
+            separator: Some(token::Semi),
+            op: tokenstream::KleeneOp::OneOrMore,
+            num_captures: 2,
+        }),
+        // to phase into semicolon-termination instead of semicolon-separation
+        TokenTree::Sequence(DUMMY_SP, tokenstream::SequenceRepetition {
+            tts: vec![TokenTree::Token(DUMMY_SP, token::Semi)],
+            separator: None,
+            op: tokenstream::KleeneOp::ZeroOrMore,
+            num_captures: 0
+        }),
+    ];
 
     // Parse the macro_rules! invocation (`none` is for no interpolations):
     let arg_reader = new_tt_reader(&cx.parse_sess().span_diagnostic,
@@ -291,17 +304,16 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
     let lhses = match **argument_map.get(&lhs_nm.name).unwrap() {
         MatchedSeq(ref s, _) => {
             s.iter().map(|m| match **m {
-                MatchedNonterminal(NtTT(ref tt)) => (**tt).clone(),
+                MatchedNonterminal(NtTT(ref tt)) => {
+                    valid &= check_lhs_nt_follows(cx, tt);
+                    (**tt).clone()
+                }
                 _ => cx.span_bug(def.span, "wrong-structured lhs")
             }).collect()
         }
         _ => cx.span_bug(def.span, "wrong-structured lhs")
     };
 
-    for lhs in &lhses {
-        check_lhs_nt_follows(cx, lhs, def.span);
-    }
-
     let rhses = match **argument_map.get(&rhs_nm.name).unwrap() {
         MatchedSeq(ref s, _) => {
             s.iter().map(|m| match **m {
@@ -327,22 +339,17 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
     NormalTT(exp, Some(def.span), def.allow_internal_unstable)
 }
 
-// why is this here? because of https://github.com/rust-lang/rust/issues/27774
-fn ref_slice<A>(s: &A) -> &[A] { use std::slice::from_raw_parts; unsafe { from_raw_parts(s, 1) } }
-
-fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &TokenTree, sp: Span) {
+fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &TokenTree) -> bool {
     // lhs is going to be like TokenTree::Delimited(...), where the
     // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
     match lhs {
-        &TokenTree::Delimited(_, ref tts) => {
-            check_matcher(cx, &tts.tts);
-        },
-        tt @ &TokenTree::Sequence(..) => {
-            check_matcher(cx, ref_slice(tt));
-        },
-        _ => cx.span_err(sp, "invalid macro matcher; matchers must be contained \
-                              in balanced delimiters or a repetition indicator")
-    };
+        &TokenTree::Delimited(_, ref tts) => check_matcher(cx, &tts.tts),
+        _ => {
+            cx.span_err(lhs.get_span(), "invalid macro matcher; matchers must \
+                                         be contained in balanced delimiters");
+            false
+        }
+    }
     // we don't abort on errors on rejection, the driver will do that for us
     // after parsing/expansion. we can report every error in every macro this way.
 }
@@ -355,192 +362,12 @@ fn check_rhs(cx: &mut ExtCtxt, rhs: &TokenTree) -> bool {
     false
 }
 
-// Issue 30450: when we are through a warning cycle, we can just error
-// on all failure conditions and remove this struct and enum.
-
-#[derive(Debug)]
-struct OnFail {
-    saw_failure: bool,
-    action: OnFailAction,
-}
-
-#[derive(Copy, Clone, Debug)]
-enum OnFailAction { Warn, Error, DoNothing }
-
-impl OnFail {
-    fn warn() -> OnFail { OnFail { saw_failure: false, action: OnFailAction::Warn } }
-    fn error() -> OnFail { OnFail { saw_failure: false, action: OnFailAction::Error } }
-    fn do_nothing() -> OnFail { OnFail { saw_failure: false, action: OnFailAction::DoNothing } }
-    fn react(&mut self, cx: &mut ExtCtxt, sp: Span, msg: &str) {
-        match self.action {
-            OnFailAction::DoNothing => {}
-            OnFailAction::Error => cx.span_err(sp, msg),
-            OnFailAction::Warn => {
-                cx.struct_span_warn(sp, msg)
-                    .span_note(sp, "The above warning will be a hard error in the next release.")
-                    .emit();
-            }
-        };
-        self.saw_failure = true;
-    }
-}
-
-fn check_matcher(cx: &mut ExtCtxt, matcher: &[TokenTree]) {
-    // Issue 30450: when we are through a warning cycle, we can just
-    // error on all failure conditions (and remove check_matcher_old).
-
-    // First run the old-pass, but *only* to find out if it would have failed.
-    let mut on_fail = OnFail::do_nothing();
-    check_matcher_old(cx, matcher.iter(), &Eof, &mut on_fail);
-    // Then run the new pass, but merely warn if the old pass accepts and new pass rejects.
-    // (Note this silently accepts code if new pass accepts.)
-    let mut on_fail = if on_fail.saw_failure {
-        OnFail::error()
-    } else {
-        OnFail::warn()
-    };
-    check_matcher_new(cx, matcher, &mut on_fail);
-}
-
-// returns the last token that was checked, for TokenTree::Sequence.
-// return value is used by recursive calls.
-fn check_matcher_old<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token, on_fail: &mut OnFail)
--> Option<(Span, Token)> where I: Iterator<Item=&'a TokenTree> {
-    use print::pprust::token_to_string;
-    use std::iter::once;
-
-    let mut last = None;
-
-    // 2. For each token T in M:
-    let mut tokens = matcher.peekable();
-    while let Some(token) = tokens.next() {
-        last = match *token {
-            TokenTree::Token(sp, MatchNt(ref name, ref frag_spec)) => {
-                // ii. If T is a simple NT, look ahead to the next token T' in
-                // M. If T' is in the set FOLLOW(NT), continue. Else; reject.
-                if can_be_followed_by_any(&frag_spec.name.as_str()) {
-                    continue
-                } else {
-                    let next_token = match tokens.peek() {
-                        // If T' closes a complex NT, replace T' with F
-                        Some(&&TokenTree::Token(_, CloseDelim(_))) => follow.clone(),
-                        Some(&&TokenTree::Token(_, ref tok)) => tok.clone(),
-                        Some(&&TokenTree::Sequence(sp, _)) => {
-                            // Be conservative around sequences: to be
-                            // more specific, we would need to
-                            // consider FIRST sets, but also the
-                            // possibility that the sequence occurred
-                            // zero times (in which case we need to
-                            // look at the token that follows the
-                            // sequence, which may itself be a sequence,
-                            // and so on).
-                            on_fail.react(cx, sp,
-                                        &format!("`${0}:{1}` is followed by a \
-                                                  sequence repetition, which is not \
-                                                  allowed for `{1}` fragments",
-                                                 name, frag_spec)
-                                        );
-                            Eof
-                        },
-                        // die next iteration
-                        Some(&&TokenTree::Delimited(_, ref delim)) => delim.close_token(),
-                        // else, we're at the end of the macro or sequence
-                        None => follow.clone()
-                    };
-
-                    let tok = if let TokenTree::Token(_, ref tok) = *token {
-                        tok
-                    } else {
-                        unreachable!()
-                    };
-
-                    // If T' is in the set FOLLOW(NT), continue. Else, reject.
-                    match (&next_token, is_in_follow(cx, &next_token, &frag_spec.name.as_str())) {
-                        (_, Err(msg)) => {
-                            on_fail.react(cx, sp, &msg);
-                            continue
-                        }
-                        (&Eof, _) => return Some((sp, tok.clone())),
-                        (_, Ok(true)) => continue,
-                        (next, Ok(false)) => {
-                            on_fail.react(cx, sp, &format!("`${0}:{1}` is followed by `{2}`, which \
-                                                      is not allowed for `{1}` fragments",
-                                                     name, frag_spec,
-                                                     token_to_string(next)));
-                            continue
-                        },
-                    }
-                }
-            },
-            TokenTree::Sequence(sp, ref seq) => {
-                // iii. Else, T is a complex NT.
-                match seq.separator {
-                    // If T has the form $(...)U+ or $(...)U* for some token U,
-                    // run the algorithm on the contents with F set to U. If it
-                    // accepts, continue, else, reject.
-                    Some(ref u) => {
-                        let last = check_matcher_old(cx, seq.tts.iter(), u, on_fail);
-                        match last {
-                            // Since the delimiter isn't required after the last
-                            // repetition, make sure that the *next* token is
-                            // sane. This doesn't actually compute the FIRST of
-                            // the rest of the matcher yet, it only considers
-                            // single tokens and simple NTs. This is imprecise,
-                            // but conservatively correct.
-                            Some((span, tok)) => {
-                                let fol = match tokens.peek() {
-                                    Some(&&TokenTree::Token(_, ref tok)) => tok.clone(),
-                                    Some(&&TokenTree::Delimited(_, ref delim)) =>
-                                        delim.close_token(),
-                                    Some(_) => {
-                                        on_fail.react(cx, sp, "sequence repetition followed by \
-                                                another sequence repetition, which is not allowed");
-                                        Eof
-                                    },
-                                    None => Eof
-                                };
-                                check_matcher_old(cx, once(&TokenTree::Token(span, tok.clone())),
-                                                  &fol, on_fail)
-                            },
-                            None => last,
-                        }
-                    },
-                    // If T has the form $(...)+ or $(...)*, run the algorithm
-                    // on the contents with F set to the token following the
-                    // sequence. If it accepts, continue, else, reject.
-                    None => {
-                        let fol = match tokens.peek() {
-                            Some(&&TokenTree::Token(_, ref tok)) => tok.clone(),
-                            Some(&&TokenTree::Delimited(_, ref delim)) => delim.close_token(),
-                            Some(_) => {
-                                on_fail.react(cx, sp, "sequence repetition followed by another \
-                                             sequence repetition, which is not allowed");
-                                Eof
-                            },
-                            None => Eof
-                        };
-                        check_matcher_old(cx, seq.tts.iter(), &fol, on_fail)
-                    }
-                }
-            },
-            TokenTree::Token(..) => {
-                // i. If T is not an NT, continue.
-                continue
-            },
-            TokenTree::Delimited(_, ref tts) => {
-                // if we don't pass in that close delimiter, we'll incorrectly consider the matcher
-                // `{ $foo:ty }` as having a follow that isn't `RBrace`
-                check_matcher_old(cx, tts.tts.iter(), &tts.close_token(), on_fail)
-            }
-        }
-    }
-    last
-}
-
-fn check_matcher_new(cx: &mut ExtCtxt, matcher: &[TokenTree], on_fail: &mut OnFail) {
+fn check_matcher(cx: &mut ExtCtxt, matcher: &[TokenTree]) -> bool {
     let first_sets = FirstSets::new(matcher);
     let empty_suffix = TokenSet::empty();
-    check_matcher_core(cx, &first_sets, matcher, &empty_suffix, on_fail);
+    let err = cx.parse_sess.span_diagnostic.err_count();
+    check_matcher_core(cx, &first_sets, matcher, &empty_suffix);
+    err == cx.parse_sess.span_diagnostic.err_count()
 }
 
 // The FirstSets for a matcher is a mapping from subsequences in the
@@ -613,7 +440,7 @@ impl FirstSets {
                         }
 
                         // Reverse scan: Sequence comes before `first`.
-                        if subfirst.maybe_empty || seq_rep.op == ast::KleeneOp::ZeroOrMore {
+                        if subfirst.maybe_empty || seq_rep.op == tokenstream::KleeneOp::ZeroOrMore {
                             // If sequence is potentially empty, then
                             // union them (preserving first emptiness).
                             first.add_all(&TokenSet { maybe_empty: true, ..subfirst });
@@ -660,7 +487,8 @@ impl FirstSets {
 
                             assert!(first.maybe_empty);
                             first.add_all(subfirst);
-                            if subfirst.maybe_empty || seq_rep.op == ast::KleeneOp::ZeroOrMore {
+                            if subfirst.maybe_empty ||
+                               seq_rep.op == tokenstream::KleeneOp::ZeroOrMore {
                                 // continue scanning for more first
                                 // tokens, but also make sure we
                                 // restore empty-tracking state
@@ -780,8 +608,7 @@ impl TokenSet {
 fn check_matcher_core(cx: &mut ExtCtxt,
                       first_sets: &FirstSets,
                       matcher: &[TokenTree],
-                      follow: &TokenSet,
-                      on_fail: &mut OnFail) -> TokenSet {
+                      follow: &TokenSet) -> TokenSet {
     use print::pprust::token_to_string;
 
     let mut last = TokenSet::empty();
@@ -810,7 +637,11 @@ fn check_matcher_core(cx: &mut ExtCtxt,
             TokenTree::Token(sp, ref tok) => {
                 let can_be_followed_by_any;
                 if let Err(bad_frag) = has_legal_fragment_specifier(tok) {
-                    on_fail.react(cx, sp, &format!("invalid fragment specifier `{}`", bad_frag));
+                    cx.struct_span_err(sp, &format!("invalid fragment specifier `{}`", bad_frag))
+                        .help("valid fragment specifiers are `ident`, `block`, \
+                               `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
+                               and `item`")
+                        .emit();
                     // (This eliminates false positives and duplicates
                     // from error messages.)
                     can_be_followed_by_any = true;
@@ -831,7 +662,7 @@ fn check_matcher_core(cx: &mut ExtCtxt,
             }
             TokenTree::Delimited(_, ref d) => {
                 let my_suffix = TokenSet::singleton((d.close_span, Token::CloseDelim(d.delim)));
-                check_matcher_core(cx, first_sets, &d.tts, &my_suffix, on_fail);
+                check_matcher_core(cx, first_sets, &d.tts, &my_suffix);
                 // don't track non NT tokens
                 last.replace_with_irrelevant();
 
@@ -863,7 +694,7 @@ fn check_matcher_core(cx: &mut ExtCtxt,
                 // At this point, `suffix_first` is built, and
                 // `my_suffix` is some TokenSet that we can use
                 // for checking the interior of `seq_rep`.
-                let next = check_matcher_core(cx, first_sets, &seq_rep.tts, my_suffix, on_fail);
+                let next = check_matcher_core(cx, first_sets, &seq_rep.tts, my_suffix);
                 if next.maybe_empty {
                     last.add_all(&next);
                 } else {
@@ -884,8 +715,8 @@ fn check_matcher_core(cx: &mut ExtCtxt,
             if let MatchNt(ref name, ref frag_spec) = *t {
                 for &(sp, ref next_token) in &suffix_first.tokens {
                     match is_in_follow(cx, next_token, &frag_spec.name.as_str()) {
-                        Err(msg) => {
-                            on_fail.react(cx, sp, &msg);
+                        Err((msg, help)) => {
+                            cx.struct_span_err(sp, &msg).help(help).emit();
                             // don't bother reporting every source of
                             // conflict for a particular element of `last`.
                             continue 'each_last;
@@ -900,14 +731,15 @@ fn check_matcher_core(cx: &mut ExtCtxt,
                                 "may be"
                             };
 
-                            on_fail.react(
-                                cx, sp,
+                            cx.span_err(
+                                sp,
                                 &format!("`${name}:{frag}` {may_be} followed by `{next}`, which \
                                           is not allowed for `{frag}` fragments",
                                          name=name,
                                          frag=frag_spec,
                                          next=token_to_string(next_token),
-                                         may_be=may_be));
+                                         may_be=may_be)
+                            );
                         }
                     }
                 }
@@ -936,33 +768,11 @@ fn token_can_be_followed_by_any(tok: &Token) -> bool {
 /// ANYTHING without fear of future compatibility hazards).
 fn frag_can_be_followed_by_any(frag: &str) -> bool {
     match frag {
-        "item" |  // always terminated by `}` or `;`
-        "block" | // exactly one token tree
-        "ident" | // exactly one token tree
-        "meta" |  // exactly one token tree
-        "tt" =>    // exactly one token tree
-            true,
-
-        _ =>
-            false,
-    }
-}
-
-/// True if a fragment of type `frag` can be followed by any sort of
-/// token.  We use this (among other things) as a useful approximation
-/// for when `frag` can be followed by a repetition like `$(...)*` or
-/// `$(...)+`. In general, these can be a bit tricky to reason about,
-/// so we adopt a conservative position that says that any fragment
-/// specifier which consumes at most one token tree can be followed by
-/// a fragment specifier (indeed, these fragments can be followed by
-/// ANYTHING without fear of future compatibility hazards).
-fn can_be_followed_by_any(frag: &str) -> bool {
-    match frag {
-        "item" |  // always terminated by `}` or `;`
+        "item"  | // always terminated by `}` or `;`
         "block" | // exactly one token tree
         "ident" | // exactly one token tree
-        "meta"  // exactly one token tree
-        "tt" =>    // exactly one token tree
+        "meta"  | // exactly one token tree
+        "tt" =>   // exactly one token tree
             true,
 
         _ =>
@@ -978,7 +788,7 @@ fn can_be_followed_by_any(frag: &str) -> bool {
 /// break macros that were relying on that binary operator as a
 /// separator.
 // when changing this do not forget to update doc/book/macros.md!
-fn is_in_follow(_: &ExtCtxt, tok: &Token, frag: &str) -> Result<bool, String> {
+fn is_in_follow(_: &ExtCtxt, tok: &Token, frag: &str) -> Result<bool, (String, &'static str)> {
     if let &CloseDelim(_) = tok {
         // closing a token tree can never be matched by any fragment;
         // iow, we always require that `(` and `)` match, etc.
@@ -1027,7 +837,10 @@ fn is_in_follow(_: &ExtCtxt, tok: &Token, frag: &str) -> Result<bool, String> {
                 // harmless
                 Ok(true)
             },
-            _ => Err(format!("invalid fragment specifier `{}`", frag))
+            _ => Err((format!("invalid fragment specifier `{}`", frag),
+                     "valid fragment specifiers are `ident`, `block`, \
+                      `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
+                      and `item`"))
         }
     }
 }
index 6b3b5ce9de9140c387417688c0fa1063d6e198f2..40944a9a1c2d360bf13c78a9b1a45d742f2fb998 100644 (file)
@@ -9,15 +9,15 @@
 // except according to those terms.
 use self::LockstepIterSize::*;
 
-use ast;
-use ast::{TokenTree, Ident, Name};
-use codemap::{Span, DUMMY_SP};
+use ast::{Ident, Name};
+use syntax_pos::{Span, DUMMY_SP};
 use errors::{Handler, DiagnosticBuilder};
 use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
 use parse::token::{DocComment, MatchNt, SubstNt};
 use parse::token::{Token, NtIdent, SpecialMacroVar};
 use parse::token;
 use parse::lexer::TokenAndSpan;
+use tokenstream::{self, TokenTree};
 
 use std::rc::Rc;
 use std::ops::Add;
@@ -59,7 +59,7 @@ pub struct TtReader<'a> {
 pub fn new_tt_reader(sp_diag: &Handler,
                      interp: Option<HashMap<Name, Rc<NamedMatch>>>,
                      imported_from: Option<Ident>,
-                     src: Vec<ast::TokenTree>)
+                     src: Vec<tokenstream::TokenTree>)
                      -> TtReader {
     new_tt_reader_with_doc_flag(sp_diag, interp, imported_from, src, false)
 }
@@ -73,17 +73,17 @@ pub fn new_tt_reader(sp_diag: &Handler,
 pub fn new_tt_reader_with_doc_flag(sp_diag: &Handler,
                                    interp: Option<HashMap<Name, Rc<NamedMatch>>>,
                                    imported_from: Option<Ident>,
-                                   src: Vec<ast::TokenTree>,
+                                   src: Vec<tokenstream::TokenTree>,
                                    desugar_doc_comments: bool)
                                    -> TtReader {
     let mut r = TtReader {
         sp_diag: sp_diag,
         stack: vec!(TtFrame {
-            forest: TokenTree::Sequence(DUMMY_SP, Rc::new(ast::SequenceRepetition {
+            forest: TokenTree::Sequence(DUMMY_SP, tokenstream::SequenceRepetition {
                 tts: src,
                 // doesn't matter. This merely holds the root unzipping.
-                separator: None, op: ast::KleeneOp::ZeroOrMore, num_captures: 0
-            })),
+                separator: None, op: tokenstream::KleeneOp::ZeroOrMore, num_captures: 0
+            }),
             idx: 0,
             dotdotdoted: false,
             sep: None,
@@ -225,12 +225,9 @@ pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan {
         } else { /* repeat */
             *r.repeat_idx.last_mut().unwrap() += 1;
             r.stack.last_mut().unwrap().idx = 0;
-            match r.stack.last().unwrap().sep.clone() {
-                Some(tk) => {
-                    r.cur_tok = tk; /* repeat same span, I guess */
-                    return ret_val;
-                }
-                None => {}
+            if let Some(tk) = r.stack.last().unwrap().sep.clone() {
+                r.cur_tok = tk; // repeat same span, I guess
+                return ret_val;
             }
         }
     }
@@ -259,7 +256,7 @@ pub fn tt_next_token(r: &mut TtReader) -> TokenAndSpan {
                     }
                     LisConstraint(len, _) => {
                         if len == 0 {
-                            if seq.op == ast::KleeneOp::OneOrMore {
+                            if seq.op == tokenstream::KleeneOp::OneOrMore {
                                 // FIXME #2887 blame invoker
                                 panic!(r.sp_diag.span_fatal(sp.clone(),
                                                      "this must repeat at least once"));
index acef98f2afc1bc3b724480e556dd66109623001d..27485ee65fcc037640e5309d2534d583e47e91da 100644 (file)
@@ -30,14 +30,14 @@ use ast::{NodeId, PatKind};
 use ast;
 use attr;
 use attr::AttrMetaMethods;
-use codemap::{CodeMap, Span};
+use codemap::CodeMap;
+use syntax_pos::Span;
 use errors::Handler;
-use visit;
-use visit::{FnKind, Visitor};
+use visit::{self, FnKind, Visitor};
+use parse::ParseSess;
 use parse::token::InternedString;
 
 use std::ascii::AsciiExt;
-use std::cmp;
 
 macro_rules! setter {
     ($field: ident) => {{
@@ -59,8 +59,8 @@ macro_rules! declare_features {
 
         /// A set of features to be used by later passes.
         pub struct Features {
-            /// spans of #![feature] attrs for stable language features. for error reporting
-            pub declared_stable_lang_features: Vec<Span>,
+            /// #![feature] attrs for stable language features, for error reporting
+            pub declared_stable_lang_features: Vec<(InternedString, Span)>,
             /// #![feature] attrs for non-language (library) features
             pub declared_lib_features: Vec<(InternedString, Span)>,
             $(pub $feature: bool),+
@@ -274,7 +274,10 @@ declare_features! (
     (active, drop_types_in_const, "1.9.0", Some(33156)),
 
     // Allows cfg(target_has_atomic = "...").
-    (active, cfg_target_has_atomic, "1.9.0", Some(32976))
+    (active, cfg_target_has_atomic, "1.9.0", Some(32976)),
+
+    // Allows `..` in tuple (struct) patterns
+    (active, dotdot_in_tuple_patterns, "1.10.0", Some(33627))
 );
 
 declare_features! (
@@ -315,7 +318,6 @@ declare_features! (
     // Allows `#[deprecated]` attribute
     (accepted, deprecated, "1.9.0", Some(29935))
 );
-
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
 #[derive(PartialEq, Copy, Clone, Debug)]
@@ -500,6 +502,13 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
                                          is just used to make tests pass \
                                          and will never be stable",
                                         cfg_fn!(rustc_attrs))),
+    ("rustc_inherit_overflow_checks", Whitelisted, Gated("rustc_attrs",
+                                                         "the `#[rustc_inherit_overflow_checks]` \
+                                                          attribute is just used to control \
+                                                          overflow checking behavior of several \
+                                                          libcore functions that are inlined \
+                                                          across crates and will never be stable",
+                                                          cfg_fn!(rustc_attrs))),
 
     ("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
                                               EXPLAIN_ALLOW_INTERNAL_UNSTABLE,
@@ -594,60 +603,12 @@ const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)]
     ("target_has_atomic", "cfg_target_has_atomic", cfg_fn!(cfg_target_has_atomic)),
 ];
 
-#[derive(Debug, Eq, PartialEq)]
-pub enum GatedCfgAttr {
-    GatedCfg(GatedCfg),
-    GatedAttr(Span),
-}
-
 #[derive(Debug, Eq, PartialEq)]
 pub struct GatedCfg {
     span: Span,
     index: usize,
 }
 
-impl Ord for GatedCfgAttr {
-    fn cmp(&self, other: &GatedCfgAttr) -> cmp::Ordering {
-        let to_tup = |s: &GatedCfgAttr| match *s {
-            GatedCfgAttr::GatedCfg(ref gated_cfg) => {
-                (gated_cfg.span.lo.0, gated_cfg.span.hi.0, gated_cfg.index)
-            }
-            GatedCfgAttr::GatedAttr(ref span) => {
-                (span.lo.0, span.hi.0, GATED_CFGS.len())
-            }
-        };
-        to_tup(self).cmp(&to_tup(other))
-    }
-}
-
-impl PartialOrd for GatedCfgAttr {
-    fn partial_cmp(&self, other: &GatedCfgAttr) -> Option<cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl GatedCfgAttr {
-    pub fn check_and_emit(&self,
-                          diagnostic: &Handler,
-                          features: &Features,
-                          codemap: &CodeMap) {
-        match *self {
-            GatedCfgAttr::GatedCfg(ref cfg) => {
-                cfg.check_and_emit(diagnostic, features, codemap);
-            }
-            GatedCfgAttr::GatedAttr(span) => {
-                if !features.stmt_expr_attributes {
-                    emit_feature_err(diagnostic,
-                                     "stmt_expr_attributes",
-                                     span,
-                                     GateIssue::Language,
-                                     EXPLAIN_STMT_ATTR_SYNTAX);
-                }
-            }
-        }
-    }
-}
-
 impl GatedCfg {
     pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
         let name = cfg.name();
@@ -660,12 +621,11 @@ impl GatedCfg {
                       }
                   })
     }
-    fn check_and_emit(&self,
-                      diagnostic: &Handler,
-                      features: &Features,
-                      codemap: &CodeMap) {
+
+    pub fn check_and_emit(&self, sess: &ParseSess, features: &Features) {
         let (cfg, feature, has_feature) = GATED_CFGS[self.index];
-        if !has_feature(features) && !codemap.span_allows_unstable(self.span) {
+        if !has_feature(features) && !sess.codemap().span_allows_unstable(self.span) {
+            let diagnostic = &sess.span_diagnostic;
             let explain = format!("`cfg({})` is experimental and subject to change", cfg);
             emit_feature_err(diagnostic, feature, self.span, GateIssue::Language, &explain);
         }
@@ -751,6 +711,10 @@ pub fn check_attribute(attr: &ast::Attribute, handler: &Handler,
     cx.check_attribute(attr, true);
 }
 
+pub fn find_lang_feature_accepted_version(feature: &str) -> Option<&'static str> {
+    ACCEPTED_FEATURES.iter().find(|t| t.0 == feature).map(|t| t.1)
+}
+
 fn find_lang_feature_issue(feature: &str) -> Option<u32> {
     if let Some(info) = ACTIVE_FEATURES.iter().find(|t| t.0 == feature) {
         let issue = info.2;
@@ -797,7 +761,7 @@ pub fn emit_feature_err(diag: &Handler, feature: &str, span: Span, issue: GateIs
 const EXPLAIN_BOX_SYNTAX: &'static str =
     "box expression syntax is experimental; you can call `Box::new` instead.";
 
-const EXPLAIN_STMT_ATTR_SYNTAX: &'static str =
+pub const EXPLAIN_STMT_ATTR_SYNTAX: &'static str =
     "attributes on non-item statements and expressions are experimental.";
 
 pub const EXPLAIN_ASM: &'static str =
@@ -836,7 +800,7 @@ macro_rules! gate_feature_post {
     }}
 }
 
-impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
+impl<'a> Visitor for PostExpansionVisitor<'a> {
     fn visit_attribute(&mut self, attr: &ast::Attribute) {
         if !self.context.cm.span_allows_unstable(attr.span) {
             self.context.check_attribute(attr, false);
@@ -950,22 +914,6 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
         visit::walk_item(self, i);
     }
 
-    fn visit_variant_data(&mut self, s: &'v ast::VariantData, _: ast::Ident,
-                          _: &'v ast::Generics, _: ast::NodeId, span: Span) {
-        if s.fields().is_empty() {
-            if s.is_tuple() {
-                self.context.span_handler.struct_span_err(span, "empty tuple structs and enum \
-                                                                 variants are not allowed, use \
-                                                                 unit structs and enum variants \
-                                                                 instead")
-                                         .span_help(span, "remove trailing `()` to make a unit \
-                                                           struct or unit enum variant")
-                                         .emit();
-            }
-        }
-        visit::walk_struct_def(self, s)
-    }
-
     fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
         let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs,
                                                                      "link_name") {
@@ -997,6 +945,9 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
             ast::ExprKind::Try(..) => {
                 gate_feature_post!(&self, question_mark, e.span, "the `?` operator is not stable");
             }
+            ast::ExprKind::InPlace(..) => {
+                gate_feature_post!(&self, placement_in_syntax, e.span, EXPLAIN_PLACEMENT_IN);
+            }
             _ => {}
         }
         visit::walk_expr(self, e);
@@ -1021,15 +972,33 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
                                   pattern.span,
                                   "box pattern syntax is experimental");
             }
+            PatKind::Tuple(_, ddpos)
+                    if ddpos.is_some() => {
+                gate_feature_post!(&self, dotdot_in_tuple_patterns,
+                                  pattern.span,
+                                  "`..` in tuple patterns is experimental");
+            }
+            PatKind::TupleStruct(_, ref fields, ddpos)
+                    if ddpos.is_some() && !fields.is_empty() => {
+                gate_feature_post!(&self, dotdot_in_tuple_patterns,
+                                  pattern.span,
+                                  "`..` in tuple struct patterns is experimental");
+            }
+            PatKind::TupleStruct(_, ref fields, ddpos)
+                    if ddpos.is_none() && fields.is_empty() => {
+                self.context.span_handler.struct_span_err(pattern.span,
+                                                          "nullary enum variants are written with \
+                                                           no trailing `( )`").emit();
+            }
             _ => {}
         }
         visit::walk_pat(self, pattern)
     }
 
     fn visit_fn(&mut self,
-                fn_kind: FnKind<'v>,
-                fn_decl: &'v ast::FnDecl,
-                block: &'v ast::Block,
+                fn_kind: FnKind,
+                fn_decl: &ast::FnDecl,
+                block: &ast::Block,
                 span: Span,
                 _node_id: NodeId) {
         // check for const fn declarations
@@ -1068,7 +1037,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
         visit::walk_fn(self, fn_kind, fn_decl, block, span);
     }
 
-    fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
+    fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
         match ti.node {
             ast::TraitItemKind::Const(..) => {
                 gate_feature_post!(&self, associated_consts,
@@ -1089,7 +1058,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
         visit::walk_trait_item(self, ti);
     }
 
-    fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
+    fn visit_impl_item(&mut self, ii: &ast::ImplItem) {
         if ii.defaultness == ast::Defaultness::Default {
             gate_feature_post!(&self, specialization,
                               ii.span,
@@ -1112,49 +1081,38 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
         visit::walk_impl_item(self, ii);
     }
 
-    fn visit_vis(&mut self, vis: &'v ast::Visibility) {
+    fn visit_vis(&mut self, vis: &ast::Visibility) {
         let span = match *vis {
             ast::Visibility::Crate(span) => span,
-            ast::Visibility::Restricted { ref path, .. } => {
-                // Check for type parameters
-                let found_param = path.segments.iter().any(|segment| {
-                    !segment.parameters.types().is_empty() ||
-                    !segment.parameters.lifetimes().is_empty() ||
-                    !segment.parameters.bindings().is_empty()
-                });
-                if found_param {
-                    self.context.span_handler.span_err(path.span, "type or lifetime parameters \
-                                                                   in visibility path");
-                }
-                path.span
-            }
+            ast::Visibility::Restricted { ref path, .. } => path.span,
             _ => return,
         };
         gate_feature_post!(&self, pub_restricted, span, "`pub(restricted)` syntax is experimental");
+
+        visit::walk_vis(self, vis)
     }
 }
 
-pub fn get_features(span_handler: &Handler, krate: &ast::Crate) -> Features {
+pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features {
     let mut features = Features::new();
 
-    for attr in &krate.attrs {
+    for attr in krate_attrs {
         if !attr.check_name("feature") {
             continue
         }
 
         match attr.meta_item_list() {
             None => {
-                span_handler.span_err(attr.span, "malformed feature attribute, \
-                                                  expected #![feature(...)]");
+                span_err!(span_handler, attr.span, E0555,
+                          "malformed feature attribute, expected #![feature(...)]");
             }
             Some(list) => {
                 for mi in list {
                     let name = match mi.node {
                         ast::MetaItemKind::Word(ref word) => (*word).clone(),
                         _ => {
-                            span_handler.span_err(mi.span,
-                                                  "malformed feature, expected just \
-                                                   one word");
+                            span_err!(span_handler, mi.span, E0556,
+                                      "malformed feature, expected just one word");
                             continue
                         }
                     };
@@ -1164,11 +1122,11 @@ pub fn get_features(span_handler: &Handler, krate: &ast::Crate) -> Features {
                     }
                     else if let Some(&(_, _, _)) = REMOVED_FEATURES.iter()
                         .find(|& &(n, _, _)| name == n) {
-                        span_handler.span_err(mi.span, "feature has been removed");
+                        span_err!(span_handler, mi.span, E0557, "feature has been removed");
                     }
                     else if let Some(&(_, _, _)) = ACCEPTED_FEATURES.iter()
                         .find(|& &(n, _, _)| name == n) {
-                        features.declared_stable_lang_features.push(mi.span);
+                        features.declared_stable_lang_features.push((name, mi.span));
                     } else {
                         features.declared_lib_features.push((name, mi.span));
                     }
@@ -1180,21 +1138,19 @@ pub fn get_features(span_handler: &Handler, krate: &ast::Crate) -> Features {
     features
 }
 
-pub fn check_crate(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate,
+pub fn check_crate(krate: &ast::Crate,
+                   sess: &ParseSess,
+                   features: &Features,
                    plugin_attributes: &[(String, AttributeType)],
-                   unstable: UnstableFeatures) -> Features {
-    maybe_stage_features(span_handler, krate, unstable);
-    let features = get_features(span_handler, krate);
-    {
-        let ctx = Context {
-            features: &features,
-            span_handler: span_handler,
-            cm: cm,
-            plugin_attributes: plugin_attributes,
-        };
-        visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate);
-    }
-    features
+                   unstable: UnstableFeatures) {
+    maybe_stage_features(&sess.span_diagnostic, krate, unstable);
+    let ctx = Context {
+        features: features,
+        span_handler: &sess.span_diagnostic,
+        cm: sess.codemap(),
+        plugin_attributes: plugin_attributes,
+    };
+    visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate);
 }
 
 #[derive(Clone, Copy)]
@@ -1222,9 +1178,9 @@ fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
         for attr in &krate.attrs {
             if attr.check_name("feature") {
                 let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
-                let ref msg = format!("#[feature] may not be used on the {} release channel",
-                                      release_channel);
-                span_handler.span_err(attr.span, msg);
+                span_err!(span_handler, attr.span, E0554,
+                          "#[feature] may not be used on the {} release channel",
+                          release_channel);
             }
         }
     }
index 2c325080c0c2681c41447856ead46f4099c1d054..ed6f09eed645f7741951542aaeea87665667266c 100644 (file)
 
 use ast::*;
 use ast;
-use attr::{ThinAttributes, ThinAttributesExt};
-use codemap::{respan, Span, Spanned};
+use syntax_pos::Span;
+use codemap::{Spanned, respan};
 use parse::token::{self, keywords};
 use ptr::P;
+use tokenstream::*;
 use util::small_vector::SmallVector;
 use util::move_map::MoveMap;
 
-use std::rc::Rc;
-
 pub trait Folder : Sized {
     // Any additions to this trait should happen in form
     // of a call to a public `noop_*` function that only calls
@@ -102,10 +101,6 @@ pub trait Folder : Sized {
         noop_fold_pat(p, self)
     }
 
-    fn fold_decl(&mut self, d: P<Decl>) -> SmallVector<P<Decl>> {
-        noop_fold_decl(d, self)
-    }
-
     fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
         e.map(|e| noop_fold_expr(e, self))
     }
@@ -179,14 +174,6 @@ pub trait Folder : Sized {
         // fold::noop_fold_mac(_mac, self)
     }
 
-    fn fold_explicit_self(&mut self, es: ExplicitSelf) -> ExplicitSelf {
-        noop_fold_explicit_self(es, self)
-    }
-
-    fn fold_explicit_self_kind(&mut self, es: SelfKind) -> SelfKind {
-        noop_fold_explicit_self_kind(es, self)
-    }
-
     fn fold_lifetime(&mut self, l: Lifetime) -> Lifetime {
         noop_fold_lifetime(l, self)
     }
@@ -235,11 +222,11 @@ pub trait Folder : Sized {
         noop_fold_ty_params(tps, self)
     }
 
-    fn fold_tt(&mut self, tt: &TokenTree) -> TokenTree {
+    fn fold_tt(&mut self, tt: TokenTree) -> TokenTree {
         noop_fold_tt(tt, self)
     }
 
-    fn fold_tts(&mut self, tts: &[TokenTree]) -> Vec<TokenTree> {
+    fn fold_tts(&mut self, tts: Vec<TokenTree>) -> Vec<TokenTree> {
         noop_fold_tts(tts, self)
     }
 
@@ -344,8 +331,8 @@ pub fn fold_attrs<T: Folder>(attrs: Vec<Attribute>, fld: &mut T) -> Vec<Attribut
     attrs.move_flat_map(|x| fld.fold_attribute(x))
 }
 
-pub fn fold_thin_attrs<T: Folder>(attrs: ThinAttributes, fld: &mut T) -> ThinAttributes {
-    attrs.map_thin_attrs(|v| fold_attrs(v, fld))
+pub fn fold_thin_attrs<T: Folder>(attrs: ThinVec<Attribute>, fld: &mut T) -> ThinVec<Attribute> {
+    fold_attrs(attrs.into(), fld).into()
 }
 
 pub fn noop_fold_arm<T: Folder>(Arm {attrs, pats, guard, body}: Arm, fld: &mut T) -> Arm {
@@ -357,19 +344,6 @@ pub fn noop_fold_arm<T: Folder>(Arm {attrs, pats, guard, body}: Arm, fld: &mut T
     }
 }
 
-pub fn noop_fold_decl<T: Folder>(d: P<Decl>, fld: &mut T) -> SmallVector<P<Decl>> {
-    d.and_then(|Spanned {node, span}| match node {
-        DeclKind::Local(l) => SmallVector::one(P(Spanned {
-            node: DeclKind::Local(fld.fold_local(l)),
-            span: fld.new_span(span)
-        })),
-        DeclKind::Item(it) => fld.fold_item(it).into_iter().map(|i| P(Spanned {
-            node: DeclKind::Item(i),
-            span: fld.new_span(span)
-        })).collect()
-    })
-}
-
 pub fn noop_fold_ty_binding<T: Folder>(b: TypeBinding, fld: &mut T) -> TypeBinding {
     TypeBinding {
         id: fld.new_id(b.id),
@@ -383,7 +357,7 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
     t.map(|Ty {id, node, span}| Ty {
         id: fld.new_id(id),
         node: match node {
-            TyKind::Infer => node,
+            TyKind::Infer | TyKind::ImplicitSelf => node,
             TyKind::Vec(ty) => TyKind::Vec(fld.fold_ty(ty)),
             TyKind::Ptr(mt) => TyKind::Ptr(fld.fold_mt(mt)),
             TyKind::Rptr(region, mt) => {
@@ -506,7 +480,7 @@ pub fn noop_fold_local<T: Folder>(l: P<Local>, fld: &mut T) -> P<Local> {
         pat: fld.fold_pat(pat),
         init: init.map(|e| fld.fold_expr(e)),
         span: fld.new_span(span),
-        attrs: attrs.map_thin_attrs(|v| fold_attrs(v, fld)),
+        attrs: fold_attrs(attrs.into(), fld).into(),
     })
 }
 
@@ -523,34 +497,11 @@ pub fn noop_fold_attribute<T: Folder>(at: Attribute, fld: &mut T) -> Option<Attr
     })
 }
 
-pub fn noop_fold_explicit_self_kind<T: Folder>(es: SelfKind, fld: &mut T)
-                                                     -> SelfKind {
-    match es {
-        SelfKind::Static | SelfKind::Value(_) => es,
-        SelfKind::Region(lifetime, m, ident) => {
-            SelfKind::Region(fld.fold_opt_lifetime(lifetime), m, ident)
-        }
-        SelfKind::Explicit(typ, ident) => {
-            SelfKind::Explicit(fld.fold_ty(typ), ident)
-        }
-    }
-}
-
-pub fn noop_fold_explicit_self<T: Folder>(Spanned {span, node}: ExplicitSelf, fld: &mut T)
-                                          -> ExplicitSelf {
-    Spanned {
-        node: fld.fold_explicit_self_kind(node),
-        span: fld.new_span(span)
-    }
-}
-
-
 pub fn noop_fold_mac<T: Folder>(Spanned {node, span}: Mac, fld: &mut T) -> Mac {
     Spanned {
         node: Mac_ {
             path: fld.fold_path(node.path),
-            tts: fld.fold_tts(&node.tts),
-            ctxt: node.ctxt,
+            tts: fld.fold_tts(node.tts),
         },
         span: fld.new_span(span)
     }
@@ -577,34 +528,26 @@ pub fn noop_fold_arg<T: Folder>(Arg {id, pat, ty}: Arg, fld: &mut T) -> Arg {
     }
 }
 
-pub fn noop_fold_tt<T: Folder>(tt: &TokenTree, fld: &mut T) -> TokenTree {
-    match *tt {
+pub fn noop_fold_tt<T: Folder>(tt: TokenTree, fld: &mut T) -> TokenTree {
+    match tt {
         TokenTree::Token(span, ref tok) =>
             TokenTree::Token(span, fld.fold_token(tok.clone())),
-        TokenTree::Delimited(span, ref delimed) => {
-            TokenTree::Delimited(span, Rc::new(
-                            Delimited {
-                                delim: delimed.delim,
-                                open_span: delimed.open_span,
-                                tts: fld.fold_tts(&delimed.tts),
-                                close_span: delimed.close_span,
-                            }
-                        ))
-        },
-        TokenTree::Sequence(span, ref seq) =>
-            TokenTree::Sequence(span,
-                       Rc::new(SequenceRepetition {
-                           tts: fld.fold_tts(&seq.tts),
-                           separator: seq.separator.clone().map(|tok| fld.fold_token(tok)),
-                           ..**seq
-                       })),
+        TokenTree::Delimited(span, delimed) => TokenTree::Delimited(span, Delimited {
+            delim: delimed.delim,
+            open_span: delimed.open_span,
+            tts: fld.fold_tts(delimed.tts),
+            close_span: delimed.close_span,
+        }),
+        TokenTree::Sequence(span, seq) => TokenTree::Sequence(span, SequenceRepetition {
+            tts: fld.fold_tts(seq.tts),
+            separator: seq.separator.clone().map(|tok| fld.fold_token(tok)),
+            ..seq
+        }),
     }
 }
 
-pub fn noop_fold_tts<T: Folder>(tts: &[TokenTree], fld: &mut T) -> Vec<TokenTree> {
-    // FIXME: Does this have to take a tts slice?
-    // Could use move_map otherwise...
-    tts.iter().map(|tt| fld.fold_tt(tt)).collect()
+pub fn noop_fold_tts<T: Folder>(tts: Vec<TokenTree>, fld: &mut T) -> Vec<TokenTree> {
+    tts.move_map(|tt| fld.fold_tt(tt))
 }
 
 // apply ident folder if it's an ident, apply other folds to interpolated nodes
@@ -662,7 +605,7 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T)
             token::NtIdent(Box::new(Spanned::<Ident>{node: fld.fold_ident(id.node), ..*id})),
         token::NtMeta(meta_item) => token::NtMeta(fld.fold_meta_item(meta_item)),
         token::NtPath(path) => token::NtPath(Box::new(fld.fold_path(*path))),
-        token::NtTT(tt) => token::NtTT(P(fld.fold_tt(&tt))),
+        token::NtTT(tt) => token::NtTT(tt.map(|tt| fld.fold_tt(tt))),
         token::NtArm(arm) => token::NtArm(fld.fold_arm(arm)),
         token::NtImplItem(arm) =>
             token::NtImplItem(arm.map(|arm| fld.fold_impl_item(arm)
@@ -875,10 +818,9 @@ fn noop_fold_bounds<T: Folder>(bounds: TyParamBounds, folder: &mut T)
 }
 
 pub fn noop_fold_block<T: Folder>(b: P<Block>, folder: &mut T) -> P<Block> {
-    b.map(|Block {id, stmts, expr, rules, span}| Block {
+    b.map(|Block {id, stmts, rules, span}| Block {
         id: folder.new_id(id),
         stmts: stmts.move_flat_map(|s| folder.fold_stmt(s).into_iter()),
-        expr: expr.and_then(|x| folder.fold_opt_expr(x)),
         rules: rules,
         span: folder.new_span(span),
     })
@@ -975,6 +917,9 @@ pub fn noop_fold_trait_item<T: Folder>(i: TraitItem, folder: &mut T)
                 TraitItemKind::Type(folder.fold_bounds(bounds),
                               default.map(|x| folder.fold_ty(x)))
             }
+            ast::TraitItemKind::Macro(mac) => {
+                TraitItemKind::Macro(folder.fold_mac(mac))
+            }
         },
         span: folder.new_span(i.span)
     })
@@ -1096,7 +1041,6 @@ pub fn noop_fold_method_sig<T: Folder>(sig: MethodSig, folder: &mut T) -> Method
     MethodSig {
         generics: folder.fold_generics(sig.generics),
         abi: sig.abi,
-        explicit_self: folder.fold_explicit_self(sig.explicit_self),
         unsafety: sig.unsafety,
         constness: sig.constness,
         decl: folder.fold_fn_decl(sig.decl)
@@ -1115,16 +1059,15 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                         sub.map(|x| folder.fold_pat(x)))
             }
             PatKind::Lit(e) => PatKind::Lit(folder.fold_expr(e)),
-            PatKind::TupleStruct(pth, pats) => {
+            PatKind::TupleStruct(pth, pats, ddpos) => {
                 PatKind::TupleStruct(folder.fold_path(pth),
-                        pats.map(|pats| pats.move_map(|x| folder.fold_pat(x))))
-            }
-            PatKind::Path(pth) => {
-                PatKind::Path(folder.fold_path(pth))
+                        pats.move_map(|x| folder.fold_pat(x)), ddpos)
             }
-            PatKind::QPath(qself, pth) => {
-                let qself = QSelf {ty: folder.fold_ty(qself.ty), .. qself};
-                PatKind::QPath(qself, folder.fold_path(pth))
+            PatKind::Path(opt_qself, pth) => {
+                let opt_qself = opt_qself.map(|qself| {
+                    QSelf { ty: folder.fold_ty(qself.ty), position: qself.position }
+                });
+                PatKind::Path(opt_qself, folder.fold_path(pth))
             }
             PatKind::Struct(pth, fields, etc) => {
                 let pth = folder.fold_path(pth);
@@ -1138,7 +1081,9 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                 });
                 PatKind::Struct(pth, fs, etc)
             }
-            PatKind::Tup(elts) => PatKind::Tup(elts.move_map(|x| folder.fold_pat(x))),
+            PatKind::Tuple(elts, ddpos) => {
+                PatKind::Tuple(elts.move_map(|x| folder.fold_pat(x)), ddpos)
+            }
             PatKind::Box(inner) => PatKind::Box(folder.fold_pat(inner)),
             PatKind::Ref(inner, mutbl) => PatKind::Ref(folder.fold_pat(inner), mutbl),
             PatKind::Range(e1, e2) => {
@@ -1157,7 +1102,6 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
 
 pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mut T) -> Expr {
     Expr {
-        id: folder.new_id(id),
         node: match node {
             ExprKind::Box(e) => {
                 ExprKind::Box(folder.fold_expr(e))
@@ -1212,23 +1156,27 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
             ExprKind::While(cond, body, opt_ident) => {
                 ExprKind::While(folder.fold_expr(cond),
                           folder.fold_block(body),
-                          opt_ident.map(|i| folder.fold_ident(i)))
+                          opt_ident.map(|label| respan(folder.new_span(label.span),
+                                                       folder.fold_ident(label.node))))
             }
             ExprKind::WhileLet(pat, expr, body, opt_ident) => {
                 ExprKind::WhileLet(folder.fold_pat(pat),
                              folder.fold_expr(expr),
                              folder.fold_block(body),
-                             opt_ident.map(|i| folder.fold_ident(i)))
+                             opt_ident.map(|label| respan(folder.new_span(label.span),
+                                                          folder.fold_ident(label.node))))
             }
             ExprKind::ForLoop(pat, iter, body, opt_ident) => {
                 ExprKind::ForLoop(folder.fold_pat(pat),
                             folder.fold_expr(iter),
                             folder.fold_block(body),
-                            opt_ident.map(|i| folder.fold_ident(i)))
+                            opt_ident.map(|label| respan(folder.new_span(label.span),
+                                                         folder.fold_ident(label.node))))
             }
             ExprKind::Loop(body, opt_ident) => {
                 ExprKind::Loop(folder.fold_block(body),
-                        opt_ident.map(|i| folder.fold_ident(i)))
+                               opt_ident.map(|label| respan(folder.new_span(label.span),
+                                                            folder.fold_ident(label.node))))
             }
             ExprKind::Match(expr, arms) => {
                 ExprKind::Match(folder.fold_expr(expr),
@@ -1280,7 +1228,7 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
                 respan(folder.new_span(label.span),
                        folder.fold_ident(label.node)))
             ),
-            ExprKind::Again(opt_ident) => ExprKind::Again(opt_ident.map(|label|
+            ExprKind::Continue(opt_ident) => ExprKind::Continue(opt_ident.map(|label|
                 respan(folder.new_span(label.span),
                        folder.fold_ident(label.node)))
             ),
@@ -1321,11 +1269,21 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
                         fields.move_map(|x| folder.fold_field(x)),
                         maybe_expr.map(|x| folder.fold_expr(x)))
             },
-            ExprKind::Paren(ex) => ExprKind::Paren(folder.fold_expr(ex)),
+            ExprKind::Paren(ex) => {
+                let sub_expr = folder.fold_expr(ex);
+                return Expr {
+                    // Nodes that are equal modulo `Paren` sugar no-ops should have the same ids.
+                    id: sub_expr.id,
+                    node: ExprKind::Paren(sub_expr),
+                    span: folder.new_span(span),
+                    attrs: fold_attrs(attrs.into(), folder).into(),
+                };
+            }
             ExprKind::Try(ex) => ExprKind::Try(folder.fold_expr(ex)),
         },
+        id: folder.new_id(id),
         span: folder.new_span(span),
-        attrs: attrs.map_thin_attrs(|v| fold_attrs(v, folder)),
+        attrs: fold_attrs(attrs.into(), folder).into(),
     }
 }
 
@@ -1337,44 +1295,50 @@ pub fn noop_fold_exprs<T: Folder>(es: Vec<P<Expr>>, folder: &mut T) -> Vec<P<Exp
     es.move_flat_map(|e| folder.fold_opt_expr(e))
 }
 
-pub fn noop_fold_stmt<T: Folder>(Spanned {node, span}: Stmt, folder: &mut T)
+pub fn noop_fold_stmt<T: Folder>(Stmt {node, span, id}: Stmt, folder: &mut T)
                                  -> SmallVector<Stmt> {
+    let id = folder.new_id(id);
     let span = folder.new_span(span);
+
     match node {
-        StmtKind::Decl(d, id) => {
-            let id = folder.new_id(id);
-            folder.fold_decl(d).into_iter().map(|d| Spanned {
-                node: StmtKind::Decl(d, id),
-                span: span
-            }).collect()
-        }
-        StmtKind::Expr(e, id) => {
-            let id = folder.new_id(id);
-            if let Some(e) = folder.fold_opt_expr(e) {
-                SmallVector::one(Spanned {
-                    node: StmtKind::Expr(e, id),
-                    span: span
+        StmtKind::Local(local) => SmallVector::one(Stmt {
+            id: id,
+            node: StmtKind::Local(folder.fold_local(local)),
+            span: span,
+        }),
+        StmtKind::Item(item) => folder.fold_item(item).into_iter().map(|item| Stmt {
+            id: id,
+            node: StmtKind::Item(item),
+            span: span,
+        }).collect(),
+        StmtKind::Expr(expr) => {
+            if let Some(expr) = folder.fold_opt_expr(expr) {
+                SmallVector::one(Stmt {
+                    id: id,
+                    node: StmtKind::Expr(expr),
+                    span: span,
                 })
             } else {
                 SmallVector::zero()
             }
         }
-        StmtKind::Semi(e, id) => {
-            let id = folder.new_id(id);
-            if let Some(e) = folder.fold_opt_expr(e) {
-                SmallVector::one(Spanned {
-                    node: StmtKind::Semi(e, id),
-                    span: span
+        StmtKind::Semi(expr) => {
+            if let Some(expr) = folder.fold_opt_expr(expr) {
+                SmallVector::one(Stmt {
+                    id: id,
+                    node: StmtKind::Semi(expr),
+                    span: span,
                 })
             } else {
                 SmallVector::zero()
             }
         }
-        StmtKind::Mac(mac, semi, attrs) => SmallVector::one(Spanned {
-            node: StmtKind::Mac(mac.map(|m| folder.fold_mac(m)),
-                                semi,
-                                attrs.map_thin_attrs(|v| fold_attrs(v, folder))),
-            span: span
+        StmtKind::Mac(mac) => SmallVector::one(Stmt {
+            id: id,
+            node: StmtKind::Mac(mac.map(|(mac, semi, attrs)| {
+                (folder.fold_mac(mac), semi, fold_attrs(attrs.into(), folder).into())
+            })),
+            span: span,
         })
     }
 }
diff --git a/src/libsyntax/json.rs b/src/libsyntax/json.rs
new file mode 100644 (file)
index 0000000..dc9a5ee
--- /dev/null
@@ -0,0 +1,369 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A JSON emitter for errors.
+//!
+//! This works by converting errors to a simplified structural format (see the
+//! structs at the start of the file) and then serialising them. These should
+//! contain as much information about the error as possible.
+//!
+//! The format of the JSON output should be considered *unstable*. For now the
+//! structs at the end of this file (Diagnostic*) specify the error format.
+
+// FIXME spec the JSON output properly.
+
+use codemap::CodeMap;
+use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan};
+use errors::registry::Registry;
+use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion, CodeMapper};
+use errors::emitter::Emitter;
+
+use std::rc::Rc;
+use std::io::{self, Write};
+use std::vec;
+
+use rustc_serialize::json::as_json;
+
+pub struct JsonEmitter {
+    dst: Box<Write + Send>,
+    registry: Option<Registry>,
+    cm: Rc<CodeMapper + 'static>,
+}
+
+impl JsonEmitter {
+    pub fn basic() -> JsonEmitter {
+        JsonEmitter::stderr(None, Rc::new(CodeMap::new()))
+    }
+
+    pub fn stderr(registry: Option<Registry>,
+                  code_map: Rc<CodeMap>) -> JsonEmitter {
+        JsonEmitter {
+            dst: Box::new(io::stderr()),
+            registry: registry,
+            cm: code_map,
+        }
+    }
+}
+
+impl Emitter for JsonEmitter {
+    fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, level: Level) {
+        let data = Diagnostic::new(span, msg, code, level, self);
+        if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
+    }
+
+    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
+        let data = Diagnostic::from_diagnostic_builder(db, self);
+        if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
+    }
+}
+
+// The following data types are provided just for serialisation.
+
+#[derive(RustcEncodable)]
+struct Diagnostic<'a> {
+    /// The primary error message.
+    message: &'a str,
+    code: Option<DiagnosticCode>,
+    /// "error: internal compiler error", "error", "warning", "note", "help".
+    level: &'static str,
+    spans: Vec<DiagnosticSpan>,
+    /// Associated diagnostic messages.
+    children: Vec<Diagnostic<'a>>,
+    /// The message as rustc would render it. Currently this is only
+    /// `Some` for "suggestions", but eventually it will include all
+    /// snippets.
+    rendered: Option<String>,
+}
+
+#[derive(RustcEncodable)]
+struct DiagnosticSpan {
+    file_name: String,
+    byte_start: u32,
+    byte_end: u32,
+    /// 1-based.
+    line_start: usize,
+    line_end: usize,
+    /// 1-based, character offset.
+    column_start: usize,
+    column_end: usize,
+    /// Is this a "primary" span -- meaning the point, or one of the points,
+    /// where the error occurred?
+    is_primary: bool,
+    /// Source text from the start of line_start to the end of line_end.
+    text: Vec<DiagnosticSpanLine>,
+    /// Label that should be placed at this location (if any)
+    label: Option<String>,
+    /// If we are suggesting a replacement, this will contain text
+    /// that should be sliced in atop this span. You may prefer to
+    /// load the fully rendered version from the parent `Diagnostic`,
+    /// however.
+    suggested_replacement: Option<String>,
+    /// Macro invocations that created the code at this span, if any.
+    expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
+}
+
+#[derive(RustcEncodable)]
+struct DiagnosticSpanLine {
+    text: String,
+
+    /// 1-based, character offset in self.text.
+    highlight_start: usize,
+
+    highlight_end: usize,
+}
+
+#[derive(RustcEncodable)]
+struct DiagnosticSpanMacroExpansion {
+    /// span where macro was applied to generate this code; note that
+    /// this may itself derive from a macro (if
+    /// `span.expansion.is_some()`)
+    span: DiagnosticSpan,
+
+    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
+    macro_decl_name: String,
+
+    /// span where macro was defined (if known)
+    def_site_span: Option<DiagnosticSpan>,
+}
+
+#[derive(RustcEncodable)]
+struct DiagnosticCode {
+    /// The code itself.
+    code: String,
+    /// An explanation for the code.
+    explanation: Option<&'static str>,
+}
+
+impl<'a> Diagnostic<'a> {
+    fn new(msp: &MultiSpan,
+           msg: &'a str,
+           code: Option<&str>,
+           level: Level,
+           je: &JsonEmitter)
+           -> Diagnostic<'a> {
+        Diagnostic {
+            message: msg,
+            code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je),
+            level: level.to_str(),
+            spans: DiagnosticSpan::from_multispan(msp, je),
+            children: vec![],
+            rendered: None,
+        }
+    }
+
+    fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
+                                   je: &JsonEmitter)
+                                   -> Diagnostic<'c> {
+        Diagnostic {
+            message: &db.message,
+            code: DiagnosticCode::map_opt_string(db.code.clone(), je),
+            level: db.level.to_str(),
+            spans: DiagnosticSpan::from_multispan(&db.span, je),
+            children: db.children.iter().map(|c| {
+                Diagnostic::from_sub_diagnostic(c, je)
+            }).collect(),
+            rendered: None,
+        }
+    }
+
+    fn from_sub_diagnostic<'c>(db: &'c SubDiagnostic, je: &JsonEmitter) -> Diagnostic<'c> {
+        Diagnostic {
+            message: &db.message,
+            code: None,
+            level: db.level.to_str(),
+            spans: db.render_span.as_ref()
+                     .map(|sp| DiagnosticSpan::from_render_span(sp, je))
+                     .unwrap_or_else(|| DiagnosticSpan::from_multispan(&db.span, je)),
+            children: vec![],
+            rendered: db.render_span.as_ref()
+                                    .and_then(|rsp| je.render(rsp)),
+        }
+    }
+}
+
+impl DiagnosticSpan {
+    fn from_span_label(span: SpanLabel,
+                       suggestion: Option<&String>,
+                       je: &JsonEmitter)
+                       -> DiagnosticSpan {
+        Self::from_span_etc(span.span,
+                            span.is_primary,
+                            span.label,
+                            suggestion,
+                            je)
+    }
+
+    fn from_span_etc(span: Span,
+                     is_primary: bool,
+                     label: Option<String>,
+                     suggestion: Option<&String>,
+                     je: &JsonEmitter)
+                     -> DiagnosticSpan {
+        // obtain the full backtrace from the `macro_backtrace`
+        // helper; in some ways, it'd be better to expand the
+        // backtrace ourselves, but the `macro_backtrace` helper makes
+        // some decision, such as dropping some frames, and I don't
+        // want to duplicate that logic here.
+        let backtrace = je.cm.macro_backtrace(span).into_iter();
+        DiagnosticSpan::from_span_full(span,
+                                       is_primary,
+                                       label,
+                                       suggestion,
+                                       backtrace,
+                                       je)
+    }
+
+    fn from_span_full(span: Span,
+                      is_primary: bool,
+                      label: Option<String>,
+                      suggestion: Option<&String>,
+                      mut backtrace: vec::IntoIter<MacroBacktrace>,
+                      je: &JsonEmitter)
+                      -> DiagnosticSpan {
+        let start = je.cm.lookup_char_pos(span.lo);
+        let end = je.cm.lookup_char_pos(span.hi);
+        let backtrace_step = backtrace.next().map(|bt| {
+            let call_site =
+                Self::from_span_full(bt.call_site,
+                                     false,
+                                     None,
+                                     None,
+                                     backtrace,
+                                     je);
+            let def_site_span = bt.def_site_span.map(|sp| {
+                Self::from_span_full(sp,
+                                     false,
+                                     None,
+                                     None,
+                                     vec![].into_iter(),
+                                     je)
+            });
+            Box::new(DiagnosticSpanMacroExpansion {
+                span: call_site,
+                macro_decl_name: bt.macro_decl_name,
+                def_site_span: def_site_span,
+            })
+        });
+        DiagnosticSpan {
+            file_name: start.file.name.clone(),
+            byte_start: span.lo.0,
+            byte_end: span.hi.0,
+            line_start: start.line,
+            line_end: end.line,
+            column_start: start.col.0 + 1,
+            column_end: end.col.0 + 1,
+            is_primary: is_primary,
+            text: DiagnosticSpanLine::from_span(span, je),
+            suggested_replacement: suggestion.cloned(),
+            expansion: backtrace_step,
+            label: label,
+        }
+    }
+
+    fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
+        msp.span_labels()
+           .into_iter()
+           .map(|span_str| Self::from_span_label(span_str, None, je))
+           .collect()
+    }
+
+    fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter)
+                       -> Vec<DiagnosticSpan> {
+        assert_eq!(suggestion.msp.span_labels().len(), suggestion.substitutes.len());
+        suggestion.msp.span_labels()
+                      .into_iter()
+                      .zip(&suggestion.substitutes)
+                      .map(|(span_label, suggestion)| {
+                          DiagnosticSpan::from_span_label(span_label,
+                                                          Some(suggestion),
+                                                          je)
+                      })
+                      .collect()
+    }
+
+    fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
+        match *rsp {
+            RenderSpan::FullSpan(ref msp) =>
+                DiagnosticSpan::from_multispan(msp, je),
+            RenderSpan::Suggestion(ref suggestion) =>
+                DiagnosticSpan::from_suggestion(suggestion, je),
+        }
+    }
+}
+
+impl DiagnosticSpanLine {
+    fn line_from_filemap(fm: &syntax_pos::FileMap,
+                         index: usize,
+                         h_start: usize,
+                         h_end: usize)
+                         -> DiagnosticSpanLine {
+        DiagnosticSpanLine {
+            text: fm.get_line(index).unwrap().to_owned(),
+            highlight_start: h_start,
+            highlight_end: h_end,
+        }
+    }
+
+    /// Create a list of DiagnosticSpanLines from span - each line with any part
+    /// of `span` gets a DiagnosticSpanLine, with the highlight indicating the
+    /// `span` within the line.
+    fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
+        je.cm.span_to_lines(span)
+             .map(|lines| {
+                 let fm = &*lines.file;
+                 lines.lines
+                      .iter()
+                      .map(|line| {
+                          DiagnosticSpanLine::line_from_filemap(fm,
+                                                                line.line_index,
+                                                                line.start_col.0 + 1,
+                                                                line.end_col.0 + 1)
+                      })
+                     .collect()
+             })
+            .unwrap_or(vec![])
+    }
+}
+
+impl DiagnosticCode {
+    fn map_opt_string(s: Option<String>, je: &JsonEmitter) -> Option<DiagnosticCode> {
+        s.map(|s| {
+
+            let explanation = je.registry
+                                .as_ref()
+                                .and_then(|registry| registry.find_description(&s));
+
+            DiagnosticCode {
+                code: s,
+                explanation: explanation,
+            }
+        })
+    }
+}
+
+impl JsonEmitter {
+    fn render(&self, render_span: &RenderSpan) -> Option<String> {
+        use std::borrow::Borrow;
+
+        match *render_span {
+            RenderSpan::FullSpan(_) => {
+                None
+            }
+            RenderSpan::Suggestion(ref suggestion) => {
+                Some(suggestion.splice_lines(self.cm.borrow()))
+            }
+        }
+    }
+}
+
index 420a41e03b9142c06b2382f0c3e1707026b817b4..8febf1c49ec2badeb618ed632b4c661168da73d4 100644 (file)
@@ -33,7 +33,7 @@
 #![feature(str_escape)]
 #![feature(unicode)]
 #![feature(question_mark)]
-#![feature(range_contains)]
+#![feature(rustc_diagnostic_macros)]
 
 extern crate serialize;
 extern crate term;
@@ -41,9 +41,12 @@ extern crate libc;
 #[macro_use] extern crate log;
 #[macro_use] #[no_link] extern crate rustc_bitflags;
 extern crate rustc_unicode;
+pub extern crate rustc_errors as errors;
+extern crate syntax_pos;
 
 extern crate serialize as rustc_serialize; // used by deriving
 
+
 // A variant of 'try!' that panics on an Err. This is used as a crutch on the
 // way towards a non-panic!-prone parser. It should be used for fatal parsing
 // errors; eventually we plan to convert all code using panictry to just use
@@ -53,7 +56,7 @@ extern crate serialize as rustc_serialize; // used by deriving
 macro_rules! panictry {
     ($e:expr) => ({
         use std::result::Result::{Ok, Err};
-        use $crate::errors::FatalError;
+        use errors::FatalError;
         match $e {
             Ok(e) => e,
             Err(mut e) => {
@@ -64,6 +67,18 @@ macro_rules! panictry {
     })
 }
 
+#[macro_use]
+pub mod diagnostics {
+    #[macro_use]
+    pub mod macros;
+    pub mod plugin;
+    pub mod metadata;
+}
+
+// NB: This module needs to be declared first so diagnostics are
+// registered before they are used.
+pub mod diagnostic_list;
+
 pub mod util {
     pub mod interner;
     pub mod lev_distance;
@@ -73,16 +88,12 @@ pub mod util {
     pub mod parser_testing;
     pub mod small_vector;
     pub mod move_map;
-}
 
-pub mod diagnostics {
-    pub mod macros;
-    pub mod plugin;
-    pub mod registry;
-    pub mod metadata;
+    mod thin_vec;
+    pub use self::thin_vec::ThinVec;
 }
 
-pub mod errors;
+pub mod json;
 
 pub mod syntax {
     pub use ext;
@@ -104,6 +115,7 @@ pub mod show_span;
 pub mod std_inject;
 pub mod str;
 pub mod test;
+pub mod tokenstream;
 pub mod visit;
 
 pub mod print {
@@ -125,3 +137,5 @@ pub mod ext {
         pub mod macro_rules;
     }
 }
+
+// __build_diagnostic_array! { libsyntax, DIAGNOSTICS }
index db643eb0df07a680b076431fc3f9fe68a94c1ba0..15344cef1dbcf438d157614df5bfd39064462152 100644 (file)
@@ -10,7 +10,8 @@
 
 use attr;
 use ast;
-use codemap::{spanned, Spanned, mk_sp, Span};
+use syntax_pos::{mk_sp, Span};
+use codemap::{spanned, Spanned};
 use parse::common::SeqSep;
 use parse::PResult;
 use parse::token;
@@ -159,12 +160,9 @@ impl<'a> Parser<'a> {
             _ => None,
         };
 
-        match nt_meta {
-            Some(meta) => {
-                self.bump();
-                return Ok(meta);
-            }
-            None => {}
+        if let Some(meta) = nt_meta {
+            self.bump();
+            return Ok(meta);
         }
 
         let lo = self.span.lo;
index 89110f3160fc9343fe24acafd6b003bc243e0faf..4fe4ec7e4c0ed3f785fb498ded087152b4b67892 100644 (file)
@@ -47,13 +47,9 @@ pub fn expr_is_simple_block(e: &ast::Expr) -> bool {
 /// seen the semicolon, and thus don't need another.
 pub fn stmt_ends_with_semi(stmt: &ast::StmtKind) -> bool {
     match *stmt {
-        ast::StmtKind::Decl(ref d, _) => {
-            match d.node {
-                ast::DeclKind::Local(_) => true,
-                ast::DeclKind::Item(_) => false,
-            }
-        }
-        ast::StmtKind::Expr(ref e, _) => expr_requires_semi_to_be_stmt(e),
+        ast::StmtKind::Local(_) => true,
+        ast::StmtKind::Item(_) => false,
+        ast::StmtKind::Expr(ref e) => expr_requires_semi_to_be_stmt(e),
         ast::StmtKind::Semi(..) => false,
         ast::StmtKind::Mac(..) => false,
     }
index 629edced804f51f015f88e89989caa5fc294a485..5eb5605ea71a0e4f93a575b364216fb2adddc09a 100644 (file)
@@ -11,7 +11,8 @@
 pub use self::CommentStyle::*;
 
 use ast;
-use codemap::{BytePos, CharPos, CodeMap, Pos};
+use codemap::CodeMap;
+use syntax_pos::{BytePos, CharPos, Pos};
 use errors;
 use parse::lexer::is_block_doc_comment;
 use parse::lexer::{StringReader, TokenAndSpan};
@@ -346,7 +347,7 @@ pub fn gather_comments_and_literals(span_diagnostic: &errors::Handler,
     srdr.read_to_end(&mut src).unwrap();
     let src = String::from_utf8(src).unwrap();
     let cm = CodeMap::new();
-    let filemap = cm.new_filemap(path, src);
+    let filemap = cm.new_filemap(path, None, src);
     let mut rdr = lexer::StringReader::new_raw(span_diagnostic, filemap);
 
     let mut comments: Vec<Comment> = Vec::new();
index da62e5286d4e7ef99688ba812a26e854f5908878..77b5c10899a3da8cc158c1003161b44a200d78bf 100644 (file)
@@ -9,8 +9,8 @@
 // except according to those terms.
 
 use ast;
-use codemap::{BytePos, CharPos, CodeMap, Pos, Span};
-use codemap;
+use syntax_pos::{self, BytePos, CharPos, Pos, Span};
+use codemap::CodeMap;
 use errors::{FatalError, Handler, DiagnosticBuilder};
 use ext::tt::transcribe::tt_next_token;
 use parse::token::{self, keywords, str_to_ident};
@@ -84,7 +84,7 @@ pub struct StringReader<'a> {
     pub col: CharPos,
     /// The last character to be read
     pub curr: Option<char>,
-    pub filemap: Rc<codemap::FileMap>,
+    pub filemap: Rc<syntax_pos::FileMap>,
     // cached:
     pub peek_tok: token::Token,
     pub peek_span: Span,
@@ -162,7 +162,7 @@ impl<'a> Reader for TtReader<'a> {
 impl<'a> StringReader<'a> {
     /// For comments.rs, which hackily pokes into pos and curr
     pub fn new_raw<'b>(span_diagnostic: &'b Handler,
-                       filemap: Rc<codemap::FileMap>)
+                       filemap: Rc<syntax_pos::FileMap>)
                        -> StringReader<'b> {
         if filemap.src.is_none() {
             span_diagnostic.bug(&format!("Cannot lex filemap \
@@ -181,7 +181,7 @@ impl<'a> StringReader<'a> {
             filemap: filemap,
             // dummy values; not read
             peek_tok: token::Eof,
-            peek_span: codemap::DUMMY_SP,
+            peek_span: syntax_pos::DUMMY_SP,
             source_text: source_text,
             fatal_errs: Vec::new(),
         };
@@ -190,7 +190,7 @@ impl<'a> StringReader<'a> {
     }
 
     pub fn new<'b>(span_diagnostic: &'b Handler,
-                   filemap: Rc<codemap::FileMap>)
+                   filemap: Rc<syntax_pos::FileMap>)
                    -> StringReader<'b> {
         let mut sr = StringReader::new_raw(span_diagnostic, filemap);
         if let Err(_) = sr.advance_token() {
@@ -217,12 +217,12 @@ impl<'a> StringReader<'a> {
 
     /// Report a fatal error spanning [`from_pos`, `to_pos`).
     fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError {
-        self.fatal_span(codemap::mk_sp(from_pos, to_pos), m)
+        self.fatal_span(syntax_pos::mk_sp(from_pos, to_pos), m)
     }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`).
     fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) {
-        self.err_span(codemap::mk_sp(from_pos, to_pos), m)
+        self.err_span(syntax_pos::mk_sp(from_pos, to_pos), m)
     }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
@@ -246,7 +246,7 @@ impl<'a> StringReader<'a> {
         for c in c.escape_default() {
             m.push(c)
         }
-        self.span_diagnostic.struct_span_fatal(codemap::mk_sp(from_pos, to_pos), &m[..])
+        self.span_diagnostic.struct_span_fatal(syntax_pos::mk_sp(from_pos, to_pos), &m[..])
     }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
@@ -270,7 +270,7 @@ impl<'a> StringReader<'a> {
         for c in c.escape_default() {
             m.push(c)
         }
-        self.span_diagnostic.struct_span_err(codemap::mk_sp(from_pos, to_pos), &m[..])
+        self.span_diagnostic.struct_span_err(syntax_pos::mk_sp(from_pos, to_pos), &m[..])
     }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending the
@@ -294,11 +294,11 @@ impl<'a> StringReader<'a> {
             None => {
                 if self.is_eof() {
                     self.peek_tok = token::Eof;
-                    self.peek_span = codemap::mk_sp(self.filemap.end_pos, self.filemap.end_pos);
+                    self.peek_span = syntax_pos::mk_sp(self.filemap.end_pos, self.filemap.end_pos);
                 } else {
                     let start_bytepos = self.last_pos;
                     self.peek_tok = self.next_token_inner()?;
-                    self.peek_span = codemap::mk_sp(start_bytepos, self.last_pos);
+                    self.peek_span = syntax_pos::mk_sp(start_bytepos, self.last_pos);
                 };
             }
         }
@@ -470,15 +470,12 @@ impl<'a> StringReader<'a> {
     /// PRECONDITION: self.curr is not whitespace
     /// Eats any kind of comment.
     fn scan_comment(&mut self) -> Option<TokenAndSpan> {
-        match self.curr {
-            Some(c) => {
-                if c.is_whitespace() {
-                    self.span_diagnostic.span_err(codemap::mk_sp(self.last_pos, self.last_pos),
-                                                  "called consume_any_line_comment, but there \
-                                                   was whitespace");
-                }
+        if let Some(c) = self.curr {
+            if c.is_whitespace() {
+                self.span_diagnostic.span_err(syntax_pos::mk_sp(self.last_pos, self.last_pos),
+                                              "called consume_any_line_comment, but there \
+                                               was whitespace");
             }
-            None => {}
         }
 
         if self.curr_is('/') {
@@ -524,13 +521,13 @@ impl<'a> StringReader<'a> {
 
                             Some(TokenAndSpan {
                                 tok: tok,
-                                sp: codemap::mk_sp(start_bpos, self.last_pos),
+                                sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
                             })
                         })
                     } else {
                         Some(TokenAndSpan {
                             tok: token::Comment,
-                            sp: codemap::mk_sp(start_bpos, self.last_pos),
+                            sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
                         })
                     };
                 }
@@ -563,7 +560,7 @@ impl<'a> StringReader<'a> {
                     }
                     return Some(TokenAndSpan {
                         tok: token::Shebang(self.name_from(start)),
-                        sp: codemap::mk_sp(start, self.last_pos),
+                        sp: syntax_pos::mk_sp(start, self.last_pos),
                     });
                 }
             }
@@ -591,7 +588,7 @@ impl<'a> StringReader<'a> {
                 }
                 let c = Some(TokenAndSpan {
                     tok: token::Whitespace,
-                    sp: codemap::mk_sp(start_bpos, self.last_pos),
+                    sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
                 });
                 debug!("scanning whitespace: {:?}", c);
                 c
@@ -653,7 +650,7 @@ impl<'a> StringReader<'a> {
 
             Some(TokenAndSpan {
                 tok: tok,
-                sp: codemap::mk_sp(start_bpos, self.last_pos),
+                sp: syntax_pos::mk_sp(start_bpos, self.last_pos),
             })
         })
     }
@@ -850,7 +847,7 @@ impl<'a> StringReader<'a> {
                                 let valid = if self.curr_is('{') {
                                     self.scan_unicode_escape(delim) && !ascii_only
                                 } else {
-                                    let span = codemap::mk_sp(start, self.last_pos);
+                                    let span = syntax_pos::mk_sp(start, self.last_pos);
                                     self.span_diagnostic
                                         .struct_span_err(span, "incorrect unicode escape sequence")
                                         .span_help(span,
@@ -888,13 +885,13 @@ impl<'a> StringReader<'a> {
                                                                         },
                                                                         c);
                                 if e == '\r' {
-                                    err.span_help(codemap::mk_sp(escaped_pos, last_pos),
+                                    err.span_help(syntax_pos::mk_sp(escaped_pos, last_pos),
                                                   "this is an isolated carriage return; consider \
                                                    checking your editor and version control \
                                                    settings");
                                 }
                                 if (e == '{' || e == '}') && !ascii_only {
-                                    err.span_help(codemap::mk_sp(escaped_pos, last_pos),
+                                    err.span_help(syntax_pos::mk_sp(escaped_pos, last_pos),
                                                   "if used in a formatting string, curly braces \
                                                    are escaped with `{{` and `}}`");
                                 }
@@ -1677,7 +1674,8 @@ fn ident_continue(c: Option<char>) -> bool {
 mod tests {
     use super::*;
 
-    use codemap::{BytePos, CodeMap, Span, NO_EXPANSION};
+    use syntax_pos::{BytePos, Span, NO_EXPANSION};
+    use codemap::CodeMap;
     use errors;
     use parse::token;
     use parse::token::str_to_ident;
@@ -1686,7 +1684,10 @@ mod tests {
 
     fn mk_sh(cm: Rc<CodeMap>) -> errors::Handler {
         // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
-        let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), None, cm);
+        let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()),
+                                                None,
+                                                cm,
+                                                errors::snippet::FormatMode::EnvironmentSelected);
         errors::Handler::with_emitter(true, false, Box::new(emitter))
     }
 
@@ -1695,7 +1696,7 @@ mod tests {
                  span_handler: &'a errors::Handler,
                  teststr: String)
                  -> StringReader<'a> {
-        let fm = cm.new_filemap("zebra.rs".to_string(), teststr);
+        let fm = cm.new_filemap("zebra.rs".to_string(), None, teststr);
         StringReader::new(span_handler, fm)
     }
 
@@ -1889,7 +1890,7 @@ mod tests {
         let mut lexer = setup(&cm, &sh, "// test\r\n/// test\r\n".to_string());
         let comment = lexer.next_token();
         assert_eq!(comment.tok, token::Comment);
-        assert_eq!(comment.sp, ::codemap::mk_sp(BytePos(0), BytePos(7)));
+        assert_eq!(comment.sp, ::syntax_pos::mk_sp(BytePos(0), BytePos(7)));
         assert_eq!(lexer.next_token().tok, token::Whitespace);
         assert_eq!(lexer.next_token().tok,
                    token::DocComment(token::intern("/// test")));
index d337c78bee8b5d1207658c06a41dc094f2d8823e..dab97d1d5a6ffc8872ec6733758086ea49e468e2 100644 (file)
@@ -11,7 +11,7 @@
 // Characters and their corresponding confusables were collected from
 // http://www.unicode.org/Public/security/revision-06/confusables.txt
 
-use codemap::mk_sp as make_span;
+use syntax_pos::mk_sp as make_span;
 use errors::DiagnosticBuilder;
 use super::StringReader;
 
index 2a9bcfd658c18a8925e1c6b1308602968549e7fe..bbcc044d43c6b74b8701924847df75867f0a8ec6 100644 (file)
 //! The main parser interface
 
 use ast;
-use codemap::{self, Span, CodeMap, FileMap};
+use codemap::CodeMap;
+use syntax_pos::{self, Span, FileMap};
 use errors::{Handler, ColorConfig, DiagnosticBuilder};
 use parse::parser::Parser;
 use parse::token::InternedString;
 use ptr::P;
 use str::char_at;
+use tokenstream;
 
 use std::cell::RefCell;
 use std::iter;
@@ -160,7 +162,7 @@ pub fn parse_tts_from_source_str<'a>(name: String,
                                      source: String,
                                      cfg: ast::CrateConfig,
                                      sess: &'a ParseSess)
-                                     -> PResult<'a, Vec<ast::TokenTree>> {
+                                     -> PResult<'a, Vec<tokenstream::TokenTree>> {
     let mut p = new_parser_from_source_str(
         sess,
         cfg,
@@ -178,7 +180,7 @@ pub fn new_parser_from_source_str<'a>(sess: &'a ParseSess,
                                       name: String,
                                       source: String)
                                       -> Parser<'a> {
-    filemap_to_parser(sess, sess.codemap().new_filemap(name, source), cfg)
+    filemap_to_parser(sess, sess.codemap().new_filemap(name, None, source), cfg)
 }
 
 /// Create a new parser, handling errors as appropriate
@@ -211,8 +213,8 @@ pub fn filemap_to_parser<'a>(sess: &'a ParseSess,
     let end_pos = filemap.end_pos;
     let mut parser = tts_to_parser(sess, filemap_to_tts(sess, filemap), cfg);
 
-    if parser.token == token::Eof && parser.span == codemap::DUMMY_SP {
-        parser.span = codemap::mk_sp(end_pos, end_pos);
+    if parser.token == token::Eof && parser.span == syntax_pos::DUMMY_SP {
+        parser.span = syntax_pos::mk_sp(end_pos, end_pos);
     }
 
     parser
@@ -222,7 +224,7 @@ pub fn filemap_to_parser<'a>(sess: &'a ParseSess,
 // compiler expands into it
 pub fn new_parser_from_tts<'a>(sess: &'a ParseSess,
                                cfg: ast::CrateConfig,
-                               tts: Vec<ast::TokenTree>) -> Parser<'a> {
+                               tts: Vec<tokenstream::TokenTree>) -> Parser<'a> {
     tts_to_parser(sess, tts, cfg)
 }
 
@@ -247,7 +249,7 @@ fn file_to_filemap(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
 
 /// Given a filemap, produce a sequence of token-trees
 pub fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>)
-    -> Vec<ast::TokenTree> {
+    -> Vec<tokenstream::TokenTree> {
     // it appears to me that the cfg doesn't matter here... indeed,
     // parsing tt's probably shouldn't require a parser at all.
     let cfg = Vec::new();
@@ -258,7 +260,7 @@ pub fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>)
 
 /// Given tts and cfg, produce a parser
 pub fn tts_to_parser<'a>(sess: &'a ParseSess,
-                         tts: Vec<ast::TokenTree>,
+                         tts: Vec<tokenstream::TokenTree>,
                          cfg: ast::CrateConfig) -> Parser<'a> {
     let trdr = lexer::new_tt_reader(&sess.span_diagnostic, None, None, tts);
     let mut p = Parser::new(sess, cfg, Box::new(trdr));
@@ -660,9 +662,9 @@ pub fn integer_lit(s: &str,
 #[cfg(test)]
 mod tests {
     use super::*;
-    use std::rc::Rc;
-    use codemap::{Span, BytePos, Pos, Spanned, NO_EXPANSION};
-    use ast::{self, TokenTree, PatKind};
+    use syntax_pos::{Span, BytePos, Pos, NO_EXPANSION};
+    use codemap::Spanned;
+    use ast::{self, PatKind};
     use abi::Abi;
     use attr::{first_attr_value_str_by_name, AttrMetaMethods};
     use parse;
@@ -670,10 +672,12 @@ mod tests {
     use parse::token::{str_to_ident};
     use print::pprust::item_to_string;
     use ptr::P;
+    use tokenstream::{self, TokenTree};
     use util::parser_testing::{string_to_tts, string_to_parser};
     use util::parser_testing::{string_to_expr, string_to_item, string_to_stmt};
+    use util::ThinVec;
 
-    // produce a codemap::span
+    // produce a syntax_pos::span
     fn sp(a: u32, b: u32) -> Span {
         Span {lo: BytePos(a), hi: BytePos(b), expn_id: NO_EXPANSION}
     }
@@ -693,7 +697,7 @@ mod tests {
                         ),
                     }),
                     span: sp(0, 1),
-                    attrs: None,
+                    attrs: ThinVec::new(),
                    }))
     }
 
@@ -716,7 +720,7 @@ mod tests {
                             )
                         }),
                     span: sp(0, 6),
-                    attrs: None,
+                    attrs: ThinVec::new(),
                    }))
     }
 
@@ -729,7 +733,7 @@ mod tests {
     #[test]
     fn string_to_tts_macro () {
         let tts = string_to_tts("macro_rules! zip (($a)=>($a))".to_string());
-        let tts: &[ast::TokenTree] = &tts[..];
+        let tts: &[tokenstream::TokenTree] = &tts[..];
 
         match (tts.len(), tts.get(0), tts.get(1), tts.get(2), tts.get(3)) {
             (
@@ -759,7 +763,7 @@ mod tests {
                             )
                             if first_delimed.delim == token::Paren
                             && ident.name.as_str() == "a" => {},
-                            _ => panic!("value 3: {:?}", **first_delimed),
+                            _ => panic!("value 3: {:?}", *first_delimed),
                         }
                         let tts = &second_delimed.tts[..];
                         match (tts.len(), tts.get(0), tts.get(1)) {
@@ -770,10 +774,10 @@ mod tests {
                             )
                             if second_delimed.delim == token::Paren
                             && ident.name.as_str() == "a" => {},
-                            _ => panic!("value 4: {:?}", **second_delimed),
+                            _ => panic!("value 4: {:?}", *second_delimed),
                         }
                     },
-                    _ => panic!("value 2: {:?}", **macro_delimed),
+                    _ => panic!("value 2: {:?}", *macro_delimed),
                 }
             },
             _ => panic!("value: {:?}",tts),
@@ -789,7 +793,7 @@ mod tests {
             TokenTree::Token(sp(3, 4), token::Ident(str_to_ident("a"))),
             TokenTree::Delimited(
                 sp(5, 14),
-                Rc::new(ast::Delimited {
+                tokenstream::Delimited {
                     delim: token::DelimToken::Paren,
                     open_span: sp(5, 6),
                     tts: vec![
@@ -798,10 +802,10 @@ mod tests {
                         TokenTree::Token(sp(10, 13), token::Ident(str_to_ident("i32"))),
                     ],
                     close_span: sp(13, 14),
-                })),
+                }),
             TokenTree::Delimited(
                 sp(15, 21),
-                Rc::new(ast::Delimited {
+                tokenstream::Delimited {
                     delim: token::DelimToken::Brace,
                     open_span: sp(15, 16),
                     tts: vec![
@@ -809,7 +813,7 @@ mod tests {
                         TokenTree::Token(sp(18, 19), token::Semi),
                     ],
                     close_span: sp(20, 21),
-                }))
+                })
         ];
 
         assert_eq!(tts, expected);
@@ -832,16 +836,16 @@ mod tests {
                             ),
                         }),
                         span:sp(7,8),
-                        attrs: None,
+                        attrs: ThinVec::new(),
                     }))),
                     span:sp(0,8),
-                    attrs: None,
+                    attrs: ThinVec::new(),
                    }))
     }
 
     #[test] fn parse_stmt_1 () {
         assert!(string_to_stmt("b;".to_string()) ==
-                   Some(Spanned{
+                   Some(ast::Stmt {
                        node: ast::StmtKind::Expr(P(ast::Expr {
                            id: ast::DUMMY_NODE_ID,
                            node: ast::ExprKind::Path(None, ast::Path {
@@ -855,8 +859,8 @@ mod tests {
                                ),
                             }),
                            span: sp(0,1),
-                           attrs: None}),
-                                           ast::DUMMY_NODE_ID),
+                           attrs: ThinVec::new()})),
+                       id: ast::DUMMY_NODE_ID,
                        span: sp(0,1)}))
 
     }
@@ -932,7 +936,7 @@ mod tests {
                                         }
                                     },
                                     P(ast::Block {
-                                        stmts: vec!(Spanned{
+                                        stmts: vec!(ast::Stmt {
                                             node: ast::StmtKind::Semi(P(ast::Expr{
                                                 id: ast::DUMMY_NODE_ID,
                                                 node: ast::ExprKind::Path(None,
@@ -950,10 +954,9 @@ mod tests {
                                                         ),
                                                       }),
                                                 span: sp(17,18),
-                                                attrs: None,}),
-                                                ast::DUMMY_NODE_ID),
+                                                attrs: ThinVec::new()})),
+                                            id: ast::DUMMY_NODE_ID,
                                             span: sp(17,19)}),
-                                        expr: None,
                                         id: ast::DUMMY_NODE_ID,
                                         rules: ast::BlockCheckMode::Default, // no idea
                                         span: sp(15,21),
@@ -992,8 +995,8 @@ mod tests {
         struct PatIdentVisitor {
             spans: Vec<Span>
         }
-        impl<'v> ::visit::Visitor<'v> for PatIdentVisitor {
-            fn visit_pat(&mut self, p: &'v ast::Pat) {
+        impl ::visit::Visitor for PatIdentVisitor {
+            fn visit_pat(&mut self, p: &ast::Pat) {
                 match p.node {
                     PatKind::Ident(_ , ref spannedident, _) => {
                         self.spans.push(spannedident.span.clone());
index 75f1ac49c9acc92956446f5db7d56edab98e57b7..a1d7ddcdf4bdf5fbe19a2af879c034ad65bdf051 100644 (file)
@@ -13,7 +13,7 @@
 //!
 //! Obsolete syntax that becomes too hard to parse can be removed.
 
-use codemap::Span;
+use syntax_pos::Span;
 use parse::parser;
 
 /// The specific types of unsupported syntax
index fc62cee92fdbc5948b2dec120236b7125c927129..6fa95afd9fb2223c9dec5dc1565cb68a40db4e7f 100644 (file)
@@ -16,8 +16,8 @@ use ast::{Mod, Arg, Arm, Attribute, BindingMode, TraitItemKind};
 use ast::Block;
 use ast::{BlockCheckMode, CaptureBy};
 use ast::{Constness, Crate, CrateConfig};
-use ast::{Decl, DeclKind, Defaultness};
-use ast::{EMPTY_CTXT, EnumDef, ExplicitSelf};
+use ast::Defaultness;
+use ast::EnumDef;
 use ast::{Expr, ExprKind, RangeLimits};
 use ast::{Field, FnDecl};
 use ast::{ForeignItem, ForeignItemKind, FunctionRetTy};
@@ -33,14 +33,14 @@ use ast::{Stmt, StmtKind};
 use ast::{VariantData, StructField};
 use ast::StrStyle;
 use ast::SelfKind;
-use ast::{Delimited, SequenceRepetition, TokenTree, TraitItem, TraitRef};
+use ast::{TraitItem, TraitRef};
 use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds};
 use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
 use ast::{Visibility, WhereClause};
-use attr::{ThinAttributes, ThinAttributesExt, AttributesExt};
 use ast::{BinOpKind, UnOp};
 use ast;
-use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap};
+use codemap::{self, CodeMap, Spanned, spanned};
+use syntax_pos::{self, Span, BytePos, mk_sp};
 use errors::{self, DiagnosticBuilder};
 use ext::tt::macro_parser;
 use parse;
@@ -55,6 +55,8 @@ use util::parser::{AssocOp, Fixity};
 use print::pprust;
 use ptr::P;
 use parse::PResult;
+use tokenstream::{self, Delimited, SequenceRepetition, TokenTree};
+use util::ThinVec;
 
 use std::collections::HashSet;
 use std::mem;
@@ -120,7 +122,7 @@ macro_rules! maybe_whole_expr {
                         _ => unreachable!()
                     };
                     let span = $p.span;
-                    Some($p.mk_expr(span.lo, span.hi, ExprKind::Path(None, pt), None))
+                    Some($p.mk_expr(span.lo, span.hi, ExprKind::Path(None, pt), ThinVec::new()))
                 }
                 token::Interpolated(token::NtBlock(_)) => {
                     // FIXME: The following avoids an issue with lexical borrowck scopes,
@@ -130,7 +132,7 @@ macro_rules! maybe_whole_expr {
                         _ => unreachable!()
                     };
                     let span = $p.span;
-                    Some($p.mk_expr(span.lo, span.hi, ExprKind::Block(b), None))
+                    Some($p.mk_expr(span.lo, span.hi, ExprKind::Block(b), ThinVec::new()))
                 }
                 _ => None
             };
@@ -316,12 +318,12 @@ pub struct ModulePathError {
 
 pub enum LhsExpr {
     NotYetParsed,
-    AttributesParsed(ThinAttributes),
+    AttributesParsed(ThinVec<Attribute>),
     AlreadyParsed(P<Expr>),
 }
 
-impl From<Option<ThinAttributes>> for LhsExpr {
-    fn from(o: Option<ThinAttributes>) -> Self {
+impl From<Option<ThinVec<Attribute>>> for LhsExpr {
+    fn from(o: Option<ThinVec<Attribute>>) -> Self {
         if let Some(attrs) = o {
             LhsExpr::AttributesParsed(attrs)
         } else {
@@ -344,7 +346,7 @@ impl<'a> Parser<'a> {
     {
         let tok0 = rdr.real_token();
         let span = tok0.sp;
-        let filename = if span != codemap::DUMMY_SP {
+        let filename = if span != syntax_pos::DUMMY_SP {
             Some(sess.codemap().span_to_filename(span))
         } else { None };
         let placeholder = TokenAndSpan {
@@ -551,10 +553,6 @@ impl<'a> Parser<'a> {
         self.expect_one_of(edible, inedible)
     }
 
-    pub fn commit_stmt_expecting(&mut self, edible: token::Token) -> PResult<'a, ()> {
-        self.commit_stmt(&[edible], &[])
-    }
-
     /// returns the span of expr, if it was not interpolated or the span of the interpolated token
     fn interpolated_or_expr_span(&self,
                                  expr: PResult<'a, P<Expr>>)
@@ -954,25 +952,6 @@ impl<'a> Parser<'a> {
         Ok(result)
     }
 
-    /// Parse a sequence parameter of enum variant. For consistency purposes,
-    /// these should not be empty.
-    pub fn parse_enum_variant_seq<T, F>(&mut self,
-                                        bra: &token::Token,
-                                        ket: &token::Token,
-                                        sep: SeqSep,
-                                        f: F)
-                                        -> PResult<'a, Vec<T>> where
-        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
-    {
-        let result = self.parse_unspanned_seq(bra, ket, sep, f)?;
-        if result.is_empty() {
-            let last_span = self.last_span;
-            self.span_err(last_span,
-            "nullary enum variants are written with no trailing `( )`");
-        }
-        Ok(result)
-    }
-
     // NB: Do not use this function unless you actually plan to place the
     // spanned list in the AST.
     pub fn parse_seq<T, F>(&mut self,
@@ -1251,55 +1230,70 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the items in a trait declaration
-    pub fn parse_trait_items(&mut self) -> PResult<'a,  Vec<TraitItem>> {
-        self.parse_unspanned_seq(
-            &token::OpenDelim(token::Brace),
-            &token::CloseDelim(token::Brace),
-            SeqSep::none(),
-            |p| -> PResult<'a, TraitItem> {
-            maybe_whole!(no_clone_from_p p, NtTraitItem);
-            let mut attrs = p.parse_outer_attributes()?;
-            let lo = p.span.lo;
-
-            let (name, node) = if p.eat_keyword(keywords::Type) {
-                let TyParam {ident, bounds, default, ..} = p.parse_ty_param()?;
-                p.expect(&token::Semi)?;
-                (ident, TraitItemKind::Type(bounds, default))
-            } else if p.is_const_item() {
-                p.expect_keyword(keywords::Const)?;
-                let ident = p.parse_ident()?;
-                p.expect(&token::Colon)?;
-                let ty = p.parse_ty_sum()?;
-                let default = if p.check(&token::Eq) {
-                    p.bump();
-                    let expr = p.parse_expr()?;
-                    p.commit_expr_expecting(&expr, token::Semi)?;
-                    Some(expr)
-                } else {
-                    p.expect(&token::Semi)?;
-                    None
-                };
-                (ident, TraitItemKind::Const(ty, default))
+    pub fn parse_trait_item(&mut self) -> PResult<'a, TraitItem> {
+        maybe_whole!(no_clone_from_p self, NtTraitItem);
+        let mut attrs = self.parse_outer_attributes()?;
+        let lo = self.span.lo;
+
+        let (name, node) = if self.eat_keyword(keywords::Type) {
+            let TyParam {ident, bounds, default, ..} = self.parse_ty_param()?;
+            self.expect(&token::Semi)?;
+            (ident, TraitItemKind::Type(bounds, default))
+        } else if self.is_const_item() {
+                self.expect_keyword(keywords::Const)?;
+            let ident = self.parse_ident()?;
+            self.expect(&token::Colon)?;
+            let ty = self.parse_ty_sum()?;
+            let default = if self.check(&token::Eq) {
+                self.bump();
+                let expr = self.parse_expr()?;
+                self.commit_expr_expecting(&expr, token::Semi)?;
+                Some(expr)
+            } else {
+                self.expect(&token::Semi)?;
+                None
+            };
+            (ident, TraitItemKind::Const(ty, default))
+        } else if !self.token.is_any_keyword()
+            && self.look_ahead(1, |t| *t == token::Not)
+            && (self.look_ahead(2, |t| *t == token::OpenDelim(token::Paren))
+                || self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace))) {
+                // trait item macro.
+                // code copied from parse_macro_use_or_failure... abstraction!
+                let lo = self.span.lo;
+                let pth = self.parse_ident_into_path()?;
+                self.expect(&token::Not)?;
+
+                // eat a matched-delimiter token tree:
+                let delim = self.expect_open_delim()?;
+                let tts = self.parse_seq_to_end(&token::CloseDelim(delim),
+                                             SeqSep::none(),
+                                             |pp| pp.parse_token_tree())?;
+                let m_ = Mac_ { path: pth, tts: tts };
+                let m: ast::Mac = codemap::Spanned { node: m_,
+                                                     span: mk_sp(lo,
+                                                                 self.last_span.hi) };
+                if delim != token::Brace {
+                    self.expect(&token::Semi)?
+                }
+                (keywords::Invalid.ident(), ast::TraitItemKind::Macro(m))
             } else {
-                let (constness, unsafety, abi) = match p.parse_fn_front_matter() {
+                let (constness, unsafety, abi) = match self.parse_fn_front_matter() {
                     Ok(cua) => cua,
                     Err(e) => {
                         loop {
-                            match p.token {
+                            match self.token {
                                 token::Eof => break,
-
                                 token::CloseDelim(token::Brace) |
                                 token::Semi => {
-                                    p.bump();
+                                    self.bump();
                                     break;
                                 }
-
                                 token::OpenDelim(token::Brace) => {
-                                    p.parse_token_tree()?;
+                                    self.parse_token_tree()?;
                                     break;
                                 }
-
-                                _ => p.bump()
+                                _ => self.bump()
                             }
                         }
 
@@ -1307,57 +1301,66 @@ impl<'a> Parser<'a> {
                     }
                 };
 
-                let ident = p.parse_ident()?;
-                let mut generics = p.parse_generics()?;
+                let ident = self.parse_ident()?;
+                let mut generics = self.parse_generics()?;
 
-                let (explicit_self, d) = p.parse_fn_decl_with_self(|p: &mut Parser<'a>|{
+                let d = self.parse_fn_decl_with_self(|p: &mut Parser<'a>|{
                     // This is somewhat dubious; We don't want to allow
                     // argument names to be left off if there is a
                     // definition...
                     p.parse_arg_general(false)
                 })?;
 
-                generics.where_clause = p.parse_where_clause()?;
+                generics.where_clause = self.parse_where_clause()?;
                 let sig = ast::MethodSig {
                     unsafety: unsafety,
                     constness: constness,
                     decl: d,
                     generics: generics,
                     abi: abi,
-                    explicit_self: explicit_self,
                 };
 
-                let body = match p.token {
-                  token::Semi => {
-                    p.bump();
-                    debug!("parse_trait_methods(): parsing required method");
-                    None
-                  }
-                  token::OpenDelim(token::Brace) => {
-                    debug!("parse_trait_methods(): parsing provided method");
-                    let (inner_attrs, body) =
-                        p.parse_inner_attrs_and_block()?;
-                    attrs.extend(inner_attrs.iter().cloned());
-                    Some(body)
-                  }
+                let body = match self.token {
+                    token::Semi => {
+                        self.bump();
+                        debug!("parse_trait_methods(): parsing required method");
+                        None
+                    }
+                    token::OpenDelim(token::Brace) => {
+                        debug!("parse_trait_methods(): parsing provided method");
+                        let (inner_attrs, body) =
+                            self.parse_inner_attrs_and_block()?;
+                        attrs.extend(inner_attrs.iter().cloned());
+                        Some(body)
+                    }
 
-                  _ => {
-                      let token_str = p.this_token_to_string();
-                      return Err(p.fatal(&format!("expected `;` or `{{`, found `{}`",
-                                       token_str)[..]))
-                  }
+                    _ => {
+                        let token_str = self.this_token_to_string();
+                        return Err(self.fatal(&format!("expected `;` or `{{`, found `{}`",
+                                                    token_str)[..]))
+                    }
                 };
                 (ident, ast::TraitItemKind::Method(sig, body))
             };
+        Ok(TraitItem {
+            id: ast::DUMMY_NODE_ID,
+            ident: name,
+            attrs: attrs,
+            node: node,
+            span: mk_sp(lo, self.last_span.hi),
+        })
+    }
 
-            Ok(TraitItem {
-                id: ast::DUMMY_NODE_ID,
-                ident: name,
-                attrs: attrs,
-                node: node,
-                span: mk_sp(lo, p.last_span.hi),
+
+    /// Parse the items in a trait declaration
+    pub fn parse_trait_items(&mut self) -> PResult<'a,  Vec<TraitItem>> {
+        self.parse_unspanned_seq(
+            &token::OpenDelim(token::Brace),
+            &token::CloseDelim(token::Brace),
+            SeqSep::none(),
+            |p| -> PResult<'a, TraitItem> {
+                p.parse_trait_item()
             })
-        })
     }
 
     /// Parse a possibly mutable type
@@ -1487,7 +1490,7 @@ impl<'a> Parser<'a> {
                                                 SeqSep::none(),
                                                 |p| p.parse_token_tree())?;
                 let hi = self.span.hi;
-                TyKind::Mac(spanned(lo, hi, Mac_ { path: path, tts: tts, ctxt: EMPTY_CTXT }))
+                TyKind::Mac(spanned(lo, hi, Mac_ { path: path, tts: tts }))
             } else {
                 // NAMED TYPE
                 TyKind::Path(None, path)
@@ -1696,12 +1699,12 @@ impl<'a> Parser<'a> {
         let lo = self.span.lo;
         let literal = P(self.parse_lit()?);
         let hi = self.last_span.hi;
-        let expr = self.mk_expr(lo, hi, ExprKind::Lit(literal), None);
+        let expr = self.mk_expr(lo, hi, ExprKind::Lit(literal), ThinVec::new());
 
         if minus_present {
             let minus_hi = self.last_span.hi;
             let unary = self.mk_unary(UnOp::Neg, expr);
-            Ok(self.mk_expr(minus_lo, minus_hi, unary, None))
+            Ok(self.mk_expr(minus_lo, minus_hi, unary, ThinVec::new()))
         } else {
             Ok(expr)
         }
@@ -2059,13 +2062,13 @@ impl<'a> Parser<'a> {
         })
     }
 
-    pub fn mk_expr(&mut self, lo: BytePos, hi: BytePos,
-                   node: ExprKind, attrs: ThinAttributes) -> P<Expr> {
+    pub fn mk_expr(&mut self, lo: BytePos, hi: BytePos, node: ExprKind, attrs: ThinVec<Attribute>)
+                   -> P<Expr> {
         P(Expr {
             id: ast::DUMMY_NODE_ID,
             node: node,
             span: mk_sp(lo, hi),
-            attrs: attrs,
+            attrs: attrs.into(),
         })
     }
 
@@ -2122,7 +2125,7 @@ impl<'a> Parser<'a> {
     }
 
     pub fn mk_mac_expr(&mut self, lo: BytePos, hi: BytePos,
-                       m: Mac_, attrs: ThinAttributes) -> P<Expr> {
+                       m: Mac_, attrs: ThinVec<Attribute>) -> P<Expr> {
         P(Expr {
             id: ast::DUMMY_NODE_ID,
             node: ExprKind::Mac(codemap::Spanned {node: m, span: mk_sp(lo, hi)}),
@@ -2131,7 +2134,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    pub fn mk_lit_u32(&mut self, i: u32, attrs: ThinAttributes) -> P<Expr> {
+    pub fn mk_lit_u32(&mut self, i: u32, attrs: ThinVec<Attribute>) -> P<Expr> {
         let span = &self.span;
         let lv_lit = P(codemap::Spanned {
             node: LitKind::Int(i as u64, ast::LitIntType::Unsigned(UintTy::U32)),
@@ -2172,7 +2175,7 @@ impl<'a> Parser<'a> {
         //
         // Therefore, prevent sub-parser from parsing
         // attributes by giving them a empty "already parsed" list.
-        let mut attrs = None;
+        let mut attrs = ThinVec::new();
 
         let lo = self.span.lo;
         let mut hi = self.span.hi;
@@ -2184,9 +2187,7 @@ impl<'a> Parser<'a> {
             token::OpenDelim(token::Paren) => {
                 self.bump();
 
-                let attrs = self.parse_inner_attributes()?
-                    .into_thin_attrs()
-                    .prepend(attrs);
+                attrs.extend(self.parse_inner_attributes()?);
 
                 // (e) is parenthesized e
                 // (e,) is a tuple with only one field, e
@@ -2224,9 +2225,7 @@ impl<'a> Parser<'a> {
             token::OpenDelim(token::Bracket) => {
                 self.bump();
 
-                let inner_attrs = self.parse_inner_attributes()?
-                    .into_thin_attrs();
-                attrs.update(|attrs| attrs.append(inner_attrs));
+                attrs.extend(self.parse_inner_attributes()?);
 
                 if self.check(&token::CloseDelim(token::Bracket)) {
                     // Empty vector.
@@ -2283,18 +2282,19 @@ impl<'a> Parser<'a> {
                     return self.parse_while_expr(None, lo, attrs);
                 }
                 if self.token.is_lifetime() {
-                    let lifetime = self.get_lifetime();
+                    let label = Spanned { node: self.get_lifetime(),
+                                          span: self.span };
                     let lo = self.span.lo;
                     self.bump();
                     self.expect(&token::Colon)?;
                     if self.eat_keyword(keywords::While) {
-                        return self.parse_while_expr(Some(lifetime), lo, attrs)
+                        return self.parse_while_expr(Some(label), lo, attrs)
                     }
                     if self.eat_keyword(keywords::For) {
-                        return self.parse_for_expr(Some(lifetime), lo, attrs)
+                        return self.parse_for_expr(Some(label), lo, attrs)
                     }
                     if self.eat_keyword(keywords::Loop) {
-                        return self.parse_loop_expr(Some(lifetime), lo, attrs)
+                        return self.parse_loop_expr(Some(label), lo, attrs)
                     }
                     return Err(self.fatal("expected `while`, `for`, or `loop` after a label"))
                 }
@@ -2304,14 +2304,14 @@ impl<'a> Parser<'a> {
                 }
                 if self.eat_keyword(keywords::Continue) {
                     let ex = if self.token.is_lifetime() {
-                        let ex = ExprKind::Again(Some(Spanned{
+                        let ex = ExprKind::Continue(Some(Spanned{
                             node: self.get_lifetime(),
                             span: self.span
                         }));
                         self.bump();
                         ex
                     } else {
-                        ExprKind::Again(None)
+                        ExprKind::Continue(None)
                     };
                     let hi = self.last_span.hi;
                     return Ok(self.mk_expr(lo, hi, ex, attrs));
@@ -2367,7 +2367,7 @@ impl<'a> Parser<'a> {
 
                         return Ok(self.mk_mac_expr(lo,
                                                    hi,
-                                                   Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT },
+                                                   Mac_ { path: pth, tts: tts },
                                                    attrs));
                     }
                     if self.check(&token::OpenDelim(token::Brace)) {
@@ -2382,9 +2382,7 @@ impl<'a> Parser<'a> {
                             let mut fields = Vec::new();
                             let mut base = None;
 
-                            let attrs = attrs.append(
-                                self.parse_inner_attributes()?
-                                    .into_thin_attrs());
+                            attrs.extend(self.parse_inner_attributes()?);
 
                             while self.token != token::CloseDelim(token::Brace) {
                                 if self.eat(&token::DotDot) {
@@ -2451,25 +2449,24 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_or_use_outer_attributes(&mut self,
-                                     already_parsed_attrs: Option<ThinAttributes>)
-                                     -> PResult<'a, ThinAttributes> {
+                                     already_parsed_attrs: Option<ThinVec<Attribute>>)
+                                     -> PResult<'a, ThinVec<Attribute>> {
         if let Some(attrs) = already_parsed_attrs {
             Ok(attrs)
         } else {
-            self.parse_outer_attributes().map(|a| a.into_thin_attrs())
+            self.parse_outer_attributes().map(|a| a.into())
         }
     }
 
     /// Parse a block or unsafe block
     pub fn parse_block_expr(&mut self, lo: BytePos, blk_mode: BlockCheckMode,
-                            attrs: ThinAttributes)
+                            outer_attrs: ThinVec<Attribute>)
                             -> PResult<'a, P<Expr>> {
 
-        let outer_attrs = attrs;
         self.expect(&token::OpenDelim(token::Brace))?;
 
-        let inner_attrs = self.parse_inner_attributes()?.into_thin_attrs();
-        let attrs = outer_attrs.append(inner_attrs);
+        let mut attrs = outer_attrs;
+        attrs.extend(self.parse_inner_attributes()?);
 
         let blk = self.parse_block_tail(lo, blk_mode)?;
         return Ok(self.mk_expr(blk.span.lo, blk.span.hi, ExprKind::Block(blk), attrs));
@@ -2477,7 +2474,7 @@ impl<'a> Parser<'a> {
 
     /// parse a.b or a(13) or a[4] or just a
     pub fn parse_dot_or_call_expr(&mut self,
-                                  already_parsed_attrs: Option<ThinAttributes>)
+                                  already_parsed_attrs: Option<ThinVec<Attribute>>)
                                   -> PResult<'a, P<Expr>> {
         let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?;
 
@@ -2489,7 +2486,7 @@ impl<'a> Parser<'a> {
     pub fn parse_dot_or_call_expr_with(&mut self,
                                        e0: P<Expr>,
                                        lo: BytePos,
-                                       attrs: ThinAttributes)
+                                       mut attrs: ThinVec<Attribute>)
                                        -> PResult<'a, P<Expr>> {
         // Stitch the list of outer attributes onto the return value.
         // A little bit ugly, but the best way given the current code
@@ -2497,12 +2494,13 @@ impl<'a> Parser<'a> {
         self.parse_dot_or_call_expr_with_(e0, lo)
         .map(|expr|
             expr.map(|mut expr| {
-                expr.attrs.update(|a| a.prepend(attrs));
+                attrs.extend::<Vec<_>>(expr.attrs.into());
+                expr.attrs = attrs;
                 match expr.node {
                     ExprKind::If(..) | ExprKind::IfLet(..) => {
-                        if !expr.attrs.as_attr_slice().is_empty() {
+                        if !expr.attrs.is_empty() {
                             // Just point to the first attribute in there...
-                            let span = expr.attrs.as_attr_slice()[0].span;
+                            let span = expr.attrs[0].span;
 
                             self.span_err(span,
                                 "attributes are not yet allowed on `if` \
@@ -2550,7 +2548,7 @@ impl<'a> Parser<'a> {
                 es.insert(0, self_value);
                 let id = spanned(ident_span.lo, ident_span.hi, ident);
                 let nd = self.mk_method_call(id, tys, es);
-                self.mk_expr(lo, hi, nd, None)
+                self.mk_expr(lo, hi, nd, ThinVec::new())
             }
             // Field access.
             _ => {
@@ -2563,7 +2561,7 @@ impl<'a> Parser<'a> {
 
                 let id = spanned(ident_span.lo, ident_span.hi, ident);
                 let field = self.mk_field(self_value, id);
-                self.mk_expr(lo, ident_span.hi, field, None)
+                self.mk_expr(lo, ident_span.hi, field, ThinVec::new())
             }
         })
     }
@@ -2575,7 +2573,7 @@ impl<'a> Parser<'a> {
             // expr?
             while self.eat(&token::Question) {
                 let hi = self.last_span.hi;
-                e = self.mk_expr(lo, hi, ExprKind::Try(e), None);
+                e = self.mk_expr(lo, hi, ExprKind::Try(e), ThinVec::new());
             }
 
             // expr.f
@@ -2603,7 +2601,7 @@ impl<'a> Parser<'a> {
                         Some(n) => {
                             let id = spanned(dot, hi, n);
                             let field = self.mk_tup_field(e, id);
-                            e = self.mk_expr(lo, hi, field, None);
+                            e = self.mk_expr(lo, hi, field, ThinVec::new());
                         }
                         None => {
                             let last_span = self.last_span;
@@ -2655,7 +2653,7 @@ impl<'a> Parser<'a> {
                 hi = self.last_span.hi;
 
                 let nd = self.mk_call(e, es);
-                e = self.mk_expr(lo, hi, nd, None);
+                e = self.mk_expr(lo, hi, nd, ThinVec::new());
               }
 
               // expr[...]
@@ -2666,7 +2664,7 @@ impl<'a> Parser<'a> {
                 hi = self.span.hi;
                 self.commit_expr_expecting(&ix, token::CloseDelim(token::Bracket))?;
                 let index = self.mk_index(e, ix);
-                e = self.mk_expr(lo, hi, index, None)
+                e = self.mk_expr(lo, hi, index, ThinVec::new())
               }
               _ => return Ok(e)
             }
@@ -2690,19 +2688,21 @@ impl<'a> Parser<'a> {
                     )?;
                     let (sep, repeat) = self.parse_sep_and_kleene_op()?;
                     let name_num = macro_parser::count_names(&seq);
-                    return Ok(TokenTree::Sequence(mk_sp(sp.lo, seq_span.hi),
-                                      Rc::new(SequenceRepetition {
-                                          tts: seq,
-                                          separator: sep,
-                                          op: repeat,
-                                          num_captures: name_num
-                                      })));
+                    return Ok(TokenTree::Sequence(mk_sp(sp.lo, seq_span.hi), SequenceRepetition {
+                        tts: seq,
+                        separator: sep,
+                        op: repeat,
+                        num_captures: name_num
+                    }));
                 } else if self.token.is_keyword(keywords::Crate) {
                     self.bump();
                     return Ok(TokenTree::Token(sp, SpecialVarNt(SpecialMacroVar::CrateMacroVar)));
                 } else {
                     sp = mk_sp(sp.lo, self.span.hi);
-                    self.parse_ident()?
+                    self.parse_ident().unwrap_or_else(|mut e| {
+                        e.emit();
+                        keywords::Invalid.ident()
+                    })
                 }
             }
             token::SubstNt(name) => {
@@ -2736,24 +2736,24 @@ impl<'a> Parser<'a> {
     /// Parse an optional separator followed by a Kleene-style
     /// repetition token (+ or *).
     pub fn parse_sep_and_kleene_op(&mut self)
-                                   -> PResult<'a, (Option<token::Token>, ast::KleeneOp)> {
-        fn parse_kleene_op<'a>(parser: &mut Parser<'a>) -> PResult<'a,  Option<ast::KleeneOp>> {
+                                   -> PResult<'a, (Option<token::Token>, tokenstream::KleeneOp)> {
+        fn parse_kleene_op<'a>(parser: &mut Parser<'a>) ->
+          PResult<'a,  Option<tokenstream::KleeneOp>> {
             match parser.token {
                 token::BinOp(token::Star) => {
                     parser.bump();
-                    Ok(Some(ast::KleeneOp::ZeroOrMore))
+                    Ok(Some(tokenstream::KleeneOp::ZeroOrMore))
                 },
                 token::BinOp(token::Plus) => {
                     parser.bump();
-                    Ok(Some(ast::KleeneOp::OneOrMore))
+                    Ok(Some(tokenstream::KleeneOp::OneOrMore))
                 },
                 _ => Ok(None)
             }
         };
 
-        match parse_kleene_op(self)? {
-            Some(kleene_op) => return Ok((None, kleene_op)),
-            None => {}
+        if let Some(kleene_op) = parse_kleene_op(self)? {
+            return Ok((None, kleene_op));
         }
 
         let separator = self.bump_and_get();
@@ -2807,14 +2807,14 @@ impl<'a> Parser<'a> {
                 let span = Span { hi: close_span.hi, ..pre_span };
 
                 match self.token {
-                    // Correct delmiter.
+                    // Correct delimiter.
                     token::CloseDelim(d) if d == delim => {
                         self.open_braces.pop().unwrap();
 
                         // Parse the close delimiter.
                         self.bump();
                     }
-                    // Incorect delimiter.
+                    // Incorrect delimiter.
                     token::CloseDelim(other) => {
                         let token_str = self.this_token_to_string();
                         let mut err = self.diagnostic().struct_span_err(self.span,
@@ -2829,9 +2829,9 @@ impl<'a> Parser<'a> {
 
                         self.open_braces.pop().unwrap();
 
-                        // If the incorrect delimter matches an earlier opening
+                        // If the incorrect delimiter matches an earlier opening
                         // delimiter, then don't consume it (it can be used to
-                        // close the earlier one)Otherwise, consume it.
+                        // close the earlier one)Otherwise, consume it.
                         // E.g., we try to recover from:
                         // fn foo() {
                         //     bar(baz(
@@ -2845,21 +2845,21 @@ impl<'a> Parser<'a> {
                         // and an error emitted then. Thus we don't pop from
                         // self.open_braces here.
                     },
-                    _ => unreachable!(),
+                    _ => {}
                 }
 
-                Ok(TokenTree::Delimited(span, Rc::new(Delimited {
+                Ok(TokenTree::Delimited(span, Delimited {
                     delim: delim,
                     open_span: open_span,
                     tts: tts,
                     close_span: close_span,
-                })))
+                }))
             },
             _ => {
                 // invariants: the current token is not a left-delimiter,
                 // not an EOF, and not the desired right-delimiter (if
                 // it were, parse_seq_to_before_end would have prevented
-                // reaching this point.
+                // reaching this point).
                 maybe_whole!(deref self, NtTT);
                 match self.token {
                     token::CloseDelim(_) => {
@@ -2894,7 +2894,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a prefix-unary-operator expr
     pub fn parse_prefix_expr(&mut self,
-                             already_parsed_attrs: Option<ThinAttributes>)
+                             already_parsed_attrs: Option<ThinVec<Attribute>>)
                              -> PResult<'a, P<Expr>> {
         let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?;
         let lo = self.span.lo;
@@ -2939,8 +2939,7 @@ impl<'a> Parser<'a> {
                 let blk = self.parse_block()?;
                 let span = blk.span;
                 hi = span.hi;
-                let blk_expr = self.mk_expr(span.lo, span.hi, ExprKind::Block(blk),
-                                            None);
+                let blk_expr = self.mk_expr(span.lo, hi, ExprKind::Block(blk), ThinVec::new());
                 ExprKind::InPlace(place, blk_expr)
             }
             token::Ident(..) if self.token.is_keyword(keywords::Box) => {
@@ -2960,7 +2959,7 @@ impl<'a> Parser<'a> {
     /// This parses an expression accounting for associativity and precedence of the operators in
     /// the expression.
     pub fn parse_assoc_expr(&mut self,
-                            already_parsed_attrs: Option<ThinAttributes>)
+                            already_parsed_attrs: Option<ThinVec<Attribute>>)
                             -> PResult<'a, P<Expr>> {
         self.parse_assoc_expr_with(0, already_parsed_attrs.into())
     }
@@ -3013,13 +3012,13 @@ impl<'a> Parser<'a> {
             // Special cases:
             if op == AssocOp::As {
                 let rhs = self.parse_ty()?;
-                lhs = self.mk_expr(lhs_span.lo, rhs.span.hi,
-                                   ExprKind::Cast(lhs, rhs), None);
+                let (lo, hi) = (lhs_span.lo, rhs.span.hi);
+                lhs = self.mk_expr(lo, hi, ExprKind::Cast(lhs, rhs), ThinVec::new());
                 continue
             } else if op == AssocOp::Colon {
                 let rhs = self.parse_ty()?;
-                lhs = self.mk_expr(lhs_span.lo, rhs.span.hi,
-                                   ExprKind::Type(lhs, rhs), None);
+                let (lo, hi) = (lhs_span.lo, rhs.span.hi);
+                lhs = self.mk_expr(lo, hi, ExprKind::Type(lhs, rhs), ThinVec::new());
                 continue
             } else if op == AssocOp::DotDot || op == AssocOp::DotDotDot {
                 // If we didn’t have to handle `x..`/`x...`, it would be pretty easy to
@@ -3045,7 +3044,7 @@ impl<'a> Parser<'a> {
                 };
 
                 let r = try!(self.mk_range(Some(lhs), rhs, limits));
-                lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None);
+                lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, ThinVec::new());
                 break
             }
 
@@ -3072,6 +3071,7 @@ impl<'a> Parser<'a> {
                 }),
             }?;
 
+            let (lo, hi) = (lhs_span.lo, rhs.span.hi);
             lhs = match op {
                 AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide |
                 AssocOp::Modulus | AssocOp::LAnd | AssocOp::LOr | AssocOp::BitXor |
@@ -3079,14 +3079,13 @@ impl<'a> Parser<'a> {
                 AssocOp::Equal | AssocOp::Less | AssocOp::LessEqual | AssocOp::NotEqual |
                 AssocOp::Greater | AssocOp::GreaterEqual => {
                     let ast_op = op.to_ast_binop().unwrap();
-                    let (lhs_span, rhs_span) = (lhs_span, rhs.span);
                     let binary = self.mk_binary(codemap::respan(cur_op_span, ast_op), lhs, rhs);
-                    self.mk_expr(lhs_span.lo, rhs_span.hi, binary, None)
+                    self.mk_expr(lo, hi, binary, ThinVec::new())
                 }
                 AssocOp::Assign =>
-                    self.mk_expr(lhs_span.lo, rhs.span.hi, ExprKind::Assign(lhs, rhs), None),
+                    self.mk_expr(lo, hi, ExprKind::Assign(lhs, rhs), ThinVec::new()),
                 AssocOp::Inplace =>
-                    self.mk_expr(lhs_span.lo, rhs.span.hi, ExprKind::InPlace(lhs, rhs), None),
+                    self.mk_expr(lo, hi, ExprKind::InPlace(lhs, rhs), ThinVec::new()),
                 AssocOp::AssignOp(k) => {
                     let aop = match k {
                         token::Plus =>    BinOpKind::Add,
@@ -3100,9 +3099,8 @@ impl<'a> Parser<'a> {
                         token::Shl =>     BinOpKind::Shl,
                         token::Shr =>     BinOpKind::Shr,
                     };
-                    let (lhs_span, rhs_span) = (lhs_span, rhs.span);
                     let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
-                    self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None)
+                    self.mk_expr(lo, hi, aopexpr, ThinVec::new())
                 }
                 AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotDot => {
                     self.bug("As, Colon, DotDot or DotDotDot branch reached")
@@ -3137,7 +3135,7 @@ impl<'a> Parser<'a> {
 
     /// Parse prefix-forms of range notation: `..expr`, `..`, `...expr`
     fn parse_prefix_range_expr(&mut self,
-                               already_parsed_attrs: Option<ThinAttributes>)
+                               already_parsed_attrs: Option<ThinVec<Attribute>>)
                                -> PResult<'a, P<Expr>> {
         debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot);
         let tok = self.token.clone();
@@ -3182,7 +3180,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an 'if' or 'if let' expression ('if' token already eaten)
-    pub fn parse_if_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
+    pub fn parse_if_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         if self.check_keyword(keywords::Let) {
             return self.parse_if_let_expr(attrs);
         }
@@ -3200,7 +3198,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an 'if let' expression ('if' token already eaten)
-    pub fn parse_if_let_expr(&mut self, attrs: ThinAttributes)
+    pub fn parse_if_let_expr(&mut self, attrs: ThinVec<Attribute>)
                              -> PResult<'a, P<Expr>> {
         let lo = self.last_span.lo;
         self.expect_keyword(keywords::Let)?;
@@ -3221,7 +3219,7 @@ impl<'a> Parser<'a> {
     pub fn parse_lambda_expr(&mut self,
                              lo: BytePos,
                              capture_clause: CaptureBy,
-                             attrs: ThinAttributes)
+                             attrs: ThinVec<Attribute>)
                              -> PResult<'a, P<Expr>>
     {
         let decl = self.parse_fn_block_decl()?;
@@ -3233,9 +3231,12 @@ impl<'a> Parser<'a> {
                 let body_expr = self.parse_expr()?;
                 P(ast::Block {
                     id: ast::DUMMY_NODE_ID,
-                    stmts: vec![],
                     span: body_expr.span,
-                    expr: Some(body_expr),
+                    stmts: vec![Stmt {
+                        span: body_expr.span,
+                        node: StmtKind::Expr(body_expr),
+                        id: ast::DUMMY_NODE_ID,
+                    }],
                     rules: BlockCheckMode::Default,
                 })
             }
@@ -3256,24 +3257,24 @@ impl<'a> Parser<'a> {
     // `else` token already eaten
     pub fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
         if self.eat_keyword(keywords::If) {
-            return self.parse_if_expr(None);
+            return self.parse_if_expr(ThinVec::new());
         } else {
             let blk = self.parse_block()?;
-            return Ok(self.mk_expr(blk.span.lo, blk.span.hi, ExprKind::Block(blk), None));
+            return Ok(self.mk_expr(blk.span.lo, blk.span.hi, ExprKind::Block(blk), ThinVec::new()));
         }
     }
 
     /// Parse a 'for' .. 'in' expression ('for' token already eaten)
-    pub fn parse_for_expr(&mut self, opt_ident: Option<ast::Ident>,
+    pub fn parse_for_expr(&mut self, opt_ident: Option<ast::SpannedIdent>,
                           span_lo: BytePos,
-                          attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
+                          mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         // Parse: `for <src_pat> in <src_expr> <src_loop_block>`
 
         let pat = self.parse_pat()?;
         self.expect_keyword(keywords::In)?;
         let expr = self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None)?;
         let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
-        let attrs = attrs.append(iattrs.into_thin_attrs());
+        attrs.extend(iattrs);
 
         let hi = self.last_span.hi;
 
@@ -3283,46 +3284,46 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a 'while' or 'while let' expression ('while' token already eaten)
-    pub fn parse_while_expr(&mut self, opt_ident: Option<ast::Ident>,
+    pub fn parse_while_expr(&mut self, opt_ident: Option<ast::SpannedIdent>,
                             span_lo: BytePos,
-                            attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
+                            mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         if self.token.is_keyword(keywords::Let) {
             return self.parse_while_let_expr(opt_ident, span_lo, attrs);
         }
         let cond = self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None)?;
         let (iattrs, body) = self.parse_inner_attrs_and_block()?;
-        let attrs = attrs.append(iattrs.into_thin_attrs());
+        attrs.extend(iattrs);
         let hi = body.span.hi;
         return Ok(self.mk_expr(span_lo, hi, ExprKind::While(cond, body, opt_ident),
                                attrs));
     }
 
     /// Parse a 'while let' expression ('while' token already eaten)
-    pub fn parse_while_let_expr(&mut self, opt_ident: Option<ast::Ident>,
+    pub fn parse_while_let_expr(&mut self, opt_ident: Option<ast::SpannedIdent>,
                                 span_lo: BytePos,
-                                attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
+                                mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         self.expect_keyword(keywords::Let)?;
         let pat = self.parse_pat()?;
         self.expect(&token::Eq)?;
         let expr = self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None)?;
         let (iattrs, body) = self.parse_inner_attrs_and_block()?;
-        let attrs = attrs.append(iattrs.into_thin_attrs());
+        attrs.extend(iattrs);
         let hi = body.span.hi;
         return Ok(self.mk_expr(span_lo, hi, ExprKind::WhileLet(pat, expr, body, opt_ident), attrs));
     }
 
     // parse `loop {...}`, `loop` token already eaten
-    pub fn parse_loop_expr(&mut self, opt_ident: Option<ast::Ident>,
+    pub fn parse_loop_expr(&mut self, opt_ident: Option<ast::SpannedIdent>,
                            span_lo: BytePos,
-                           attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
+                           mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         let (iattrs, body) = self.parse_inner_attrs_and_block()?;
-        let attrs = attrs.append(iattrs.into_thin_attrs());
+        attrs.extend(iattrs);
         let hi = body.span.hi;
         Ok(self.mk_expr(span_lo, hi, ExprKind::Loop(body, opt_ident), attrs))
     }
 
     // `match` token already eaten
-    fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
+    fn parse_match_expr(&mut self, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         let match_span = self.last_span;
         let lo = self.last_span.lo;
         let discriminant = self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL,
@@ -3334,8 +3335,8 @@ impl<'a> Parser<'a> {
             }
             return Err(e)
         }
-        let attrs = attrs.append(
-            self.parse_inner_attributes()?.into_thin_attrs());
+        attrs.extend(self.parse_inner_attributes()?);
+
         let mut arms: Vec<Arm> = Vec::new();
         while self.token != token::CloseDelim(token::Brace) {
             match self.parse_arm() {
@@ -3408,7 +3409,7 @@ impl<'a> Parser<'a> {
 
     /// Parse an expression, subject to the given restrictions
     pub fn parse_expr_res(&mut self, r: Restrictions,
-                          already_parsed_attrs: Option<ThinAttributes>)
+                          already_parsed_attrs: Option<ThinVec<Attribute>>)
                           -> PResult<'a, P<Expr>> {
         self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs))
     }
@@ -3433,21 +3434,33 @@ impl<'a> Parser<'a> {
         };
     }
 
-    fn parse_pat_tuple_elements(&mut self) -> PResult<'a, Vec<P<Pat>>> {
+    fn parse_pat_tuple_elements(&mut self, unary_needs_comma: bool)
+                                -> PResult<'a, (Vec<P<Pat>>, Option<usize>)> {
         let mut fields = vec![];
-        if !self.check(&token::CloseDelim(token::Paren)) {
-            fields.push(self.parse_pat()?);
-            if self.look_ahead(1, |t| *t != token::CloseDelim(token::Paren)) {
-                while self.eat(&token::Comma) &&
-                      !self.check(&token::CloseDelim(token::Paren)) {
+        let mut ddpos = None;
+
+        while !self.check(&token::CloseDelim(token::Paren)) {
+            if ddpos.is_none() && self.eat(&token::DotDot) {
+                ddpos = Some(fields.len());
+                if self.eat(&token::Comma) {
+                    // `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
                     fields.push(self.parse_pat()?);
                 }
+            } else if ddpos.is_some() && self.eat(&token::DotDot) {
+                // Emit a friendly error, ignore `..` and continue parsing
+                self.span_err(self.last_span, "`..` can only be used once per \
+                                               tuple or tuple struct pattern");
+            } else {
+                fields.push(self.parse_pat()?);
             }
-            if fields.len() == 1 {
+
+            if !self.check(&token::CloseDelim(token::Paren)) ||
+                    (unary_needs_comma && fields.len() == 1 && ddpos.is_none()) {
                 self.expect(&token::Comma)?;
             }
         }
-        Ok(fields)
+
+        Ok((fields, ddpos))
     }
 
     fn parse_pat_vec_elements(
@@ -3504,7 +3517,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the fields of a struct-like pattern
-    fn parse_pat_fields(&mut self) -> PResult<'a, (Vec<codemap::Spanned<ast::FieldPat>> , bool)> {
+    fn parse_pat_fields(&mut self) -> PResult<'a, (Vec<codemap::Spanned<ast::FieldPat>>, bool)> {
         let mut fields = Vec::new();
         let mut etc = false;
         let mut first = true;
@@ -3574,9 +3587,9 @@ impl<'a> Parser<'a> {
             };
 
             fields.push(codemap::Spanned { span: mk_sp(lo, hi),
-                                           node: ast::FieldPat { ident: fieldname,
-                                                                 pat: subpat,
-                                                                 is_shorthand: is_shorthand }});
+                                              node: ast::FieldPat { ident: fieldname,
+                                                                    pat: subpat,
+                                                                    is_shorthand: is_shorthand }});
         }
         return Ok((fields, etc));
     }
@@ -3594,7 +3607,7 @@ impl<'a> Parser<'a> {
                 (None, self.parse_path(PathStyle::Expr)?)
             };
             let hi = self.last_span.hi;
-            Ok(self.mk_expr(lo, hi, ExprKind::Path(qself, path), None))
+            Ok(self.mk_expr(lo, hi, ExprKind::Path(qself, path), ThinVec::new()))
         } else {
             self.parse_pat_literal_maybe_minus()
         }
@@ -3626,9 +3639,9 @@ impl<'a> Parser<'a> {
           token::OpenDelim(token::Paren) => {
             // Parse (pat,pat,pat,...) as tuple pattern
             self.bump();
-            let fields = self.parse_pat_tuple_elements()?;
+            let (fields, ddpos) = self.parse_pat_tuple_elements(true)?;
             self.expect(&token::CloseDelim(token::Paren))?;
-            pat = PatKind::Tup(fields);
+            pat = PatKind::Tuple(fields, ddpos);
           }
           token::OpenDelim(token::Bracket) => {
             // Parse [pat,pat,...] as slice pattern
@@ -3665,9 +3678,9 @@ impl<'a> Parser<'a> {
                         let tts = self.parse_seq_to_end(
                             &token::CloseDelim(delim),
                             SeqSep::none(), |p| p.parse_token_tree())?;
-                        let mac = Mac_ { path: path, tts: tts, ctxt: EMPTY_CTXT };
+                        let mac = Mac_ { path: path, tts: tts };
                         pat = PatKind::Mac(codemap::Spanned {node: mac,
-                                                       span: mk_sp(lo, self.last_span.hi)});
+                                                               span: mk_sp(lo, self.last_span.hi)});
                     } else {
                         // Parse ident @ pat
                         // This can give false positives and parse nullary enums,
@@ -3689,7 +3702,8 @@ impl<'a> Parser<'a> {
                       token::DotDotDot => {
                         // Parse range
                         let hi = self.last_span.hi;
-                        let begin = self.mk_expr(lo, hi, ExprKind::Path(qself, path), None);
+                        let begin =
+                              self.mk_expr(lo, hi, ExprKind::Path(qself, path), ThinVec::new());
                         self.bump();
                         let end = self.parse_pat_range_end()?;
                         pat = PatKind::Range(begin, end);
@@ -3713,28 +3727,13 @@ impl<'a> Parser<'a> {
                             return Err(self.fatal("unexpected `(` after qualified path"));
                         }
                         // Parse tuple struct or enum pattern
-                        if self.look_ahead(1, |t| *t == token::DotDot) {
-                            // This is a "top constructor only" pat
-                            self.bump();
-                            self.bump();
-                            self.expect(&token::CloseDelim(token::Paren))?;
-                            pat = PatKind::TupleStruct(path, None);
-                        } else {
-                            let args = self.parse_enum_variant_seq(
-                                &token::OpenDelim(token::Paren),
-                                &token::CloseDelim(token::Paren),
-                                SeqSep::trailing_allowed(token::Comma),
-                                |p| p.parse_pat())?;
-                            pat = PatKind::TupleStruct(path, Some(args));
-                        }
+                        self.bump();
+                        let (fields, ddpos) = self.parse_pat_tuple_elements(false)?;
+                        self.expect(&token::CloseDelim(token::Paren))?;
+                        pat = PatKind::TupleStruct(path, fields, ddpos)
                       }
                       _ => {
-                        pat = match qself {
-                            // Parse qualified path
-                            Some(qself) => PatKind::QPath(qself, path),
-                            // Parse nullary enum
-                            None => PatKind::Path(path)
-                        };
+                        pat = PatKind::Path(qself, path);
                       }
                     }
                 }
@@ -3799,7 +3798,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a local variable declaration
-    fn parse_local(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Local>> {
+    fn parse_local(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Local>> {
         let lo = self.span.lo;
         let pat = self.parse_pat()?;
 
@@ -3818,13 +3817,6 @@ impl<'a> Parser<'a> {
         }))
     }
 
-    /// Parse a "let" stmt
-    fn parse_let(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Decl>> {
-        let lo = self.span.lo;
-        let local = self.parse_local(attrs)?;
-        Ok(P(spanned(lo, self.last_span.hi, DeclKind::Local(local))))
-    }
-
     /// Parse a structure field
     fn parse_name_and_ty(&mut self, pr: Visibility,
                          attrs: Vec<Attribute> ) -> PResult<'a, StructField> {
@@ -3937,12 +3929,12 @@ impl<'a> Parser<'a> {
         let attrs = self.parse_outer_attributes()?;
         let lo = self.span.lo;
 
-        Ok(Some(if self.check_keyword(keywords::Let) {
-            self.expect_keyword(keywords::Let)?;
-            let decl = self.parse_let(attrs.into_thin_attrs())?;
-            let hi = decl.span.hi;
-            let stmt = StmtKind::Decl(decl, ast::DUMMY_NODE_ID);
-            spanned(lo, hi, stmt)
+        Ok(Some(if self.eat_keyword(keywords::Let) {
+            Stmt {
+                id: ast::DUMMY_NODE_ID,
+                node: StmtKind::Local(self.parse_local(attrs.into())?),
+                span: mk_sp(lo, self.last_span.hi),
+            }
         } else if self.token.is_ident()
             && !self.token.is_any_keyword()
             && self.look_ahead(1, |t| *t == token::Not) {
@@ -3993,9 +3985,12 @@ impl<'a> Parser<'a> {
             };
 
             if id.name == keywords::Invalid.name() {
-                let mac = P(spanned(lo, hi, Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT }));
-                let stmt = StmtKind::Mac(mac, style, attrs.into_thin_attrs());
-                spanned(lo, hi, stmt)
+                let mac = spanned(lo, hi, Mac_ { path: pth, tts: tts });
+                Stmt {
+                    id: ast::DUMMY_NODE_ID,
+                    node: StmtKind::Mac(P((mac, style, attrs.into()))),
+                    span: mk_sp(lo, hi),
+                }
             } else {
                 // if it has a special ident, it's definitely an item
                 //
@@ -4009,25 +4004,28 @@ impl<'a> Parser<'a> {
                                        followed by a semicolon");
                     }
                 }
-                spanned(lo, hi, StmtKind::Decl(
-                    P(spanned(lo, hi, DeclKind::Item(
+                Stmt {
+                    id: ast::DUMMY_NODE_ID,
+                    span: mk_sp(lo, hi),
+                    node: StmtKind::Item({
                         self.mk_item(
                             lo, hi, id /*id is good here*/,
-                            ItemKind::Mac(spanned(lo, hi,
-                                            Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })),
-                            Visibility::Inherited, attrs)))),
-                    ast::DUMMY_NODE_ID))
+                            ItemKind::Mac(spanned(lo, hi, Mac_ { path: pth, tts: tts })),
+                            Visibility::Inherited,
+                            attrs)
+                    }),
+                }
             }
         } else {
             // FIXME: Bad copy of attrs
             let restrictions = self.restrictions | Restrictions::NO_NONINLINE_MOD;
             match self.with_res(restrictions,
                                 |this| this.parse_item_(attrs.clone(), false, true))? {
-                Some(i) => {
-                    let hi = i.span.hi;
-                    let decl = P(spanned(lo, hi, DeclKind::Item(i)));
-                    spanned(lo, hi, StmtKind::Decl(decl, ast::DUMMY_NODE_ID))
-                }
+                Some(i) => Stmt {
+                    id: ast::DUMMY_NODE_ID,
+                    span: mk_sp(lo, i.span.hi),
+                    node: StmtKind::Item(i),
+                },
                 None => {
                     let unused_attrs = |attrs: &[_], s: &mut Self| {
                         if attrs.len() > 0 {
@@ -4050,10 +4048,12 @@ impl<'a> Parser<'a> {
 
                     // Remainder are line-expr stmts.
                     let e = self.parse_expr_res(
-                        Restrictions::RESTRICTION_STMT_EXPR, Some(attrs.into_thin_attrs()))?;
-                    let hi = e.span.hi;
-                    let stmt = StmtKind::Expr(e, ast::DUMMY_NODE_ID);
-                    spanned(lo, hi, stmt)
+                        Restrictions::RESTRICTION_STMT_EXPR, Some(attrs.into()))?;
+                    Stmt {
+                        id: ast::DUMMY_NODE_ID,
+                        span: mk_sp(lo, e.span.hi),
+                        node: StmtKind::Expr(e),
+                    }
                 }
             }
         }))
@@ -4096,10 +4096,9 @@ impl<'a> Parser<'a> {
     /// Precondition: already parsed the '{'.
     fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P<Block>> {
         let mut stmts = vec![];
-        let mut expr = None;
 
         while !self.eat(&token::CloseDelim(token::Brace)) {
-            let Spanned {node, span} = if let Some(s) = self.parse_stmt_() {
+            let Stmt {node, span, ..} = if let Some(s) = self.parse_stmt_() {
                 s
             } else if self.token == token::Eof {
                 break;
@@ -4107,69 +4106,23 @@ impl<'a> Parser<'a> {
                 // Found only `;` or `}`.
                 continue;
             };
+
             match node {
-                StmtKind::Expr(e, _) => {
-                    self.handle_expression_like_statement(e, span, &mut stmts, &mut expr)?;
-                }
-                StmtKind::Mac(mac, MacStmtStyle::NoBraces, attrs) => {
-                    // statement macro without braces; might be an
-                    // expr depending on whether a semicolon follows
-                    match self.token {
-                        token::Semi => {
-                            stmts.push(Spanned {
-                                node: StmtKind::Mac(mac, MacStmtStyle::Semicolon, attrs),
-                                span: mk_sp(span.lo, self.span.hi),
-                            });
-                            self.bump();
-                        }
-                        _ => {
-                            let e = self.mk_mac_expr(span.lo, span.hi,
-                                                     mac.and_then(|m| m.node),
-                                                     None);
-                            let lo = e.span.lo;
-                            let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
-                            let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
-                            self.handle_expression_like_statement(
-                                e,
-                                span,
-                                &mut stmts,
-                                &mut expr)?;
-                        }
-                    }
+                StmtKind::Expr(e) => {
+                    self.handle_expression_like_statement(e, span, &mut stmts)?;
                 }
-                StmtKind::Mac(m, style, attrs) => {
-                    // statement macro; might be an expr
-                    match self.token {
-                        token::Semi => {
-                            stmts.push(Spanned {
-                                node: StmtKind::Mac(m, MacStmtStyle::Semicolon, attrs),
-                                span: mk_sp(span.lo, self.span.hi),
-                            });
-                            self.bump();
-                        }
-                        token::CloseDelim(token::Brace) => {
-                            // if a block ends in `m!(arg)` without
-                            // a `;`, it must be an expr
-                            expr = Some(self.mk_mac_expr(span.lo, span.hi,
-                                                         m.and_then(|x| x.node),
-                                                         attrs));
-                        }
-                        _ => {
-                            stmts.push(Spanned {
-                                node: StmtKind::Mac(m, style, attrs),
-                                span: span
-                            });
-                        }
-                    }
+                StmtKind::Mac(mac) => {
+                    self.handle_macro_in_block(mac.unwrap(), span, &mut stmts)?;
                 }
                 _ => { // all other kinds of statements:
                     let mut hi = span.hi;
                     if classify::stmt_ends_with_semi(&node) {
-                        self.commit_stmt_expecting(token::Semi)?;
+                        self.commit_stmt(&[token::Semi], &[])?;
                         hi = self.last_span.hi;
                     }
 
-                    stmts.push(Spanned {
+                    stmts.push(Stmt {
+                        id: ast::DUMMY_NODE_ID,
                         node: node,
                         span: mk_sp(span.lo, hi)
                     });
@@ -4179,18 +4132,64 @@ impl<'a> Parser<'a> {
 
         Ok(P(ast::Block {
             stmts: stmts,
-            expr: expr,
             id: ast::DUMMY_NODE_ID,
             rules: s,
             span: mk_sp(lo, self.last_span.hi),
         }))
     }
 
+    fn handle_macro_in_block(&mut self,
+                             (mac, style, attrs): (ast::Mac, MacStmtStyle, ThinVec<Attribute>),
+                             span: Span,
+                             stmts: &mut Vec<Stmt>)
+                             -> PResult<'a, ()> {
+        if style == MacStmtStyle::NoBraces {
+            // statement macro without braces; might be an
+            // expr depending on whether a semicolon follows
+            match self.token {
+                token::Semi => {
+                    stmts.push(Stmt {
+                        id: ast::DUMMY_NODE_ID,
+                        node: StmtKind::Mac(P((mac, MacStmtStyle::Semicolon, attrs))),
+                        span: mk_sp(span.lo, self.span.hi),
+                    });
+                    self.bump();
+                }
+                _ => {
+                    let e = self.mk_mac_expr(span.lo, span.hi, mac.node, ThinVec::new());
+                    let lo = e.span.lo;
+                    let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
+                    let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
+                    self.handle_expression_like_statement(e, span, stmts)?;
+                }
+            }
+        } else {
+            // statement macro; might be an expr
+            match self.token {
+                token::Semi => {
+                    stmts.push(Stmt {
+                        id: ast::DUMMY_NODE_ID,
+                        node: StmtKind::Mac(P((mac, MacStmtStyle::Semicolon, attrs))),
+                        span: mk_sp(span.lo, self.span.hi),
+                    });
+                    self.bump();
+                }
+                _ => {
+                    stmts.push(Stmt {
+                        id: ast::DUMMY_NODE_ID,
+                        node: StmtKind::Mac(P((mac, style, attrs))),
+                        span: span
+                    });
+                }
+            }
+        }
+        Ok(())
+    }
+
     fn handle_expression_like_statement(&mut self,
                                         e: P<Expr>,
                                         span: Span,
-                                        stmts: &mut Vec<Stmt>,
-                                        last_block_expr: &mut Option<P<Expr>>)
+                                        stmts: &mut Vec<Stmt>)
                                         -> PResult<'a, ()> {
         // expression without semicolon
         if classify::expr_requires_semi_to_be_stmt(&e) {
@@ -4211,15 +4210,16 @@ impl<'a> Parser<'a> {
                     hi: self.last_span.hi,
                     expn_id: span.expn_id,
                 };
-                stmts.push(Spanned {
-                    node: StmtKind::Semi(e, ast::DUMMY_NODE_ID),
+                stmts.push(Stmt {
+                    id: ast::DUMMY_NODE_ID,
+                    node: StmtKind::Semi(e),
                     span: span_with_semi,
                 });
             }
-            token::CloseDelim(token::Brace) => *last_block_expr = Some(e),
             _ => {
-                stmts.push(Spanned {
-                    node: StmtKind::Expr(e, ast::DUMMY_NODE_ID),
+                stmts.push(Stmt {
+                    id: ast::DUMMY_NODE_ID,
+                    node: StmtKind::Expr(e),
                     span: span
                 });
             }
@@ -4616,25 +4616,19 @@ impl<'a> Parser<'a> {
         }))
     }
 
-    /// Parse the parameter list and result type of a function that may have a `self` parameter.
-    fn parse_fn_decl_with_self<F>(&mut self,
-                                  parse_arg_fn: F)
-                                  -> PResult<'a, (ExplicitSelf, P<FnDecl>)>
-        where F: FnMut(&mut Parser<'a>) -> PResult<'a,  Arg>,
-    {
+    /// Returns the parsed optional self argument and whether a self shortcut was used.
+    fn parse_self_arg(&mut self) -> PResult<'a, Option<Arg>> {
         let expect_ident = |this: &mut Self| match this.token {
-            token::Ident(ident) => { this.bump(); ident } // Preserve hygienic context.
+            // Preserve hygienic context.
+            token::Ident(ident) => { this.bump(); codemap::respan(this.last_span, ident) }
             _ => unreachable!()
         };
 
-        self.expect(&token::OpenDelim(token::Paren))?;
-
         // Parse optional self parameter of a method.
         // Only a limited set of initial token sequences is considered self parameters, anything
         // else is parsed as a normal function parameter list, so some lookahead is required.
         let eself_lo = self.span.lo;
-        let mut eself_mutbl = Mutability::Immutable;
-        let (eself, eself_ident_sp) = match self.token {
+        let (eself, eself_ident) = match self.token {
             token::BinOp(token::And) => {
                 // &self
                 // &mut self
@@ -4643,30 +4637,26 @@ impl<'a> Parser<'a> {
                 // &not_self
                 if self.look_ahead(1, |t| t.is_keyword(keywords::SelfValue)) {
                     self.bump();
-                    (SelfKind::Region(None, Mutability::Immutable, expect_ident(self)),
-                        self.last_span)
+                    (SelfKind::Region(None, Mutability::Immutable), expect_ident(self))
                 } else if self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
                           self.look_ahead(2, |t| t.is_keyword(keywords::SelfValue)) {
                     self.bump();
                     self.bump();
-                    (SelfKind::Region(None, Mutability::Mutable, expect_ident(self)),
-                        self.last_span)
+                    (SelfKind::Region(None, Mutability::Mutable), expect_ident(self))
                 } else if self.look_ahead(1, |t| t.is_lifetime()) &&
                           self.look_ahead(2, |t| t.is_keyword(keywords::SelfValue)) {
                     self.bump();
                     let lt = self.parse_lifetime()?;
-                    (SelfKind::Region(Some(lt), Mutability::Immutable, expect_ident(self)),
-                        self.last_span)
+                    (SelfKind::Region(Some(lt), Mutability::Immutable), expect_ident(self))
                 } else if self.look_ahead(1, |t| t.is_lifetime()) &&
                           self.look_ahead(2, |t| t.is_keyword(keywords::Mut)) &&
                           self.look_ahead(3, |t| t.is_keyword(keywords::SelfValue)) {
                     self.bump();
                     let lt = self.parse_lifetime()?;
                     self.bump();
-                    (SelfKind::Region(Some(lt), Mutability::Mutable, expect_ident(self)),
-                        self.last_span)
+                    (SelfKind::Region(Some(lt), Mutability::Mutable), expect_ident(self))
                 } else {
-                    (SelfKind::Static, codemap::DUMMY_SP)
+                    return Ok(None);
                 }
             }
             token::BinOp(token::Star) => {
@@ -4678,15 +4668,15 @@ impl<'a> Parser<'a> {
                 if self.look_ahead(1, |t| t.is_keyword(keywords::SelfValue)) {
                     self.bump();
                     self.span_err(self.span, "cannot pass `self` by raw pointer");
-                    (SelfKind::Value(expect_ident(self)), self.last_span)
+                    (SelfKind::Value(Mutability::Immutable), expect_ident(self))
                 } else if self.look_ahead(1, |t| t.is_mutability()) &&
                           self.look_ahead(2, |t| t.is_keyword(keywords::SelfValue)) {
                     self.bump();
                     self.bump();
                     self.span_err(self.span, "cannot pass `self` by raw pointer");
-                    (SelfKind::Value(expect_ident(self)), self.last_span)
+                    (SelfKind::Value(Mutability::Immutable), expect_ident(self))
                 } else {
-                    (SelfKind::Static, codemap::DUMMY_SP)
+                    return Ok(None);
                 }
             }
             token::Ident(..) => {
@@ -4694,64 +4684,69 @@ impl<'a> Parser<'a> {
                     // self
                     // self: TYPE
                     let eself_ident = expect_ident(self);
-                    let eself_ident_sp = self.last_span;
                     if self.eat(&token::Colon) {
-                        (SelfKind::Explicit(self.parse_ty_sum()?, eself_ident), eself_ident_sp)
+                        let ty = self.parse_ty_sum()?;
+                        (SelfKind::Explicit(ty, Mutability::Immutable), eself_ident)
                     } else {
-                        (SelfKind::Value(eself_ident), eself_ident_sp)
+                        (SelfKind::Value(Mutability::Immutable), eself_ident)
                     }
                 } else if self.token.is_keyword(keywords::Mut) &&
                         self.look_ahead(1, |t| t.is_keyword(keywords::SelfValue)) {
                     // mut self
                     // mut self: TYPE
-                    eself_mutbl = Mutability::Mutable;
                     self.bump();
                     let eself_ident = expect_ident(self);
-                    let eself_ident_sp = self.last_span;
                     if self.eat(&token::Colon) {
-                        (SelfKind::Explicit(self.parse_ty_sum()?, eself_ident), eself_ident_sp)
+                        let ty = self.parse_ty_sum()?;
+                        (SelfKind::Explicit(ty, Mutability::Mutable), eself_ident)
                     } else {
-                        (SelfKind::Value(eself_ident), eself_ident_sp)
+                        (SelfKind::Value(Mutability::Mutable), eself_ident)
                     }
                 } else {
-                    (SelfKind::Static, codemap::DUMMY_SP)
+                    return Ok(None);
                 }
             }
-            _ => (SelfKind::Static, codemap::DUMMY_SP)
+            _ => return Ok(None),
         };
-        let mut eself = codemap::respan(mk_sp(eself_lo, self.last_span.hi), eself);
+
+        let eself = codemap::respan(mk_sp(eself_lo, self.last_span.hi), eself);
+        Ok(Some(Arg::from_self(eself, eself_ident)))
+    }
+
+    /// Parse the parameter list and result type of a function that may have a `self` parameter.
+    fn parse_fn_decl_with_self<F>(&mut self, parse_arg_fn: F) -> PResult<'a, P<FnDecl>>
+        where F: FnMut(&mut Parser<'a>) -> PResult<'a,  Arg>,
+    {
+        self.expect(&token::OpenDelim(token::Paren))?;
+
+        // Parse optional self argument
+        let self_arg = self.parse_self_arg()?;
 
         // Parse the rest of the function parameter list.
         let sep = SeqSep::trailing_allowed(token::Comma);
-        let fn_inputs = match eself.node {
-            SelfKind::Static => {
-                eself.span = codemap::DUMMY_SP;
-                self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)
-            }
-            SelfKind::Value(..) | SelfKind::Region(..) | SelfKind::Explicit(..) => {
-                if self.check(&token::CloseDelim(token::Paren)) {
-                    vec![Arg::from_self(eself.clone(), eself_ident_sp, eself_mutbl)]
-                } else if self.check(&token::Comma) {
-                    self.bump();
-                    let mut fn_inputs = vec![Arg::from_self(eself.clone(), eself_ident_sp,
-                                                            eself_mutbl)];
-                    fn_inputs.append(&mut self.parse_seq_to_before_end(
-                        &token::CloseDelim(token::Paren), sep, parse_arg_fn)
-                    );
-                    fn_inputs
-                } else {
-                    return self.unexpected();
-                }
+        let fn_inputs = if let Some(self_arg) = self_arg {
+            if self.check(&token::CloseDelim(token::Paren)) {
+                vec![self_arg]
+            } else if self.eat(&token::Comma) {
+                let mut fn_inputs = vec![self_arg];
+                fn_inputs.append(&mut self.parse_seq_to_before_end(
+                    &token::CloseDelim(token::Paren), sep, parse_arg_fn)
+                );
+                fn_inputs
+            } else {
+                return self.unexpected();
             }
+        } else {
+            self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)
         };
 
         // Parse closing paren and return type.
         self.expect(&token::CloseDelim(token::Paren))?;
-        Ok((eself, P(FnDecl {
+        Ok(P(FnDecl {
             inputs: fn_inputs,
             output: self.parse_ret_ty()?,
             variadic: false
-        })))
+        }))
     }
 
     // parse the |arg, arg| header on a lambda
@@ -4932,10 +4927,10 @@ impl<'a> Parser<'a> {
             let tts = self.parse_seq_to_end(&token::CloseDelim(delim),
                                             SeqSep::none(),
                                             |p| p.parse_token_tree())?;
-            let m_ = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT };
+            let m_ = Mac_ { path: pth, tts: tts };
             let m: ast::Mac = codemap::Spanned { node: m_,
-                                                span: mk_sp(lo,
-                                                            self.last_span.hi) };
+                                                    span: mk_sp(lo,
+                                                                self.last_span.hi) };
             if delim != token::Brace {
                 self.expect(&token::Semi)?
             }
@@ -4944,15 +4939,12 @@ impl<'a> Parser<'a> {
             let (constness, unsafety, abi) = self.parse_fn_front_matter()?;
             let ident = self.parse_ident()?;
             let mut generics = self.parse_generics()?;
-            let (explicit_self, decl) = self.parse_fn_decl_with_self(|p| {
-                    p.parse_arg()
-                })?;
+            let decl = self.parse_fn_decl_with_self(|p| p.parse_arg())?;
             generics.where_clause = self.parse_where_clause()?;
             let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
             Ok((ident, inner_attrs, ast::ImplItemKind::Method(ast::MethodSig {
                 generics: generics,
                 abi: abi,
-                explicit_self: explicit_self,
                 unsafety: unsafety,
                 constness: constness,
                 decl: decl
@@ -4962,7 +4954,6 @@ impl<'a> Parser<'a> {
 
     /// Parse trait Foo { ... }
     fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<'a, ItemInfo> {
-
         let ident = self.parse_ident()?;
         let mut tps = self.parse_generics()?;
 
@@ -5277,7 +5268,7 @@ impl<'a> Parser<'a> {
             return Err(self.fatal(&format!("expected item, found `{}`", token_str)));
         }
 
-        let hi = if self.span == codemap::DUMMY_SP {
+        let hi = if self.span == syntax_pos::DUMMY_SP {
             inner_lo
         } else {
             self.last_span.hi
@@ -5452,18 +5443,15 @@ impl<'a> Parser<'a> {
                               name: String,
                               id_sp: Span) -> PResult<'a, (ast::ItemKind, Vec<ast::Attribute> )> {
         let mut included_mod_stack = self.sess.included_mod_stack.borrow_mut();
-        match included_mod_stack.iter().position(|p| *p == path) {
-            Some(i) => {
-                let mut err = String::from("circular modules: ");
-                let len = included_mod_stack.len();
-                for p in &included_mod_stack[i.. len] {
-                    err.push_str(&p.to_string_lossy());
-                    err.push_str(" -> ");
-                }
-                err.push_str(&path.to_string_lossy());
-                return Err(self.span_fatal(id_sp, &err[..]));
+        if let Some(i) = included_mod_stack.iter().position(|p| *p == path) {
+            let mut err = String::from("circular modules: ");
+            let len = included_mod_stack.len();
+            for p in &included_mod_stack[i.. len] {
+                err.push_str(&p.to_string_lossy());
+                err.push_str(" -> ");
             }
-            None => ()
+            err.push_str(&path.to_string_lossy());
+            return Err(self.span_fatal(id_sp, &err[..]));
         }
         included_mod_stack.push(path.clone());
         drop(included_mod_stack);
@@ -5702,15 +5690,12 @@ impl<'a> Parser<'a> {
             }
             _ => None
         };
-        match nt_item {
-            Some(mut item) => {
-                self.bump();
-                let mut attrs = attrs;
-                mem::swap(&mut item.attrs, &mut attrs);
-                item.attrs.extend(attrs);
-                return Ok(Some(P(item)));
-            }
-            None => {}
+        if let Some(mut item) = nt_item {
+            self.bump();
+            let mut attrs = attrs;
+            mem::swap(&mut item.attrs, &mut attrs);
+            item.attrs.extend(attrs);
+            return Ok(Some(P(item)));
         }
 
         let lo = self.span.lo;
@@ -6024,10 +6009,10 @@ impl<'a> Parser<'a> {
                                             SeqSep::none(),
                                             |p| p.parse_token_tree())?;
             // single-variant-enum... :
-            let m = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT };
+            let m = Mac_ { path: pth, tts: tts };
             let m: ast::Mac = codemap::Spanned { node: m,
-                                             span: mk_sp(mac_lo,
-                                                         self.last_span.hi) };
+                                                 span: mk_sp(mac_lo,
+                                                             self.last_span.hi) };
 
             if delim != token::Brace {
                 if !self.eat(&token::Semi) {
index 47de32ed7d00fe6a516ac9282f8e5f11ec359ca0..8376d28164dee58ad0b0f81354ec30a5e63a729b 100644 (file)
@@ -19,6 +19,7 @@ use ext::mtwt;
 use ptr::P;
 use util::interner::{RcStr, StrInterner};
 use util::interner;
+use tokenstream;
 
 use serialize::{Decodable, Decoder, Encodable, Encoder};
 use std::fmt;
@@ -338,7 +339,7 @@ pub enum Nonterminal {
     /// Stuff inside brackets for attributes
     NtMeta(P<ast::MetaItem>),
     NtPath(Box<ast::Path>),
-    NtTT(P<ast::TokenTree>), // needs P'ed to break a circularity
+    NtTT(P<tokenstream::TokenTree>), // needs P'ed to break a circularity
     // These are not exposed to macros, but are used by quasiquote.
     NtArm(ast::Arm),
     NtImplItem(P<ast::ImplItem>),
index 4a92ad8ddb26d8c20f55fe45dbfa9ae9de17f475..32b66da4d96ceffff3e3dc6fef92336530f04891 100644 (file)
@@ -159,9 +159,9 @@ pub struct PrintStackElem {
 const SIZE_INFINITY: isize = 0xffff;
 
 pub fn mk_printer<'a>(out: Box<io::Write+'a>, linewidth: usize) -> Printer<'a> {
-    // Yes 3, it makes the ring buffers big enough to never
+    // Yes 55, it makes the ring buffers big enough to never
     // fall behind.
-    let n: usize = 3 * linewidth;
+    let n: usize = 55 * linewidth;
     debug!("mk_printer {}", linewidth);
     let token = vec![Token::Eof; n];
     let size = vec![0; n];
index ebb4927d69c0b7cfad3d5f80bc1c509ca2fc84c6..ce30c3de7595867490b94ec77281f91d2e1a51fe 100644 (file)
 pub use self::AnnNode::*;
 
 use abi::{self, Abi};
-use ast::{self, TokenTree, BlockCheckMode, PatKind};
-use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
+use ast::{self, BlockCheckMode, PatKind};
+use ast::{SelfKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
 use ast::Attribute;
-use attr::ThinAttributesExt;
 use util::parser::AssocOp;
 use attr;
 use attr::{AttrMetaMethods, AttributeMethods};
-use codemap::{self, CodeMap, BytePos};
+use codemap::{self, CodeMap};
+use syntax_pos::{self, BytePos};
 use errors;
 use parse::token::{self, keywords, BinOpToken, Token, InternedString};
 use parse::lexer::comments;
@@ -28,6 +28,7 @@ use print::pp::{Breaks, eof};
 use print::pp::Breaks::{Consistent, Inconsistent};
 use ptr::P;
 use std_inject;
+use tokenstream::{self, TokenTree};
 
 use std::ascii;
 use std::io::{self, Write, Read};
@@ -123,16 +124,16 @@ pub fn print_crate<'a>(cm: &'a CodeMap,
         let list = attr::mk_list_item(InternedString::new("feature"),
                                       vec![prelude_import_meta]);
         let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), list);
-        s.print_attribute(&fake_attr)?;
+        try!(s.print_attribute(&fake_attr));
 
         // #![no_std]
         let no_std_meta = attr::mk_word_item(InternedString::new("no_std"));
         let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), no_std_meta);
-        s.print_attribute(&fake_attr)?;
+        try!(s.print_attribute(&fake_attr));
     }
 
-    s.print_mod(&krate.module, &krate.attrs)?;
-    s.print_remaining_comments()?;
+    try!(s.print_mod(&krate.module, &krate.attrs));
+    try!(s.print_remaining_comments());
     eof(&mut s.s)
 }
 
@@ -330,11 +331,11 @@ pub fn lifetime_to_string(e: &ast::Lifetime) -> String {
     to_string(|s| s.print_lifetime(e))
 }
 
-pub fn tt_to_string(tt: &ast::TokenTree) -> String {
+pub fn tt_to_string(tt: &tokenstream::TokenTree) -> String {
     to_string(|s| s.print_tt(tt))
 }
 
-pub fn tts_to_string(tts: &[ast::TokenTree]) -> String {
+pub fn tts_to_string(tts: &[tokenstream::TokenTree]) -> String {
     to_string(|s| s.print_tts(tts))
 }
 
@@ -382,14 +383,13 @@ pub fn fun_to_string(decl: &ast::FnDecl,
                      unsafety: ast::Unsafety,
                      constness: ast::Constness,
                      name: ast::Ident,
-                     opt_explicit_self: Option<&ast::SelfKind>,
                      generics: &ast::Generics)
                      -> String {
     to_string(|s| {
-        s.head("")?;
-        s.print_fn(decl, unsafety, constness, Abi::Rust, Some(name),
-                   generics, opt_explicit_self, &ast::Visibility::Inherited)?;
-        s.end()?; // Close the head box
+        try!(s.head(""));
+        try!(s.print_fn(decl, unsafety, constness, Abi::Rust, Some(name),
+                   generics, &ast::Visibility::Inherited));
+        try!(s.end()); // Close the head box
         s.end() // Close the outer box
     })
 }
@@ -397,9 +397,9 @@ pub fn fun_to_string(decl: &ast::FnDecl,
 pub fn block_to_string(blk: &ast::Block) -> String {
     to_string(|s| {
         // containing cbox, will be closed by print-block at }
-        s.cbox(INDENT_UNIT)?;
+        try!(s.cbox(INDENT_UNIT));
         // head-ibox, will be closed by print-block after {
-        s.ibox(0)?;
+        try!(s.ibox(0));
         s.print_block(blk)
     })
 }
@@ -416,10 +416,6 @@ pub fn lit_to_string(l: &ast::Lit) -> String {
     to_string(|s| s.print_literal(l))
 }
 
-pub fn explicit_self_to_string(explicit_self: &ast::SelfKind) -> String {
-    to_string(|s| s.print_explicit_self(explicit_self, ast::Mutability::Immutable).map(|_| {}))
-}
-
 pub fn variant_to_string(var: &ast::Variant) -> String {
     to_string(|s| s.print_variant(var))
 }
@@ -459,7 +455,7 @@ pub trait PrintState<'a> {
     fn literals(&self) -> &Option<Vec<comments::Literal>>;
 
     fn word_space(&mut self, w: &str) -> io::Result<()> {
-        word(self.writer(), w)?;
+        try!(word(self.writer(), w));
         space(self.writer())
     }
 
@@ -488,7 +484,7 @@ pub trait PrintState<'a> {
 
     fn hardbreak_if_not_bol(&mut self) -> io::Result<()> {
         if !self.is_bol() {
-            hardbreak(self.writer())?
+            try!(hardbreak(self.writer()))
         }
         Ok(())
     }
@@ -512,11 +508,11 @@ pub trait PrintState<'a> {
     fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], mut op: F) -> io::Result<()>
         where F: FnMut(&mut Self, &T) -> io::Result<()>,
     {
-        self.rbox(0, b)?;
+        try!(self.rbox(0, b));
         let mut first = true;
         for elt in elts {
-            if first { first = false; } else { self.word_space(",")?; }
-            op(self, elt)?;
+            if first { first = false; } else { try!(self.word_space(",")); }
+            try!(op(self, elt));
         }
         self.end()
     }
@@ -548,7 +544,7 @@ pub trait PrintState<'a> {
             match self.next_comment() {
                 Some(ref cmnt) => {
                     if (*cmnt).pos < pos {
-                        self.print_comment(cmnt)?;
+                        try!(self.print_comment(cmnt));
                         self.cur_cmnt_and_lit().cur_cmnt += 1;
                     } else { break; }
                 }
@@ -563,34 +559,34 @@ pub trait PrintState<'a> {
         match cmnt.style {
             comments::Mixed => {
                 assert_eq!(cmnt.lines.len(), 1);
-                zerobreak(self.writer())?;
-                word(self.writer(), &cmnt.lines[0])?;
+                try!(zerobreak(self.writer()));
+                try!(word(self.writer(), &cmnt.lines[0]));
                 zerobreak(self.writer())
             }
             comments::Isolated => {
-                self.hardbreak_if_not_bol()?;
+                try!(self.hardbreak_if_not_bol());
                 for line in &cmnt.lines {
                     // Don't print empty lines because they will end up as trailing
                     // whitespace
                     if !line.is_empty() {
-                        word(self.writer(), &line[..])?;
+                        try!(word(self.writer(), &line[..]));
                     }
-                    hardbreak(self.writer())?;
+                    try!(hardbreak(self.writer()));
                 }
                 Ok(())
             }
             comments::Trailing => {
-                word(self.writer(), " ")?;
+                try!(word(self.writer(), " "));
                 if cmnt.lines.len() == 1 {
-                    word(self.writer(), &cmnt.lines[0])?;
+                    try!(word(self.writer(), &cmnt.lines[0]));
                     hardbreak(self.writer())
                 } else {
-                    self.ibox(0)?;
+                    try!(self.ibox(0));
                     for line in &cmnt.lines {
                         if !line.is_empty() {
-                            word(self.writer(), &line[..])?;
+                            try!(word(self.writer(), &line[..]));
                         }
-                        hardbreak(self.writer())?;
+                        try!(hardbreak(self.writer()));
                     }
                     self.end()
                 }
@@ -602,7 +598,7 @@ pub trait PrintState<'a> {
                     _ => false
                 };
                 if is_semi || self.is_begin() || self.is_end() {
-                    hardbreak(self.writer())?;
+                    try!(hardbreak(self.writer()));
                 }
                 hardbreak(self.writer())
             }
@@ -624,7 +620,7 @@ pub trait PrintState<'a> {
     }
 
     fn print_literal(&mut self, lit: &ast::Lit) -> io::Result<()> {
-        self.maybe_print_comment(lit.span.lo)?;
+        try!(self.maybe_print_comment(lit.span.lo));
         match self.next_lit(lit.span.lo) {
             Some(ref ltrl) => {
                 return word(self.writer(), &(*ltrl).lit);
@@ -730,15 +726,15 @@ pub trait PrintState<'a> {
         let mut count = 0;
         for attr in attrs {
             if attr.node.style == kind {
-                    self.print_attribute_inline(attr, is_inline)?;
-                    if is_inline {
-                        self.nbsp()?;
-                    }
-                    count += 1;
+                try!(self.print_attribute_inline(attr, is_inline));
+                if is_inline {
+                    try!(self.nbsp());
+                }
+                count += 1;
             }
         }
         if count > 0 && trailing_hardbreak && !is_inline {
-            self.hardbreak_if_not_bol()?;
+            try!(self.hardbreak_if_not_bol());
         }
         Ok(())
     }
@@ -750,47 +746,47 @@ pub trait PrintState<'a> {
     fn print_attribute_inline(&mut self, attr: &ast::Attribute,
                               is_inline: bool) -> io::Result<()> {
         if !is_inline {
-            self.hardbreak_if_not_bol()?;
+            try!(self.hardbreak_if_not_bol());
         }
-        self.maybe_print_comment(attr.span.lo)?;
+        try!(self.maybe_print_comment(attr.span.lo));
         if attr.node.is_sugared_doc {
-            word(self.writer(), &attr.value_str().unwrap())?;
+            try!(word(self.writer(), &attr.value_str().unwrap()));
             hardbreak(self.writer())
         } else {
             match attr.node.style {
-                ast::AttrStyle::Inner => word(self.writer(), "#![")?,
-                ast::AttrStyle::Outer => word(self.writer(), "#[")?,
+                ast::AttrStyle::Inner => try!(word(self.writer(), "#![")),
+                ast::AttrStyle::Outer => try!(word(self.writer(), "#[")),
             }
-            self.print_meta_item(&attr.meta())?;
+            try!(self.print_meta_item(&attr.meta()));
             word(self.writer(), "]")
         }
     }
 
     fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> {
-        self.ibox(INDENT_UNIT)?;
+        try!(self.ibox(INDENT_UNIT));
         match item.node {
             ast::MetaItemKind::Word(ref name) => {
-                word(self.writer(), &name)?;
+                try!(word(self.writer(), &name));
             }
             ast::MetaItemKind::NameValue(ref name, ref value) => {
-                self.word_space(&name[..])?;
-                self.word_space("=")?;
-                self.print_literal(value)?;
+                try!(self.word_space(&name[..]));
+                try!(self.word_space("="));
+                try!(self.print_literal(value));
             }
             ast::MetaItemKind::List(ref name, ref items) => {
-                word(self.writer(), &name)?;
-                self.popen()?;
-                self.commasep(Consistent,
+                try!(word(self.writer(), &name));
+                try!(self.popen());
+                try!(self.commasep(Consistent,
                               &items[..],
-                              |s, i| s.print_meta_item(&i))?;
-                self.pclose()?;
+                              |s, i| s.print_meta_item(&i)));
+                try!(self.pclose());
             }
         }
         self.end()
     }
 
     fn space_if_not_bol(&mut self) -> io::Result<()> {
-        if !self.is_bol() { space(self.writer())?; }
+        if !self.is_bol() { try!(space(self.writer())); }
         Ok(())
     }
 
@@ -826,42 +822,42 @@ impl<'a> State<'a> {
     }
 
     pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> {
-        word(&mut self.s, w)?;
+        try!(word(&mut self.s, w));
         self.nbsp()
     }
 
     pub fn head(&mut self, w: &str) -> io::Result<()> {
         // outer-box is consistent
-        self.cbox(INDENT_UNIT)?;
+        try!(self.cbox(INDENT_UNIT));
         // head-box is inconsistent
-        self.ibox(w.len() + 1)?;
+        try!(self.ibox(w.len() + 1));
         // keyword that starts the head
         if !w.is_empty() {
-            self.word_nbsp(w)?;
+            try!(self.word_nbsp(w));
         }
         Ok(())
     }
 
     pub fn bopen(&mut self) -> io::Result<()> {
-        word(&mut self.s, "{")?;
+        try!(word(&mut self.s, "{"));
         self.end() // close the head-box
     }
 
-    pub fn bclose_(&mut self, span: codemap::Span,
+    pub fn bclose_(&mut self, span: syntax_pos::Span,
                    indented: usize) -> io::Result<()> {
         self.bclose_maybe_open(span, indented, true)
     }
-    pub fn bclose_maybe_open(&mut self, span: codemap::Span,
+    pub fn bclose_maybe_open(&mut self, span: syntax_pos::Span,
                              indented: usize, close_box: bool) -> io::Result<()> {
-        self.maybe_print_comment(span.hi)?;
-        self.break_offset_if_not_bol(1, -(indented as isize))?;
-        word(&mut self.s, "}")?;
+        try!(self.maybe_print_comment(span.hi));
+        try!(self.break_offset_if_not_bol(1, -(indented as isize)));
+        try!(word(&mut self.s, "}"));
         if close_box {
-            self.end()?; // close the outer-box
+            try!(self.end()); // close the outer-box
         }
         Ok(())
     }
-    pub fn bclose(&mut self, span: codemap::Span) -> io::Result<()> {
+    pub fn bclose(&mut self, span: syntax_pos::Span) -> io::Result<()> {
         self.bclose_(span, INDENT_UNIT)
     }
 
@@ -890,10 +886,10 @@ impl<'a> State<'a> {
     // Synthesizes a comment that was not textually present in the original source
     // file.
     pub fn synth_comment(&mut self, text: String) -> io::Result<()> {
-        word(&mut self.s, "/*")?;
-        space(&mut self.s)?;
-        word(&mut self.s, &text[..])?;
-        space(&mut self.s)?;
+        try!(word(&mut self.s, "/*"));
+        try!(space(&mut self.s));
+        try!(word(&mut self.s, &text[..]));
+        try!(space(&mut self.s));
         word(&mut self.s, "*/")
     }
 
@@ -905,20 +901,20 @@ impl<'a> State<'a> {
                                   mut op: F,
                                   mut get_span: G) -> io::Result<()> where
         F: FnMut(&mut State, &T) -> io::Result<()>,
-        G: FnMut(&T) -> codemap::Span,
+        G: FnMut(&T) -> syntax_pos::Span,
     {
-        self.rbox(0, b)?;
+        try!(self.rbox(0, b));
         let len = elts.len();
         let mut i = 0;
         for elt in elts {
-            self.maybe_print_comment(get_span(elt).hi)?;
-            op(self, elt)?;
+            try!(self.maybe_print_comment(get_span(elt).hi));
+            try!(op(self, elt));
             i += 1;
             if i < len {
-                word(&mut self.s, ",")?;
-                self.maybe_print_trailing_comment(get_span(elt),
-                                                  Some(get_span(&elts[i]).hi))?;
-                self.space_if_not_bol()?;
+                try!(word(&mut self.s, ","));
+                try!(self.maybe_print_trailing_comment(get_span(elt),
+                                                  Some(get_span(&elts[i]).hi)));
+                try!(self.space_if_not_bol());
             }
         }
         self.end()
@@ -931,18 +927,18 @@ impl<'a> State<'a> {
 
     pub fn print_mod(&mut self, _mod: &ast::Mod,
                      attrs: &[ast::Attribute]) -> io::Result<()> {
-        self.print_inner_attributes(attrs)?;
+        try!(self.print_inner_attributes(attrs));
         for item in &_mod.items {
-            self.print_item(&item)?;
+            try!(self.print_item(&item));
         }
         Ok(())
     }
 
     pub fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod,
                              attrs: &[ast::Attribute]) -> io::Result<()> {
-        self.print_inner_attributes(attrs)?;
+        try!(self.print_inner_attributes(attrs));
         for item in &nmod.items {
-            self.print_foreign_item(item)?;
+            try!(self.print_foreign_item(item));
         }
         Ok(())
     }
@@ -950,47 +946,47 @@ impl<'a> State<'a> {
     pub fn print_opt_lifetime(&mut self,
                               lifetime: &Option<ast::Lifetime>) -> io::Result<()> {
         if let Some(l) = *lifetime {
-            self.print_lifetime(&l)?;
-            self.nbsp()?;
+            try!(self.print_lifetime(&l));
+            try!(self.nbsp());
         }
         Ok(())
     }
 
     pub fn print_type(&mut self, ty: &ast::Ty) -> io::Result<()> {
-        self.maybe_print_comment(ty.span.lo)?;
-        self.ibox(0)?;
+        try!(self.maybe_print_comment(ty.span.lo));
+        try!(self.ibox(0));
         match ty.node {
             ast::TyKind::Vec(ref ty) => {
-                word(&mut self.s, "[")?;
-                self.print_type(&ty)?;
-                word(&mut self.s, "]")?;
+                try!(word(&mut self.s, "["));
+                try!(self.print_type(&ty));
+                try!(word(&mut self.s, "]"));
             }
             ast::TyKind::Ptr(ref mt) => {
-                word(&mut self.s, "*")?;
+                try!(word(&mut self.s, "*"));
                 match mt.mutbl {
-                    ast::Mutability::Mutable => self.word_nbsp("mut")?,
-                    ast::Mutability::Immutable => self.word_nbsp("const")?,
+                    ast::Mutability::Mutable => try!(self.word_nbsp("mut")),
+                    ast::Mutability::Immutable => try!(self.word_nbsp("const")),
                 }
-                self.print_type(&mt.ty)?;
+                try!(self.print_type(&mt.ty));
             }
             ast::TyKind::Rptr(ref lifetime, ref mt) => {
-                word(&mut self.s, "&")?;
-                self.print_opt_lifetime(lifetime)?;
-                self.print_mt(mt)?;
+                try!(word(&mut self.s, "&"));
+                try!(self.print_opt_lifetime(lifetime));
+                try!(self.print_mt(mt));
             }
             ast::TyKind::Tup(ref elts) => {
-                self.popen()?;
-                self.commasep(Inconsistent, &elts[..],
-                              |s, ty| s.print_type(&ty))?;
+                try!(self.popen());
+                try!(self.commasep(Inconsistent, &elts[..],
+                              |s, ty| s.print_type(&ty)));
                 if elts.len() == 1 {
-                    word(&mut self.s, ",")?;
+                    try!(word(&mut self.s, ","));
                 }
-                self.pclose()?;
+                try!(self.pclose());
             }
             ast::TyKind::Paren(ref typ) => {
-                self.popen()?;
-                self.print_type(&typ)?;
-                self.pclose()?;
+                try!(self.popen());
+                try!(self.print_type(&typ));
+                try!(self.pclose());
             }
             ast::TyKind::BareFn(ref f) => {
                 let generics = ast::Generics {
@@ -1001,43 +997,45 @@ impl<'a> State<'a> {
                         predicates: Vec::new(),
                     },
                 };
-                self.print_ty_fn(f.abi,
+                try!(self.print_ty_fn(f.abi,
                                  f.unsafety,
                                  &f.decl,
                                  None,
-                                 &generics,
-                                 None)?;
+                                 &generics));
             }
             ast::TyKind::Path(None, ref path) => {
-                self.print_path(path, false, 0)?;
+                try!(self.print_path(path, false, 0));
             }
             ast::TyKind::Path(Some(ref qself), ref path) => {
-                self.print_qpath(path, qself, false)?
+                try!(self.print_qpath(path, qself, false))
             }
             ast::TyKind::ObjectSum(ref ty, ref bounds) => {
-                self.print_type(&ty)?;
-                self.print_bounds("+", &bounds[..])?;
+                try!(self.print_type(&ty));
+                try!(self.print_bounds("+", &bounds[..]));
             }
             ast::TyKind::PolyTraitRef(ref bounds) => {
-                self.print_bounds("", &bounds[..])?;
+                try!(self.print_bounds("", &bounds[..]));
             }
             ast::TyKind::FixedLengthVec(ref ty, ref v) => {
-                word(&mut self.s, "[")?;
-                self.print_type(&ty)?;
-                word(&mut self.s, "; ")?;
-                self.print_expr(&v)?;
-                word(&mut self.s, "]")?;
+                try!(word(&mut self.s, "["));
+                try!(self.print_type(&ty));
+                try!(word(&mut self.s, "; "));
+                try!(self.print_expr(&v));
+                try!(word(&mut self.s, "]"));
             }
             ast::TyKind::Typeof(ref e) => {
-                word(&mut self.s, "typeof(")?;
-                self.print_expr(&e)?;
-                word(&mut self.s, ")")?;
+                try!(word(&mut self.s, "typeof("));
+                try!(self.print_expr(&e));
+                try!(word(&mut self.s, ")"));
             }
             ast::TyKind::Infer => {
-                word(&mut self.s, "_")?;
+                try!(word(&mut self.s, "_"));
+            }
+            ast::TyKind::ImplicitSelf => {
+                try!(word(&mut self.s, "Self"));
             }
             ast::TyKind::Mac(ref m) => {
-                self.print_mac(m, token::Paren)?;
+                try!(self.print_mac(m, token::Paren));
             }
         }
         self.end()
@@ -1045,30 +1043,30 @@ impl<'a> State<'a> {
 
     pub fn print_foreign_item(&mut self,
                               item: &ast::ForeignItem) -> io::Result<()> {
-        self.hardbreak_if_not_bol()?;
-        self.maybe_print_comment(item.span.lo)?;
-        self.print_outer_attributes(&item.attrs)?;
+        try!(self.hardbreak_if_not_bol());
+        try!(self.maybe_print_comment(item.span.lo));
+        try!(self.print_outer_attributes(&item.attrs));
         match item.node {
             ast::ForeignItemKind::Fn(ref decl, ref generics) => {
-                self.head("")?;
-                self.print_fn(decl, ast::Unsafety::Normal,
+                try!(self.head(""));
+                try!(self.print_fn(decl, ast::Unsafety::Normal,
                               ast::Constness::NotConst,
                               Abi::Rust, Some(item.ident),
-                              generics, None, &item.vis)?;
-                self.end()?; // end head-ibox
-                word(&mut self.s, ";")?;
+                              generics, &item.vis));
+                try!(self.end()); // end head-ibox
+                try!(word(&mut self.s, ";"));
                 self.end() // end the outer fn box
             }
             ast::ForeignItemKind::Static(ref t, m) => {
-                self.head(&visibility_qualified(&item.vis, "static"))?;
+                try!(self.head(&visibility_qualified(&item.vis, "static")));
                 if m {
-                    self.word_space("mut")?;
+                    try!(self.word_space("mut"));
                 }
-                self.print_ident(item.ident)?;
-                self.word_space(":")?;
-                self.print_type(&t)?;
-                word(&mut self.s, ";")?;
-                self.end()?; // end the head-ibox
+                try!(self.print_ident(item.ident));
+                try!(self.word_space(":"));
+                try!(self.print_type(&t));
+                try!(word(&mut self.s, ";"));
+                try!(self.end()); // end the head-ibox
                 self.end() // end the outer cbox
             }
         }
@@ -1081,15 +1079,15 @@ impl<'a> State<'a> {
                               vis: &ast::Visibility)
                               -> io::Result<()>
     {
-        word(&mut self.s, &visibility_qualified(vis, ""))?;
-        self.word_space("const")?;
-        self.print_ident(ident)?;
-        self.word_space(":")?;
-        self.print_type(ty)?;
+        try!(word(&mut self.s, &visibility_qualified(vis, "")));
+        try!(self.word_space("const"));
+        try!(self.print_ident(ident));
+        try!(self.word_space(":"));
+        try!(self.print_type(ty));
         if let Some(expr) = default {
-            space(&mut self.s)?;
-            self.word_space("=")?;
-            self.print_expr(expr)?;
+            try!(space(&mut self.s));
+            try!(self.word_space("="));
+            try!(self.print_expr(expr));
         }
         word(&mut self.s, ";")
     }
@@ -1099,150 +1097,149 @@ impl<'a> State<'a> {
                              bounds: Option<&ast::TyParamBounds>,
                              ty: Option<&ast::Ty>)
                              -> io::Result<()> {
-        self.word_space("type")?;
-        self.print_ident(ident)?;
+        try!(self.word_space("type"));
+        try!(self.print_ident(ident));
         if let Some(bounds) = bounds {
-            self.print_bounds(":", bounds)?;
+            try!(self.print_bounds(":", bounds));
         }
         if let Some(ty) = ty {
-            space(&mut self.s)?;
-            self.word_space("=")?;
-            self.print_type(ty)?;
+            try!(space(&mut self.s));
+            try!(self.word_space("="));
+            try!(self.print_type(ty));
         }
         word(&mut self.s, ";")
     }
 
     /// Pretty-print an item
     pub fn print_item(&mut self, item: &ast::Item) -> io::Result<()> {
-        self.hardbreak_if_not_bol()?;
-        self.maybe_print_comment(item.span.lo)?;
-        self.print_outer_attributes(&item.attrs)?;
-        self.ann.pre(self, NodeItem(item))?;
+        try!(self.hardbreak_if_not_bol());
+        try!(self.maybe_print_comment(item.span.lo));
+        try!(self.print_outer_attributes(&item.attrs));
+        try!(self.ann.pre(self, NodeItem(item)));
         match item.node {
             ast::ItemKind::ExternCrate(ref optional_path) => {
-                self.head(&visibility_qualified(&item.vis, "extern crate"))?;
+                try!(self.head(&visibility_qualified(&item.vis, "extern crate")));
                 if let Some(p) = *optional_path {
                     let val = p.as_str();
                     if val.contains("-") {
-                        self.print_string(&val, ast::StrStyle::Cooked)?;
+                        try!(self.print_string(&val, ast::StrStyle::Cooked));
                     } else {
-                        self.print_name(p)?;
+                        try!(self.print_name(p));
                     }
-                    space(&mut self.s)?;
-                    word(&mut self.s, "as")?;
-                    space(&mut self.s)?;
+                    try!(space(&mut self.s));
+                    try!(word(&mut self.s, "as"));
+                    try!(space(&mut self.s));
                 }
-                self.print_ident(item.ident)?;
-                word(&mut self.s, ";")?;
-                self.end()?; // end inner head-block
-                self.end()?; // end outer head-block
+                try!(self.print_ident(item.ident));
+                try!(word(&mut self.s, ";"));
+                try!(self.end()); // end inner head-block
+                try!(self.end()); // end outer head-block
             }
             ast::ItemKind::Use(ref vp) => {
-                self.head(&visibility_qualified(&item.vis, "use"))?;
-                self.print_view_path(&vp)?;
-                word(&mut self.s, ";")?;
-                self.end()?; // end inner head-block
-                self.end()?; // end outer head-block
+                try!(self.head(&visibility_qualified(&item.vis, "use")));
+                try!(self.print_view_path(&vp));
+                try!(word(&mut self.s, ";"));
+                try!(self.end()); // end inner head-block
+                try!(self.end()); // end outer head-block
             }
             ast::ItemKind::Static(ref ty, m, ref expr) => {
-                self.head(&visibility_qualified(&item.vis, "static"))?;
+                try!(self.head(&visibility_qualified(&item.vis, "static")));
                 if m == ast::Mutability::Mutable {
-                    self.word_space("mut")?;
+                    try!(self.word_space("mut"));
                 }
-                self.print_ident(item.ident)?;
-                self.word_space(":")?;
-                self.print_type(&ty)?;
-                space(&mut self.s)?;
-                self.end()?; // end the head-ibox
+                try!(self.print_ident(item.ident));
+                try!(self.word_space(":"));
+                try!(self.print_type(&ty));
+                try!(space(&mut self.s));
+                try!(self.end()); // end the head-ibox
 
-                self.word_space("=")?;
-                self.print_expr(&expr)?;
-                word(&mut self.s, ";")?;
-                self.end()?; // end the outer cbox
+                try!(self.word_space("="));
+                try!(self.print_expr(&expr));
+                try!(word(&mut self.s, ";"));
+                try!(self.end()); // end the outer cbox
             }
             ast::ItemKind::Const(ref ty, ref expr) => {
-                self.head(&visibility_qualified(&item.vis, "const"))?;
-                self.print_ident(item.ident)?;
-                self.word_space(":")?;
-                self.print_type(&ty)?;
-                space(&mut self.s)?;
-                self.end()?; // end the head-ibox
-
-                self.word_space("=")?;
-                self.print_expr(&expr)?;
-                word(&mut self.s, ";")?;
-                self.end()?; // end the outer cbox
+                try!(self.head(&visibility_qualified(&item.vis, "const")));
+                try!(self.print_ident(item.ident));
+                try!(self.word_space(":"));
+                try!(self.print_type(&ty));
+                try!(space(&mut self.s));
+                try!(self.end()); // end the head-ibox
+
+                try!(self.word_space("="));
+                try!(self.print_expr(&expr));
+                try!(word(&mut self.s, ";"));
+                try!(self.end()); // end the outer cbox
             }
             ast::ItemKind::Fn(ref decl, unsafety, constness, abi, ref typarams, ref body) => {
-                self.head("")?;
-                self.print_fn(
+                try!(self.head(""));
+                try!(self.print_fn(
                     decl,
                     unsafety,
                     constness,
                     abi,
                     Some(item.ident),
                     typarams,
-                    None,
                     &item.vis
-                )?;
-                word(&mut self.s, " ")?;
-                self.print_block_with_attrs(&body, &item.attrs)?;
+                ));
+                try!(word(&mut self.s, " "));
+                try!(self.print_block_with_attrs(&body, &item.attrs));
             }
             ast::ItemKind::Mod(ref _mod) => {
-                self.head(&visibility_qualified(&item.vis, "mod"))?;
-                self.print_ident(item.ident)?;
-                self.nbsp()?;
-                self.bopen()?;
-                self.print_mod(_mod, &item.attrs)?;
-                self.bclose(item.span)?;
+                try!(self.head(&visibility_qualified(&item.vis, "mod")));
+                try!(self.print_ident(item.ident));
+                try!(self.nbsp());
+                try!(self.bopen());
+                try!(self.print_mod(_mod, &item.attrs));
+                try!(self.bclose(item.span));
             }
             ast::ItemKind::ForeignMod(ref nmod) => {
-                self.head("extern")?;
-                self.word_nbsp(&nmod.abi.to_string())?;
-                self.bopen()?;
-                self.print_foreign_mod(nmod, &item.attrs)?;
-                self.bclose(item.span)?;
+                try!(self.head("extern"));
+                try!(self.word_nbsp(&nmod.abi.to_string()));
+                try!(self.bopen());
+                try!(self.print_foreign_mod(nmod, &item.attrs));
+                try!(self.bclose(item.span));
             }
             ast::ItemKind::Ty(ref ty, ref params) => {
-                self.ibox(INDENT_UNIT)?;
-                self.ibox(0)?;
-                self.word_nbsp(&visibility_qualified(&item.vis, "type"))?;
-                self.print_ident(item.ident)?;
-                self.print_generics(params)?;
-                self.end()?; // end the inner ibox
-
-                self.print_where_clause(&params.where_clause)?;
-                space(&mut self.s)?;
-                self.word_space("=")?;
-                self.print_type(&ty)?;
-                word(&mut self.s, ";")?;
-                self.end()?; // end the outer ibox
+                try!(self.ibox(INDENT_UNIT));
+                try!(self.ibox(0));
+                try!(self.word_nbsp(&visibility_qualified(&item.vis, "type")));
+                try!(self.print_ident(item.ident));
+                try!(self.print_generics(params));
+                try!(self.end()); // end the inner ibox
+
+                try!(self.print_where_clause(&params.where_clause));
+                try!(space(&mut self.s));
+                try!(self.word_space("="));
+                try!(self.print_type(&ty));
+                try!(word(&mut self.s, ";"));
+                try!(self.end()); // end the outer ibox
             }
             ast::ItemKind::Enum(ref enum_definition, ref params) => {
-                self.print_enum_def(
+                try!(self.print_enum_def(
                     enum_definition,
                     params,
                     item.ident,
                     item.span,
                     &item.vis
-                )?;
+                ));
             }
             ast::ItemKind::Struct(ref struct_def, ref generics) => {
-                self.head(&visibility_qualified(&item.vis, "struct"))?;
-                self.print_struct(&struct_def, generics, item.ident, item.span, true)?;
+                try!(self.head(&visibility_qualified(&item.vis, "struct")));
+                try!(self.print_struct(&struct_def, generics, item.ident, item.span, true));
             }
 
             ast::ItemKind::DefaultImpl(unsafety, ref trait_ref) => {
-                self.head("")?;
-                self.print_visibility(&item.vis)?;
-                self.print_unsafety(unsafety)?;
-                self.word_nbsp("impl")?;
-                self.print_trait_ref(trait_ref)?;
-                space(&mut self.s)?;
-                self.word_space("for")?;
-                self.word_space("..")?;
-                self.bopen()?;
-                self.bclose(item.span)?;
+                try!(self.head(""));
+                try!(self.print_visibility(&item.vis));
+                try!(self.print_unsafety(unsafety));
+                try!(self.word_nbsp("impl"));
+                try!(self.print_trait_ref(trait_ref));
+                try!(space(&mut self.s));
+                try!(self.word_space("for"));
+                try!(self.word_space(".."));
+                try!(self.bopen());
+                try!(self.bclose(item.span));
             }
             ast::ItemKind::Impl(unsafety,
                           polarity,
@@ -1250,80 +1247,77 @@ impl<'a> State<'a> {
                           ref opt_trait,
                           ref ty,
                           ref impl_items) => {
-                self.head("")?;
-                self.print_visibility(&item.vis)?;
-                self.print_unsafety(unsafety)?;
-                self.word_nbsp("impl")?;
+                try!(self.head(""));
+                try!(self.print_visibility(&item.vis));
+                try!(self.print_unsafety(unsafety));
+                try!(self.word_nbsp("impl"));
 
                 if generics.is_parameterized() {
-                    self.print_generics(generics)?;
-                    space(&mut self.s)?;
+                    try!(self.print_generics(generics));
+                    try!(space(&mut self.s));
                 }
 
                 match polarity {
                     ast::ImplPolarity::Negative => {
-                        word(&mut self.s, "!")?;
+                        try!(word(&mut self.s, "!"));
                     },
                     _ => {}
                 }
 
-                match *opt_trait {
-                    Some(ref t) => {
-                        self.print_trait_ref(t)?;
-                        space(&mut self.s)?;
-                        self.word_space("for")?;
-                    }
-                    None => {}
+                if let Some(ref t) = *opt_trait {
+                    try!(self.print_trait_ref(t));
+                    try!(space(&mut self.s));
+                    try!(self.word_space("for"));
                 }
 
-                self.print_type(&ty)?;
-                self.print_where_clause(&generics.where_clause)?;
+                try!(self.print_type(&ty));
+                try!(self.print_where_clause(&generics.where_clause));
 
-                space(&mut self.s)?;
-                self.bopen()?;
-                self.print_inner_attributes(&item.attrs)?;
+                try!(space(&mut self.s));
+                try!(self.bopen());
+                try!(self.print_inner_attributes(&item.attrs));
                 for impl_item in impl_items {
-                    self.print_impl_item(impl_item)?;
+                    try!(self.print_impl_item(impl_item));
                 }
-                self.bclose(item.span)?;
+                try!(self.bclose(item.span));
             }
             ast::ItemKind::Trait(unsafety, ref generics, ref bounds, ref trait_items) => {
-                self.head("")?;
-                self.print_visibility(&item.vis)?;
-                self.print_unsafety(unsafety)?;
-                self.word_nbsp("trait")?;
-                self.print_ident(item.ident)?;
-                self.print_generics(generics)?;
+                try!(self.head(""));
+                try!(self.print_visibility(&item.vis));
+                try!(self.print_unsafety(unsafety));
+                try!(self.word_nbsp("trait"));
+                try!(self.print_ident(item.ident));
+                try!(self.print_generics(generics));
                 let mut real_bounds = Vec::with_capacity(bounds.len());
                 for b in bounds.iter() {
                     if let TraitTyParamBound(ref ptr, ast::TraitBoundModifier::Maybe) = *b {
-                        space(&mut self.s)?;
-                        self.word_space("for ?")?;
-                        self.print_trait_ref(&ptr.trait_ref)?;
+                        try!(space(&mut self.s));
+                        try!(self.word_space("for ?"));
+                        try!(self.print_trait_ref(&ptr.trait_ref));
                     } else {
                         real_bounds.push(b.clone());
                     }
                 }
-                self.print_bounds(":", &real_bounds[..])?;
-                self.print_where_clause(&generics.where_clause)?;
-                word(&mut self.s, " ")?;
-                self.bopen()?;
+                try!(self.print_bounds(":", &real_bounds[..]));
+                try!(self.print_where_clause(&generics.where_clause));
+                try!(word(&mut self.s, " "));
+                try!(self.bopen());
                 for trait_item in trait_items {
-                    self.print_trait_item(trait_item)?;
+                    try!(self.print_trait_item(trait_item));
                 }
-                self.bclose(item.span)?;
+                try!(self.bclose(item.span));
             }
             ast::ItemKind::Mac(codemap::Spanned { ref node, .. }) => {
-                self.print_visibility(&item.vis)?;
-                self.print_path(&node.path, false, 0)?;
-                word(&mut self.s, "! ")?;
-                self.print_ident(item.ident)?;
-                self.cbox(INDENT_UNIT)?;
-                self.popen()?;
-                self.print_tts(&node.tts[..])?;
-                self.pclose()?;
-                word(&mut self.s, ";")?;
-                self.end()?;
+                try!(self.print_visibility(&item.vis));
+                try!(self.print_path(&node.path, false, 0));
+                try!(word(&mut self.s, "! "));
+                try!(self.print_ident(item.ident));
+                try!(self.cbox(INDENT_UNIT));
+                try!(self.popen());
+                try!(self.print_tts(&node.tts[..]));
+                try!(self.pclose());
+                try!(word(&mut self.s, ";"));
+                try!(self.end());
             }
         }
         self.ann.post(self, NodeItem(item))
@@ -1335,50 +1329,50 @@ impl<'a> State<'a> {
 
     fn print_formal_lifetime_list(&mut self, lifetimes: &[ast::LifetimeDef]) -> io::Result<()> {
         if !lifetimes.is_empty() {
-            word(&mut self.s, "for<")?;
+            try!(word(&mut self.s, "for<"));
             let mut comma = false;
             for lifetime_def in lifetimes {
                 if comma {
-                    self.word_space(",")?
+                    try!(self.word_space(","))
                 }
-                self.print_lifetime_def(lifetime_def)?;
+                try!(self.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds));
                 comma = true;
             }
-            word(&mut self.s, ">")?;
+            try!(word(&mut self.s, ">"));
         }
         Ok(())
     }
 
     fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) -> io::Result<()> {
-        self.print_formal_lifetime_list(&t.bound_lifetimes)?;
+        try!(self.print_formal_lifetime_list(&t.bound_lifetimes));
         self.print_trait_ref(&t.trait_ref)
     }
 
     pub fn print_enum_def(&mut self, enum_definition: &ast::EnumDef,
                           generics: &ast::Generics, ident: ast::Ident,
-                          span: codemap::Span,
+                          span: syntax_pos::Span,
                           visibility: &ast::Visibility) -> io::Result<()> {
-        self.head(&visibility_qualified(visibility, "enum"))?;
-        self.print_ident(ident)?;
-        self.print_generics(generics)?;
-        self.print_where_clause(&generics.where_clause)?;
-        space(&mut self.s)?;
+        try!(self.head(&visibility_qualified(visibility, "enum")));
+        try!(self.print_ident(ident));
+        try!(self.print_generics(generics));
+        try!(self.print_where_clause(&generics.where_clause));
+        try!(space(&mut self.s));
         self.print_variants(&enum_definition.variants, span)
     }
 
     pub fn print_variants(&mut self,
                           variants: &[ast::Variant],
-                          span: codemap::Span) -> io::Result<()> {
-        self.bopen()?;
+                          span: syntax_pos::Span) -> io::Result<()> {
+        try!(self.bopen());
         for v in variants {
-            self.space_if_not_bol()?;
-            self.maybe_print_comment(v.span.lo)?;
-            self.print_outer_attributes(&v.node.attrs)?;
-            self.ibox(INDENT_UNIT)?;
-            self.print_variant(v)?;
-            word(&mut self.s, ",")?;
-            self.end()?;
-            self.maybe_print_trailing_comment(v.span, None)?;
+            try!(self.space_if_not_bol());
+            try!(self.maybe_print_comment(v.span.lo));
+            try!(self.print_outer_attributes(&v.node.attrs));
+            try!(self.ibox(INDENT_UNIT));
+            try!(self.print_variant(v));
+            try!(word(&mut self.s, ","));
+            try!(self.end());
+            try!(self.maybe_print_trailing_comment(v.span, None));
         }
         self.bclose(span)
     }
@@ -1397,44 +1391,45 @@ impl<'a> State<'a> {
                         struct_def: &ast::VariantData,
                         generics: &ast::Generics,
                         ident: ast::Ident,
-                        span: codemap::Span,
+                        span: syntax_pos::Span,
                         print_finalizer: bool) -> io::Result<()> {
-        self.print_ident(ident)?;
-        self.print_generics(generics)?;
+        try!(self.print_ident(ident));
+        try!(self.print_generics(generics));
         if !struct_def.is_struct() {
             if struct_def.is_tuple() {
-                self.popen()?;
-                self.commasep(
+                try!(self.popen());
+                try!(self.commasep(
                     Inconsistent, struct_def.fields(),
                     |s, field| {
-                        s.print_visibility(&field.vis)?;
-                        s.maybe_print_comment(field.span.lo)?;
+                        try!(s.maybe_print_comment(field.span.lo));
+                        try!(s.print_outer_attributes(&field.attrs));
+                        try!(s.print_visibility(&field.vis));
                         s.print_type(&field.ty)
                     }
-                )?;
-                self.pclose()?;
+                ));
+                try!(self.pclose());
             }
-            self.print_where_clause(&generics.where_clause)?;
+            try!(self.print_where_clause(&generics.where_clause));
             if print_finalizer {
-                word(&mut self.s, ";")?;
+                try!(word(&mut self.s, ";"));
             }
-            self.end()?;
+            try!(self.end());
             self.end() // close the outer-box
         } else {
-            self.print_where_clause(&generics.where_clause)?;
-            self.nbsp()?;
-            self.bopen()?;
-            self.hardbreak_if_not_bol()?;
+            try!(self.print_where_clause(&generics.where_clause));
+            try!(self.nbsp());
+            try!(self.bopen());
+            try!(self.hardbreak_if_not_bol());
 
             for field in struct_def.fields() {
-                self.hardbreak_if_not_bol()?;
-                self.maybe_print_comment(field.span.lo)?;
-                self.print_outer_attributes(&field.attrs)?;
-                self.print_visibility(&field.vis)?;
-                self.print_ident(field.ident.unwrap())?;
-                self.word_nbsp(":")?;
-                self.print_type(&field.ty)?;
-                word(&mut self.s, ",")?;
+                try!(self.hardbreak_if_not_bol());
+                try!(self.maybe_print_comment(field.span.lo));
+                try!(self.print_outer_attributes(&field.attrs));
+                try!(self.print_visibility(&field.vis));
+                try!(self.print_ident(field.ident.unwrap()));
+                try!(self.word_nbsp(":"));
+                try!(self.print_type(&field.ty));
+                try!(word(&mut self.s, ","));
             }
 
             self.bclose(span)
@@ -1448,10 +1443,10 @@ impl<'a> State<'a> {
     /// appropriate macro, transcribe back into the grammar we just parsed from,
     /// and then pretty-print the resulting AST nodes (so, e.g., we print
     /// expression arguments as expressions). It can be done! I think.
-    pub fn print_tt(&mut self, tt: &ast::TokenTree) -> io::Result<()> {
+    pub fn print_tt(&mut self, tt: &tokenstream::TokenTree) -> io::Result<()> {
         match *tt {
             TokenTree::Token(_, ref tk) => {
-                word(&mut self.s, &token_to_string(tk))?;
+                try!(word(&mut self.s, &token_to_string(tk)));
                 match *tk {
                     parse::token::DocComment(..) => {
                         hardbreak(&mut self.s)
@@ -1460,51 +1455,48 @@ impl<'a> State<'a> {
                 }
             }
             TokenTree::Delimited(_, ref delimed) => {
-                word(&mut self.s, &token_to_string(&delimed.open_token()))?;
-                space(&mut self.s)?;
-                self.print_tts(&delimed.tts)?;
-                space(&mut self.s)?;
+                try!(word(&mut self.s, &token_to_string(&delimed.open_token())));
+                try!(space(&mut self.s));
+                try!(self.print_tts(&delimed.tts));
+                try!(space(&mut self.s));
                 word(&mut self.s, &token_to_string(&delimed.close_token()))
             },
             TokenTree::Sequence(_, ref seq) => {
-                word(&mut self.s, "$(")?;
+                try!(word(&mut self.s, "$("));
                 for tt_elt in &seq.tts {
-                    self.print_tt(tt_elt)?;
+                    try!(self.print_tt(tt_elt));
                 }
-                word(&mut self.s, ")")?;
-                match seq.separator {
-                    Some(ref tk) => {
-                        word(&mut self.s, &token_to_string(tk))?;
-                    }
-                    None => {},
+                try!(word(&mut self.s, ")"));
+                if let Some(ref tk) = seq.separator {
+                    try!(word(&mut self.s, &token_to_string(tk)));
                 }
                 match seq.op {
-                    ast::KleeneOp::ZeroOrMore => word(&mut self.s, "*"),
-                    ast::KleeneOp::OneOrMore => word(&mut self.s, "+"),
+                    tokenstream::KleeneOp::ZeroOrMore => word(&mut self.s, "*"),
+                    tokenstream::KleeneOp::OneOrMore => word(&mut self.s, "+"),
                 }
             }
         }
     }
 
-    pub fn print_tts(&mut self, tts: &[ast::TokenTree]) -> io::Result<()> {
-        self.ibox(0)?;
+    pub fn print_tts(&mut self, tts: &[tokenstream::TokenTree]) -> io::Result<()> {
+        try!(self.ibox(0));
         for (i, tt) in tts.iter().enumerate() {
             if i != 0 {
-                space(&mut self.s)?;
+                try!(space(&mut self.s));
             }
-            self.print_tt(tt)?;
+            try!(self.print_tt(tt));
         }
         self.end()
     }
 
     pub fn print_variant(&mut self, v: &ast::Variant) -> io::Result<()> {
-        self.head("")?;
+        try!(self.head(""));
         let generics = ast::Generics::default();
-        self.print_struct(&v.node.data, &generics, v.node.name, v.span, false)?;
+        try!(self.print_struct(&v.node.data, &generics, v.node.name, v.span, false));
         match v.node.disr_expr {
             Some(ref d) => {
-                space(&mut self.s)?;
-                self.word_space("=")?;
+                try!(space(&mut self.s));
+                try!(self.word_space("="));
                 self.print_expr(&d)
             }
             _ => Ok(())
@@ -1522,110 +1514,136 @@ impl<'a> State<'a> {
                       m.abi,
                       Some(ident),
                       &m.generics,
-                      None,
                       vis)
     }
 
     pub fn print_trait_item(&mut self, ti: &ast::TraitItem)
                             -> io::Result<()> {
-        self.ann.pre(self, NodeSubItem(ti.id))?;
-        self.hardbreak_if_not_bol()?;
-        self.maybe_print_comment(ti.span.lo)?;
-        self.print_outer_attributes(&ti.attrs)?;
+        try!(self.ann.pre(self, NodeSubItem(ti.id)));
+        try!(self.hardbreak_if_not_bol());
+        try!(self.maybe_print_comment(ti.span.lo));
+        try!(self.print_outer_attributes(&ti.attrs));
         match ti.node {
             ast::TraitItemKind::Const(ref ty, ref default) => {
-                self.print_associated_const(ti.ident, &ty,
+                try!(self.print_associated_const(ti.ident, &ty,
                                             default.as_ref().map(|expr| &**expr),
-                                            &ast::Visibility::Inherited)?;
+                                            &ast::Visibility::Inherited));
             }
             ast::TraitItemKind::Method(ref sig, ref body) => {
                 if body.is_some() {
-                    self.head("")?;
+                    try!(self.head(""));
                 }
-                self.print_method_sig(ti.ident, sig, &ast::Visibility::Inherited)?;
+                try!(self.print_method_sig(ti.ident, sig, &ast::Visibility::Inherited));
                 if let Some(ref body) = *body {
-                    self.nbsp()?;
-                    self.print_block_with_attrs(body, &ti.attrs)?;
+                    try!(self.nbsp());
+                    try!(self.print_block_with_attrs(body, &ti.attrs));
                 } else {
-                    word(&mut self.s, ";")?;
+                    try!(word(&mut self.s, ";"));
                 }
             }
             ast::TraitItemKind::Type(ref bounds, ref default) => {
-                self.print_associated_type(ti.ident, Some(bounds),
-                                           default.as_ref().map(|ty| &**ty))?;
+                try!(self.print_associated_type(ti.ident, Some(bounds),
+                                           default.as_ref().map(|ty| &**ty)));
+            }
+            ast::TraitItemKind::Macro(codemap::Spanned { ref node, .. }) => {
+                // code copied from ItemKind::Mac:
+                self.print_path(&node.path, false, 0)?;
+                word(&mut self.s, "! ")?;
+                self.cbox(INDENT_UNIT)?;
+                self.popen()?;
+                self.print_tts(&node.tts[..])?;
+                self.pclose()?;
+                word(&mut self.s, ";")?;
+                self.end()?
             }
         }
         self.ann.post(self, NodeSubItem(ti.id))
     }
 
     pub fn print_impl_item(&mut self, ii: &ast::ImplItem) -> io::Result<()> {
-        self.ann.pre(self, NodeSubItem(ii.id))?;
-        self.hardbreak_if_not_bol()?;
-        self.maybe_print_comment(ii.span.lo)?;
-        self.print_outer_attributes(&ii.attrs)?;
+        try!(self.ann.pre(self, NodeSubItem(ii.id)));
+        try!(self.hardbreak_if_not_bol());
+        try!(self.maybe_print_comment(ii.span.lo));
+        try!(self.print_outer_attributes(&ii.attrs));
         if let ast::Defaultness::Default = ii.defaultness {
-            self.word_nbsp("default")?;
+            try!(self.word_nbsp("default"));
         }
         match ii.node {
             ast::ImplItemKind::Const(ref ty, ref expr) => {
-                self.print_associated_const(ii.ident, &ty, Some(&expr), &ii.vis)?;
+                try!(self.print_associated_const(ii.ident, &ty, Some(&expr), &ii.vis));
             }
             ast::ImplItemKind::Method(ref sig, ref body) => {
-                self.head("")?;
-                self.print_method_sig(ii.ident, sig, &ii.vis)?;
-                self.nbsp()?;
-                self.print_block_with_attrs(body, &ii.attrs)?;
+                try!(self.head(""));
+                try!(self.print_method_sig(ii.ident, sig, &ii.vis));
+                try!(self.nbsp());
+                try!(self.print_block_with_attrs(body, &ii.attrs));
             }
             ast::ImplItemKind::Type(ref ty) => {
-                self.print_associated_type(ii.ident, None, Some(ty))?;
+                try!(self.print_associated_type(ii.ident, None, Some(ty)));
             }
             ast::ImplItemKind::Macro(codemap::Spanned { ref node, .. }) => {
                 // code copied from ItemKind::Mac:
-                self.print_path(&node.path, false, 0)?;
-                word(&mut self.s, "! ")?;
-                self.cbox(INDENT_UNIT)?;
-                self.popen()?;
-                self.print_tts(&node.tts[..])?;
-                self.pclose()?;
-                word(&mut self.s, ";")?;
-                self.end()?
+                try!(self.print_path(&node.path, false, 0));
+                try!(word(&mut self.s, "! "));
+                try!(self.cbox(INDENT_UNIT));
+                try!(self.popen());
+                try!(self.print_tts(&node.tts[..]));
+                try!(self.pclose());
+                try!(word(&mut self.s, ";"));
+                try!(self.end())
             }
         }
         self.ann.post(self, NodeSubItem(ii.id))
     }
 
     pub fn print_stmt(&mut self, st: &ast::Stmt) -> io::Result<()> {
-        self.maybe_print_comment(st.span.lo)?;
+        try!(self.maybe_print_comment(st.span.lo));
         match st.node {
-            ast::StmtKind::Decl(ref decl, _) => {
-                self.print_decl(&decl)?;
+            ast::StmtKind::Local(ref loc) => {
+                try!(self.print_outer_attributes(&loc.attrs));
+                try!(self.space_if_not_bol());
+                try!(self.ibox(INDENT_UNIT));
+                try!(self.word_nbsp("let"));
+
+                try!(self.ibox(INDENT_UNIT));
+                try!(self.print_local_decl(&loc));
+                try!(self.end());
+                if let Some(ref init) = loc.init {
+                    try!(self.nbsp());
+                    try!(self.word_space("="));
+                    try!(self.print_expr(&init));
+                }
+                try!(word(&mut self.s, ";"));
+                self.end()?;
             }
-            ast::StmtKind::Expr(ref expr, _) => {
-                self.space_if_not_bol()?;
-                self.print_expr_outer_attr_style(&expr, false)?;
+            ast::StmtKind::Item(ref item) => self.print_item(&item)?,
+            ast::StmtKind::Expr(ref expr) => {
+                try!(self.space_if_not_bol());
+                try!(self.print_expr_outer_attr_style(&expr, false));
+                if parse::classify::expr_requires_semi_to_be_stmt(expr) {
+                    try!(word(&mut self.s, ";"));
+                }
             }
-            ast::StmtKind::Semi(ref expr, _) => {
-                self.space_if_not_bol()?;
-                self.print_expr_outer_attr_style(&expr, false)?;
-                word(&mut self.s, ";")?;
+            ast::StmtKind::Semi(ref expr) => {
+                try!(self.space_if_not_bol());
+                try!(self.print_expr_outer_attr_style(&expr, false));
+                try!(word(&mut self.s, ";"));
             }
-            ast::StmtKind::Mac(ref mac, style, ref attrs) => {
-                self.space_if_not_bol()?;
-                self.print_outer_attributes(attrs.as_attr_slice())?;
+            ast::StmtKind::Mac(ref mac) => {
+                let (ref mac, style, ref attrs) = **mac;
+                try!(self.space_if_not_bol());
+                try!(self.print_outer_attributes(&attrs));
                 let delim = match style {
                     ast::MacStmtStyle::Braces => token::Brace,
                     _ => token::Paren
                 };
-                self.print_mac(&mac, delim)?;
+                try!(self.print_mac(&mac, delim));
                 match style {
                     ast::MacStmtStyle::Braces => {}
-                    _ => word(&mut self.s, ";")?,
+                    _ => try!(word(&mut self.s, ";")),
                 }
             }
         }
-        if parse::classify::stmt_ends_with_semi(&st.node) {
-            word(&mut self.s, ";")?;
-        }
         self.maybe_print_trailing_comment(st.span, None)
     }
 
@@ -1660,27 +1678,27 @@ impl<'a> State<'a> {
                                       attrs: &[ast::Attribute],
                                       close_box: bool) -> io::Result<()> {
         match blk.rules {
-            BlockCheckMode::Unsafe(..) => self.word_space("unsafe")?,
+            BlockCheckMode::Unsafe(..) => try!(self.word_space("unsafe")),
             BlockCheckMode::Default => ()
         }
-        self.maybe_print_comment(blk.span.lo)?;
-        self.ann.pre(self, NodeBlock(blk))?;
-        self.bopen()?;
+        try!(self.maybe_print_comment(blk.span.lo));
+        try!(self.ann.pre(self, NodeBlock(blk)));
+        try!(self.bopen());
 
-        self.print_inner_attributes(attrs)?;
+        try!(self.print_inner_attributes(attrs));
 
-        for st in &blk.stmts {
-            self.print_stmt(st)?;
-        }
-        match blk.expr {
-            Some(ref expr) => {
-                self.space_if_not_bol()?;
-                self.print_expr_outer_attr_style(&expr, false)?;
-                self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi))?;
+        for (i, st) in blk.stmts.iter().enumerate() {
+            match st.node {
+                ast::StmtKind::Expr(ref expr) if i == blk.stmts.len() - 1 => {
+                    try!(self.space_if_not_bol());
+                    try!(self.print_expr_outer_attr_style(&expr, false));
+                    try!(self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi)));
+                }
+                _ => try!(self.print_stmt(st)),
             }
-            _ => ()
         }
-        self.bclose_maybe_open(blk.span, indented, close_box)?;
+
+        try!(self.bclose_maybe_open(blk.span, indented, close_box));
         self.ann.post(self, NodeBlock(blk))
     }
 
@@ -1690,32 +1708,32 @@ impl<'a> State<'a> {
                 match _else.node {
                     // "another else-if"
                     ast::ExprKind::If(ref i, ref then, ref e) => {
-                        self.cbox(INDENT_UNIT - 1)?;
-                        self.ibox(0)?;
-                        word(&mut self.s, " else if ")?;
-                        self.print_expr(&i)?;
-                        space(&mut self.s)?;
-                        self.print_block(&then)?;
+                        try!(self.cbox(INDENT_UNIT - 1));
+                        try!(self.ibox(0));
+                        try!(word(&mut self.s, " else if "));
+                        try!(self.print_expr(&i));
+                        try!(space(&mut self.s));
+                        try!(self.print_block(&then));
                         self.print_else(e.as_ref().map(|e| &**e))
                     }
                     // "another else-if-let"
                     ast::ExprKind::IfLet(ref pat, ref expr, ref then, ref e) => {
-                        self.cbox(INDENT_UNIT - 1)?;
-                        self.ibox(0)?;
-                        word(&mut self.s, " else if let ")?;
-                        self.print_pat(&pat)?;
-                        space(&mut self.s)?;
-                        self.word_space("=")?;
-                        self.print_expr(&expr)?;
-                        space(&mut self.s)?;
-                        self.print_block(&then)?;
+                        try!(self.cbox(INDENT_UNIT - 1));
+                        try!(self.ibox(0));
+                        try!(word(&mut self.s, " else if let "));
+                        try!(self.print_pat(&pat));
+                        try!(space(&mut self.s));
+                        try!(self.word_space("="));
+                        try!(self.print_expr(&expr));
+                        try!(space(&mut self.s));
+                        try!(self.print_block(&then));
                         self.print_else(e.as_ref().map(|e| &**e))
                     }
                     // "final else"
                     ast::ExprKind::Block(ref b) => {
-                        self.cbox(INDENT_UNIT - 1)?;
-                        self.ibox(0)?;
-                        word(&mut self.s, " else ")?;
+                        try!(self.cbox(INDENT_UNIT - 1));
+                        try!(self.ibox(0));
+                        try!(word(&mut self.s, " else "));
                         self.print_block(&b)
                     }
                     // BLEAH, constraints would be great here
@@ -1730,38 +1748,38 @@ impl<'a> State<'a> {
 
     pub fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block,
                     elseopt: Option<&ast::Expr>) -> io::Result<()> {
-        self.head("if")?;
-        self.print_expr(test)?;
-        space(&mut self.s)?;
-        self.print_block(blk)?;
+        try!(self.head("if"));
+        try!(self.print_expr(test));
+        try!(space(&mut self.s));
+        try!(self.print_block(blk));
         self.print_else(elseopt)
     }
 
     pub fn print_if_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, blk: &ast::Block,
                         elseopt: Option<&ast::Expr>) -> io::Result<()> {
-        self.head("if let")?;
-        self.print_pat(pat)?;
-        space(&mut self.s)?;
-        self.word_space("=")?;
-        self.print_expr(expr)?;
-        space(&mut self.s)?;
-        self.print_block(blk)?;
+        try!(self.head("if let"));
+        try!(self.print_pat(pat));
+        try!(space(&mut self.s));
+        try!(self.word_space("="));
+        try!(self.print_expr(expr));
+        try!(space(&mut self.s));
+        try!(self.print_block(blk));
         self.print_else(elseopt)
     }
 
     pub fn print_mac(&mut self, m: &ast::Mac, delim: token::DelimToken)
                      -> io::Result<()> {
-        self.print_path(&m.node.path, false, 0)?;
-        word(&mut self.s, "!")?;
+        try!(self.print_path(&m.node.path, false, 0));
+        try!(word(&mut self.s, "!"));
         match delim {
-            token::Paren => self.popen()?,
-            token::Bracket => word(&mut self.s, "[")?,
+            token::Paren => try!(self.popen()),
+            token::Bracket => try!(word(&mut self.s, "[")),
             token::Brace => {
-                self.head("")?;
-                self.bopen()?;
+                try!(self.head(""));
+                try!(self.bopen());
             }
         }
-        self.print_tts(&m.node.tts)?;
+        try!(self.print_tts(&m.node.tts));
         match delim {
             token::Paren => self.pclose(),
             token::Bracket => word(&mut self.s, "]"),
@@ -1771,8 +1789,8 @@ impl<'a> State<'a> {
 
 
     fn print_call_post(&mut self, args: &[P<ast::Expr>]) -> io::Result<()> {
-        self.popen()?;
-        self.commasep_exprs(Inconsistent, args)?;
+        try!(self.popen());
+        try!(self.commasep_exprs(Inconsistent, args));
         self.pclose()
     }
 
@@ -1794,11 +1812,11 @@ impl<'a> State<'a> {
     pub fn print_expr_maybe_paren(&mut self, expr: &ast::Expr) -> io::Result<()> {
         let needs_par = needs_parentheses(expr);
         if needs_par {
-            self.popen()?;
+            try!(self.popen());
         }
-        self.print_expr(expr)?;
+        try!(self.print_expr(expr));
         if needs_par {
-            self.pclose()?;
+            try!(self.pclose());
         }
         Ok(())
     }
@@ -1806,19 +1824,19 @@ impl<'a> State<'a> {
     fn print_expr_in_place(&mut self,
                            place: &ast::Expr,
                            expr: &ast::Expr) -> io::Result<()> {
-        self.print_expr_maybe_paren(place)?;
-        space(&mut self.s)?;
-        self.word_space("<-")?;
+        try!(self.print_expr_maybe_paren(place));
+        try!(space(&mut self.s));
+        try!(self.word_space("<-"));
         self.print_expr_maybe_paren(expr)
     }
 
     fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>],
                       attrs: &[Attribute]) -> io::Result<()> {
-        self.ibox(INDENT_UNIT)?;
-        word(&mut self.s, "[")?;
-        self.print_inner_attributes_inline(attrs)?;
-        self.commasep_exprs(Inconsistent, &exprs[..])?;
-        word(&mut self.s, "]")?;
+        try!(self.ibox(INDENT_UNIT));
+        try!(word(&mut self.s, "["));
+        try!(self.print_inner_attributes_inline(attrs));
+        try!(self.commasep_exprs(Inconsistent, &exprs[..]));
+        try!(word(&mut self.s, "]"));
         self.end()
     }
 
@@ -1826,13 +1844,13 @@ impl<'a> State<'a> {
                          element: &ast::Expr,
                          count: &ast::Expr,
                          attrs: &[Attribute]) -> io::Result<()> {
-        self.ibox(INDENT_UNIT)?;
-        word(&mut self.s, "[")?;
-        self.print_inner_attributes_inline(attrs)?;
-        self.print_expr(element)?;
-        self.word_space(";")?;
-        self.print_expr(count)?;
-        word(&mut self.s, "]")?;
+        try!(self.ibox(INDENT_UNIT));
+        try!(word(&mut self.s, "["));
+        try!(self.print_inner_attributes_inline(attrs));
+        try!(self.print_expr(element));
+        try!(self.word_space(";"));
+        try!(self.print_expr(count));
+        try!(word(&mut self.s, "]"));
         self.end()
     }
 
@@ -1841,46 +1859,46 @@ impl<'a> State<'a> {
                          fields: &[ast::Field],
                          wth: &Option<P<ast::Expr>>,
                          attrs: &[Attribute]) -> io::Result<()> {
-        self.print_path(path, true, 0)?;
-        word(&mut self.s, "{")?;
-        self.print_inner_attributes_inline(attrs)?;
-        self.commasep_cmnt(
+        try!(self.print_path(path, true, 0));
+        try!(word(&mut self.s, "{"));
+        try!(self.print_inner_attributes_inline(attrs));
+        try!(self.commasep_cmnt(
             Consistent,
             &fields[..],
             |s, field| {
-                s.ibox(INDENT_UNIT)?;
-                s.print_ident(field.ident.node)?;
-                s.word_space(":")?;
-                s.print_expr(&field.expr)?;
+                try!(s.ibox(INDENT_UNIT));
+                try!(s.print_ident(field.ident.node));
+                try!(s.word_space(":"));
+                try!(s.print_expr(&field.expr));
                 s.end()
             },
-            |f| f.span)?;
+            |f| f.span));
         match *wth {
             Some(ref expr) => {
-                self.ibox(INDENT_UNIT)?;
+                try!(self.ibox(INDENT_UNIT));
                 if !fields.is_empty() {
-                    word(&mut self.s, ",")?;
-                    space(&mut self.s)?;
+                    try!(word(&mut self.s, ","));
+                    try!(space(&mut self.s));
                 }
-                word(&mut self.s, "..")?;
-                self.print_expr(&expr)?;
-                self.end()?;
+                try!(word(&mut self.s, ".."));
+                try!(self.print_expr(&expr));
+                try!(self.end());
             }
             _ => if !fields.is_empty() {
-                word(&mut self.s, ",")?
+                try!(word(&mut self.s, ","))
             }
         }
-        word(&mut self.s, "}")?;
+        try!(word(&mut self.s, "}"));
         Ok(())
     }
 
     fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>],
                       attrs: &[Attribute]) -> io::Result<()> {
-        self.popen()?;
-        self.print_inner_attributes_inline(attrs)?;
-        self.commasep_exprs(Inconsistent, &exprs[..])?;
+        try!(self.popen());
+        try!(self.print_inner_attributes_inline(attrs));
+        try!(self.commasep_exprs(Inconsistent, &exprs[..]));
         if exprs.len() == 1 {
-            word(&mut self.s, ",")?;
+            try!(word(&mut self.s, ","));
         }
         self.pclose()
     }
@@ -1888,7 +1906,7 @@ impl<'a> State<'a> {
     fn print_expr_call(&mut self,
                        func: &ast::Expr,
                        args: &[P<ast::Expr>]) -> io::Result<()> {
-        self.print_expr_maybe_paren(func)?;
+        try!(self.print_expr_maybe_paren(func));
         self.print_call_post(args)
     }
 
@@ -1897,14 +1915,14 @@ impl<'a> State<'a> {
                               tys: &[P<ast::Ty>],
                               args: &[P<ast::Expr>]) -> io::Result<()> {
         let base_args = &args[1..];
-        self.print_expr(&args[0])?;
-        word(&mut self.s, ".")?;
-        self.print_ident(ident.node)?;
+        try!(self.print_expr(&args[0]));
+        try!(word(&mut self.s, "."));
+        try!(self.print_ident(ident.node));
         if !tys.is_empty() {
-            word(&mut self.s, "::<")?;
-            self.commasep(Inconsistent, tys,
-                          |s, ty| s.print_type(&ty))?;
-            word(&mut self.s, ">")?;
+            try!(word(&mut self.s, "::<"));
+            try!(self.commasep(Inconsistent, tys,
+                          |s, ty| s.print_type(&ty)));
+            try!(word(&mut self.s, ">"));
         }
         self.print_call_post(base_args)
     }
@@ -1914,12 +1932,12 @@ impl<'a> State<'a> {
                          lhs: &ast::Expr,
                          rhs: &ast::Expr) -> io::Result<()> {
         if self.check_expr_bin_needs_paren(lhs, op) {
-            self.print_expr_maybe_paren(lhs)?;
+            try!(self.print_expr_maybe_paren(lhs));
         } else {
-            self.print_expr(lhs)?;
+            try!(self.print_expr(lhs));
         }
-        space(&mut self.s)?;
-        self.word_space(op.node.to_string())?;
+        try!(space(&mut self.s));
+        try!(self.word_space(op.node.to_string()));
         if self.check_expr_bin_needs_paren(rhs, op) {
             self.print_expr_maybe_paren(rhs)
         } else {
@@ -1930,15 +1948,15 @@ impl<'a> State<'a> {
     fn print_expr_unary(&mut self,
                         op: ast::UnOp,
                         expr: &ast::Expr) -> io::Result<()> {
-        word(&mut self.s, ast::UnOp::to_string(op))?;
+        try!(word(&mut self.s, ast::UnOp::to_string(op)));
         self.print_expr_maybe_paren(expr)
     }
 
     fn print_expr_addr_of(&mut self,
                           mutability: ast::Mutability,
                           expr: &ast::Expr) -> io::Result<()> {
-        word(&mut self.s, "&")?;
-        self.print_mutability(mutability)?;
+        try!(word(&mut self.s, "&"));
+        try!(self.print_mutability(mutability));
         self.print_expr_maybe_paren(expr)
     }
 
@@ -1949,290 +1967,287 @@ impl<'a> State<'a> {
     fn print_expr_outer_attr_style(&mut self,
                                   expr: &ast::Expr,
                                   is_inline: bool) -> io::Result<()> {
-        self.maybe_print_comment(expr.span.lo)?;
+        try!(self.maybe_print_comment(expr.span.lo));
 
-        let attrs = expr.attrs.as_attr_slice();
+        let attrs = &expr.attrs;
         if is_inline {
-            self.print_outer_attributes_inline(attrs)?;
+            try!(self.print_outer_attributes_inline(attrs));
         } else {
-            self.print_outer_attributes(attrs)?;
+            try!(self.print_outer_attributes(attrs));
         }
 
-        self.ibox(INDENT_UNIT)?;
-        self.ann.pre(self, NodeExpr(expr))?;
+        try!(self.ibox(INDENT_UNIT));
+        try!(self.ann.pre(self, NodeExpr(expr)));
         match expr.node {
             ast::ExprKind::Box(ref expr) => {
-                self.word_space("box")?;
-                self.print_expr(expr)?;
+                try!(self.word_space("box"));
+                try!(self.print_expr(expr));
             }
             ast::ExprKind::InPlace(ref place, ref expr) => {
-                self.print_expr_in_place(place, expr)?;
+                try!(self.print_expr_in_place(place, expr));
             }
             ast::ExprKind::Vec(ref exprs) => {
-                self.print_expr_vec(&exprs[..], attrs)?;
+                try!(self.print_expr_vec(&exprs[..], attrs));
             }
             ast::ExprKind::Repeat(ref element, ref count) => {
-                self.print_expr_repeat(&element, &count, attrs)?;
+                try!(self.print_expr_repeat(&element, &count, attrs));
             }
             ast::ExprKind::Struct(ref path, ref fields, ref wth) => {
-                self.print_expr_struct(path, &fields[..], wth, attrs)?;
+                try!(self.print_expr_struct(path, &fields[..], wth, attrs));
             }
             ast::ExprKind::Tup(ref exprs) => {
-                self.print_expr_tup(&exprs[..], attrs)?;
+                try!(self.print_expr_tup(&exprs[..], attrs));
             }
             ast::ExprKind::Call(ref func, ref args) => {
-                self.print_expr_call(&func, &args[..])?;
+                try!(self.print_expr_call(&func, &args[..]));
             }
             ast::ExprKind::MethodCall(ident, ref tys, ref args) => {
-                self.print_expr_method_call(ident, &tys[..], &args[..])?;
+                try!(self.print_expr_method_call(ident, &tys[..], &args[..]));
             }
             ast::ExprKind::Binary(op, ref lhs, ref rhs) => {
-                self.print_expr_binary(op, &lhs, &rhs)?;
+                try!(self.print_expr_binary(op, &lhs, &rhs));
             }
             ast::ExprKind::Unary(op, ref expr) => {
-                self.print_expr_unary(op, &expr)?;
+                try!(self.print_expr_unary(op, &expr));
             }
             ast::ExprKind::AddrOf(m, ref expr) => {
-                self.print_expr_addr_of(m, &expr)?;
+                try!(self.print_expr_addr_of(m, &expr));
             }
             ast::ExprKind::Lit(ref lit) => {
-                self.print_literal(&lit)?;
+                try!(self.print_literal(&lit));
             }
             ast::ExprKind::Cast(ref expr, ref ty) => {
                 if let ast::ExprKind::Cast(..) = expr.node {
-                    self.print_expr(&expr)?;
+                    try!(self.print_expr(&expr));
                 } else {
-                    self.print_expr_maybe_paren(&expr)?;
+                    try!(self.print_expr_maybe_paren(&expr));
                 }
-                space(&mut self.s)?;
-                self.word_space("as")?;
-                self.print_type(&ty)?;
+                try!(space(&mut self.s));
+                try!(self.word_space("as"));
+                try!(self.print_type(&ty));
             }
             ast::ExprKind::Type(ref expr, ref ty) => {
-                self.print_expr(&expr)?;
-                self.word_space(":")?;
-                self.print_type(&ty)?;
+                try!(self.print_expr(&expr));
+                try!(self.word_space(":"));
+                try!(self.print_type(&ty));
             }
             ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
-                self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e))?;
+                try!(self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e)));
             }
             ast::ExprKind::IfLet(ref pat, ref expr, ref blk, ref elseopt) => {
-                self.print_if_let(&pat, &expr, &blk, elseopt.as_ref().map(|e| &**e))?;
+                try!(self.print_if_let(&pat, &expr, &blk, elseopt.as_ref().map(|e| &**e)));
             }
             ast::ExprKind::While(ref test, ref blk, opt_ident) => {
                 if let Some(ident) = opt_ident {
-                    self.print_ident(ident)?;
-                    self.word_space(":")?;
+                    try!(self.print_ident(ident.node));
+                    try!(self.word_space(":"));
                 }
-                self.head("while")?;
-                self.print_expr(&test)?;
-                space(&mut self.s)?;
-                self.print_block_with_attrs(&blk, attrs)?;
+                try!(self.head("while"));
+                try!(self.print_expr(&test));
+                try!(space(&mut self.s));
+                try!(self.print_block_with_attrs(&blk, attrs));
             }
             ast::ExprKind::WhileLet(ref pat, ref expr, ref blk, opt_ident) => {
                 if let Some(ident) = opt_ident {
-                    self.print_ident(ident)?;
-                    self.word_space(":")?;
+                    try!(self.print_ident(ident.node));
+                    try!(self.word_space(":"));
                 }
-                self.head("while let")?;
-                self.print_pat(&pat)?;
-                space(&mut self.s)?;
-                self.word_space("=")?;
-                self.print_expr(&expr)?;
-                space(&mut self.s)?;
-                self.print_block_with_attrs(&blk, attrs)?;
+                try!(self.head("while let"));
+                try!(self.print_pat(&pat));
+                try!(space(&mut self.s));
+                try!(self.word_space("="));
+                try!(self.print_expr(&expr));
+                try!(space(&mut self.s));
+                try!(self.print_block_with_attrs(&blk, attrs));
             }
             ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_ident) => {
                 if let Some(ident) = opt_ident {
-                    self.print_ident(ident)?;
-                    self.word_space(":")?;
+                    try!(self.print_ident(ident.node));
+                    try!(self.word_space(":"));
                 }
-                self.head("for")?;
-                self.print_pat(&pat)?;
-                space(&mut self.s)?;
-                self.word_space("in")?;
-                self.print_expr(&iter)?;
-                space(&mut self.s)?;
-                self.print_block_with_attrs(&blk, attrs)?;
+                try!(self.head("for"));
+                try!(self.print_pat(&pat));
+                try!(space(&mut self.s));
+                try!(self.word_space("in"));
+                try!(self.print_expr(&iter));
+                try!(space(&mut self.s));
+                try!(self.print_block_with_attrs(&blk, attrs));
             }
             ast::ExprKind::Loop(ref blk, opt_ident) => {
                 if let Some(ident) = opt_ident {
-                    self.print_ident(ident)?;
-                    self.word_space(":")?;
+                    try!(self.print_ident(ident.node));
+                    try!(self.word_space(":"));
                 }
-                self.head("loop")?;
-                space(&mut self.s)?;
-                self.print_block_with_attrs(&blk, attrs)?;
+                try!(self.head("loop"));
+                try!(space(&mut self.s));
+                try!(self.print_block_with_attrs(&blk, attrs));
             }
             ast::ExprKind::Match(ref expr, ref arms) => {
-                self.cbox(INDENT_UNIT)?;
-                self.ibox(4)?;
-                self.word_nbsp("match")?;
-                self.print_expr(&expr)?;
-                space(&mut self.s)?;
-                self.bopen()?;
-                self.print_inner_attributes_no_trailing_hardbreak(attrs)?;
+                try!(self.cbox(INDENT_UNIT));
+                try!(self.ibox(4));
+                try!(self.word_nbsp("match"));
+                try!(self.print_expr(&expr));
+                try!(space(&mut self.s));
+                try!(self.bopen());
+                try!(self.print_inner_attributes_no_trailing_hardbreak(attrs));
                 for arm in arms {
-                    self.print_arm(arm)?;
+                    try!(self.print_arm(arm));
                 }
-                self.bclose_(expr.span, INDENT_UNIT)?;
+                try!(self.bclose_(expr.span, INDENT_UNIT));
             }
             ast::ExprKind::Closure(capture_clause, ref decl, ref body, _) => {
-                self.print_capture_clause(capture_clause)?;
+                try!(self.print_capture_clause(capture_clause));
 
-                self.print_fn_block_args(&decl)?;
-                space(&mut self.s)?;
+                try!(self.print_fn_block_args(&decl));
+                try!(space(&mut self.s));
 
                 let default_return = match decl.output {
                     ast::FunctionRetTy::Default(..) => true,
                     _ => false
                 };
 
-                if !default_return || !body.stmts.is_empty() || body.expr.is_none() {
-                    self.print_block_unclosed(&body)?;
-                } else {
-                    // we extract the block, so as not to create another set of boxes
-                    let i_expr = body.expr.as_ref().unwrap();
-                    match i_expr.node {
-                        ast::ExprKind::Block(ref blk) => {
-                            self.print_block_unclosed_with_attrs(
-                                &blk,
-                                i_expr.attrs.as_attr_slice())?;
-                        }
-                        _ => {
+                match body.stmts.last().map(|stmt| &stmt.node) {
+                    Some(&ast::StmtKind::Expr(ref i_expr)) if default_return &&
+                                                              body.stmts.len() == 1 => {
+                        // we extract the block, so as not to create another set of boxes
+                        if let ast::ExprKind::Block(ref blk) = i_expr.node {
+                            try!(self.print_block_unclosed_with_attrs(&blk, &i_expr.attrs));
+                        } else {
                             // this is a bare expression
-                            self.print_expr(&i_expr)?;
-                            self.end()?; // need to close a box
+                            try!(self.print_expr(&i_expr));
+                            try!(self.end()); // need to close a box
                         }
                     }
+                    _ => try!(self.print_block_unclosed(&body)),
                 }
+
                 // a box will be closed by print_expr, but we didn't want an overall
                 // wrapper so we closed the corresponding opening. so create an
                 // empty box to satisfy the close.
-                self.ibox(0)?;
+                try!(self.ibox(0));
             }
             ast::ExprKind::Block(ref blk) => {
                 // containing cbox, will be closed by print-block at }
-                self.cbox(INDENT_UNIT)?;
+                try!(self.cbox(INDENT_UNIT));
                 // head-box, will be closed by print-block after {
-                self.ibox(0)?;
-                self.print_block_with_attrs(&blk, attrs)?;
+                try!(self.ibox(0));
+                try!(self.print_block_with_attrs(&blk, attrs));
             }
             ast::ExprKind::Assign(ref lhs, ref rhs) => {
-                self.print_expr(&lhs)?;
-                space(&mut self.s)?;
-                self.word_space("=")?;
-                self.print_expr(&rhs)?;
+                try!(self.print_expr(&lhs));
+                try!(space(&mut self.s));
+                try!(self.word_space("="));
+                try!(self.print_expr(&rhs));
             }
             ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => {
-                self.print_expr(&lhs)?;
-                space(&mut self.s)?;
-                word(&mut self.s, op.node.to_string())?;
-                self.word_space("=")?;
-                self.print_expr(&rhs)?;
+                try!(self.print_expr(&lhs));
+                try!(space(&mut self.s));
+                try!(word(&mut self.s, op.node.to_string()));
+                try!(self.word_space("="));
+                try!(self.print_expr(&rhs));
             }
             ast::ExprKind::Field(ref expr, id) => {
-                self.print_expr(&expr)?;
-                word(&mut self.s, ".")?;
-                self.print_ident(id.node)?;
+                try!(self.print_expr(&expr));
+                try!(word(&mut self.s, "."));
+                try!(self.print_ident(id.node));
             }
             ast::ExprKind::TupField(ref expr, id) => {
-                self.print_expr(&expr)?;
-                word(&mut self.s, ".")?;
-                self.print_usize(id.node)?;
+                try!(self.print_expr(&expr));
+                try!(word(&mut self.s, "."));
+                try!(self.print_usize(id.node));
             }
             ast::ExprKind::Index(ref expr, ref index) => {
-                self.print_expr(&expr)?;
-                word(&mut self.s, "[")?;
-                self.print_expr(&index)?;
-                word(&mut self.s, "]")?;
+                try!(self.print_expr(&expr));
+                try!(word(&mut self.s, "["));
+                try!(self.print_expr(&index));
+                try!(word(&mut self.s, "]"));
             }
             ast::ExprKind::Range(ref start, ref end, limits) => {
                 if let &Some(ref e) = start {
-                    self.print_expr(&e)?;
+                    try!(self.print_expr(&e));
                 }
                 if limits == ast::RangeLimits::HalfOpen {
-                    word(&mut self.s, "..")?;
+                    try!(word(&mut self.s, ".."));
                 } else {
-                    word(&mut self.s, "...")?;
+                    try!(word(&mut self.s, "..."));
                 }
                 if let &Some(ref e) = end {
-                    self.print_expr(&e)?;
+                    try!(self.print_expr(&e));
                 }
             }
             ast::ExprKind::Path(None, ref path) => {
-                self.print_path(path, true, 0)?
+                try!(self.print_path(path, true, 0))
             }
             ast::ExprKind::Path(Some(ref qself), ref path) => {
-                self.print_qpath(path, qself, true)?
+                try!(self.print_qpath(path, qself, true))
             }
             ast::ExprKind::Break(opt_ident) => {
-                word(&mut self.s, "break")?;
-                space(&mut self.s)?;
+                try!(word(&mut self.s, "break"));
+                try!(space(&mut self.s));
                 if let Some(ident) = opt_ident {
-                    self.print_ident(ident.node)?;
-                    space(&mut self.s)?;
+                    try!(self.print_ident(ident.node));
+                    try!(space(&mut self.s));
                 }
             }
-            ast::ExprKind::Again(opt_ident) => {
-                word(&mut self.s, "continue")?;
-                space(&mut self.s)?;
+            ast::ExprKind::Continue(opt_ident) => {
+                try!(word(&mut self.s, "continue"));
+                try!(space(&mut self.s));
                 if let Some(ident) = opt_ident {
-                    self.print_ident(ident.node)?;
-                    space(&mut self.s)?
+                    try!(self.print_ident(ident.node));
+                    try!(space(&mut self.s))
                 }
             }
             ast::ExprKind::Ret(ref result) => {
-                word(&mut self.s, "return")?;
+                try!(word(&mut self.s, "return"));
                 match *result {
                     Some(ref expr) => {
-                        word(&mut self.s, " ")?;
-                        self.print_expr(&expr)?;
+                        try!(word(&mut self.s, " "));
+                        try!(self.print_expr(&expr));
                     }
                     _ => ()
                 }
             }
             ast::ExprKind::InlineAsm(ref a) => {
-                word(&mut self.s, "asm!")?;
-                self.popen()?;
-                self.print_string(&a.asm, a.asm_str_style)?;
-                self.word_space(":")?;
+                try!(word(&mut self.s, "asm!"));
+                try!(self.popen());
+                try!(self.print_string(&a.asm, a.asm_str_style));
+                try!(self.word_space(":"));
 
-                self.commasep(Inconsistent, &a.outputs,
+                try!(self.commasep(Inconsistent, &a.outputs,
                                    |s, out| {
                     let mut ch = out.constraint.chars();
                     match ch.next() {
                         Some('=') if out.is_rw => {
-                            s.print_string(&format!("+{}", ch.as_str()),
-                                           ast::StrStyle::Cooked)?
+                            try!(s.print_string(&format!("+{}", ch.as_str()),
+                                           ast::StrStyle::Cooked))
                         }
-                        _ => s.print_string(&out.constraint,
-                                            ast::StrStyle::Cooked)?
+                        _ => try!(s.print_string(&out.constraint,
+                                            ast::StrStyle::Cooked))
                     }
-                    s.popen()?;
-                    s.print_expr(&out.expr)?;
-                    s.pclose()?;
+                    try!(s.popen());
+                    try!(s.print_expr(&out.expr));
+                    try!(s.pclose());
                     Ok(())
-                })?;
-                space(&mut self.s)?;
-                self.word_space(":")?;
+                }));
+                try!(space(&mut self.s));
+                try!(self.word_space(":"));
 
-                self.commasep(Inconsistent, &a.inputs,
+                try!(self.commasep(Inconsistent, &a.inputs,
                                    |s, &(ref co, ref o)| {
-                    s.print_string(&co, ast::StrStyle::Cooked)?;
-                    s.popen()?;
-                    s.print_expr(&o)?;
-                    s.pclose()?;
+                    try!(s.print_string(&co, ast::StrStyle::Cooked));
+                    try!(s.popen());
+                    try!(s.print_expr(&o));
+                    try!(s.pclose());
                     Ok(())
-                })?;
-                space(&mut self.s)?;
-                self.word_space(":")?;
+                }));
+                try!(space(&mut self.s));
+                try!(self.word_space(":"));
 
-                self.commasep(Inconsistent, &a.clobbers,
+                try!(self.commasep(Inconsistent, &a.clobbers,
                                    |s, co| {
-                    s.print_string(&co, ast::StrStyle::Cooked)?;
+                    try!(s.print_string(&co, ast::StrStyle::Cooked));
                     Ok(())
-                })?;
+                }));
 
                 let mut options = vec!();
                 if a.volatile {
@@ -2246,67 +2261,44 @@ impl<'a> State<'a> {
                 }
 
                 if !options.is_empty() {
-                    space(&mut self.s)?;
-                    self.word_space(":")?;
-                    self.commasep(Inconsistent, &options,
+                    try!(space(&mut self.s));
+                    try!(self.word_space(":"));
+                    try!(self.commasep(Inconsistent, &options,
                                   |s, &co| {
-                                      s.print_string(co, ast::StrStyle::Cooked)?;
+                                      try!(s.print_string(co, ast::StrStyle::Cooked));
                                       Ok(())
-                                  })?;
+                                  }));
                 }
 
-                self.pclose()?;
+                try!(self.pclose());
             }
-            ast::ExprKind::Mac(ref m) => self.print_mac(m, token::Paren)?,
+            ast::ExprKind::Mac(ref m) => try!(self.print_mac(m, token::Paren)),
             ast::ExprKind::Paren(ref e) => {
-                self.popen()?;
-                self.print_inner_attributes_inline(attrs)?;
-                self.print_expr(&e)?;
-                self.pclose()?;
+                try!(self.popen());
+                try!(self.print_inner_attributes_inline(attrs));
+                try!(self.print_expr(&e));
+                try!(self.pclose());
             },
             ast::ExprKind::Try(ref e) => {
-                self.print_expr(e)?;
-                word(&mut self.s, "?")?
+                try!(self.print_expr(e));
+                try!(word(&mut self.s, "?"))
             }
         }
-        self.ann.post(self, NodeExpr(expr))?;
+        try!(self.ann.post(self, NodeExpr(expr)));
         self.end()
     }
 
     pub fn print_local_decl(&mut self, loc: &ast::Local) -> io::Result<()> {
-        self.print_pat(&loc.pat)?;
+        try!(self.print_pat(&loc.pat));
         if let Some(ref ty) = loc.ty {
-            self.word_space(":")?;
-            self.print_type(&ty)?;
+            try!(self.word_space(":"));
+            try!(self.print_type(&ty));
         }
         Ok(())
     }
 
-    pub fn print_decl(&mut self, decl: &ast::Decl) -> io::Result<()> {
-        self.maybe_print_comment(decl.span.lo)?;
-        match decl.node {
-            ast::DeclKind::Local(ref loc) => {
-                self.print_outer_attributes(loc.attrs.as_attr_slice())?;
-                self.space_if_not_bol()?;
-                self.ibox(INDENT_UNIT)?;
-                self.word_nbsp("let")?;
-
-                self.ibox(INDENT_UNIT)?;
-                self.print_local_decl(&loc)?;
-                self.end()?;
-                if let Some(ref init) = loc.init {
-                    self.nbsp()?;
-                    self.word_space("=")?;
-                    self.print_expr(&init)?;
-                }
-                self.end()
-            }
-            ast::DeclKind::Item(ref item) => self.print_item(&item)
-        }
-    }
-
     pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> {
-        word(&mut self.s, &ident.name.as_str())?;
+        try!(word(&mut self.s, &ident.name.as_str()));
         self.ann.post(self, NodeIdent(&ident))
     }
 
@@ -2315,15 +2307,15 @@ impl<'a> State<'a> {
     }
 
     pub fn print_name(&mut self, name: ast::Name) -> io::Result<()> {
-        word(&mut self.s, &name.as_str())?;
+        try!(word(&mut self.s, &name.as_str()));
         self.ann.post(self, NodeName(&name))
     }
 
     pub fn print_for_decl(&mut self, loc: &ast::Local,
                           coll: &ast::Expr) -> io::Result<()> {
-        self.print_local_decl(loc)?;
-        space(&mut self.s)?;
-        self.word_space("in")?;
+        try!(self.print_local_decl(loc));
+        try!(space(&mut self.s));
+        try!(self.word_space("in"));
         self.print_expr(coll)
     }
 
@@ -2333,19 +2325,19 @@ impl<'a> State<'a> {
                   depth: usize)
                   -> io::Result<()>
     {
-        self.maybe_print_comment(path.span.lo)?;
+        try!(self.maybe_print_comment(path.span.lo));
 
         let mut first = !path.global;
         for segment in &path.segments[..path.segments.len()-depth] {
             if first {
                 first = false
             } else {
-                word(&mut self.s, "::")?
+                try!(word(&mut self.s, "::"))
             }
 
-            self.print_ident(segment.identifier)?;
+            try!(self.print_ident(segment.identifier));
 
-            self.print_path_parameters(&segment.parameters, colons_before_params)?;
+            try!(self.print_path_parameters(&segment.parameters, colons_before_params));
         }
 
         Ok(())
@@ -2357,18 +2349,18 @@ impl<'a> State<'a> {
                    colons_before_params: bool)
                    -> io::Result<()>
     {
-        word(&mut self.s, "<")?;
-        self.print_type(&qself.ty)?;
+        try!(word(&mut self.s, "<"));
+        try!(self.print_type(&qself.ty));
         if qself.position > 0 {
-            space(&mut self.s)?;
-            self.word_space("as")?;
+            try!(space(&mut self.s));
+            try!(self.word_space("as"));
             let depth = path.segments.len() - qself.position;
-            self.print_path(&path, false, depth)?;
+            try!(self.print_path(&path, false, depth));
         }
-        word(&mut self.s, ">")?;
-        word(&mut self.s, "::")?;
+        try!(word(&mut self.s, ">"));
+        try!(word(&mut self.s, "::"));
         let item_segment = path.segments.last().unwrap();
-        self.print_ident(item_segment.identifier)?;
+        try!(self.print_ident(item_segment.identifier));
         self.print_path_parameters(&item_segment.parameters, colons_before_params)
     }
 
@@ -2382,61 +2374,61 @@ impl<'a> State<'a> {
         }
 
         if colons_before_params {
-            word(&mut self.s, "::")?
+            try!(word(&mut self.s, "::"))
         }
 
         match *parameters {
             ast::PathParameters::AngleBracketed(ref data) => {
-                word(&mut self.s, "<")?;
+                try!(word(&mut self.s, "<"));
 
                 let mut comma = false;
                 for lifetime in &data.lifetimes {
                     if comma {
-                        self.word_space(",")?
+                        try!(self.word_space(","))
                     }
-                    self.print_lifetime(lifetime)?;
+                    try!(self.print_lifetime(lifetime));
                     comma = true;
                 }
 
                 if !data.types.is_empty() {
                     if comma {
-                        self.word_space(",")?
+                        try!(self.word_space(","))
                     }
-                    self.commasep(
+                    try!(self.commasep(
                         Inconsistent,
                         &data.types,
-                        |s, ty| s.print_type(&ty))?;
+                        |s, ty| s.print_type(&ty)));
                         comma = true;
                 }
 
                 for binding in data.bindings.iter() {
                     if comma {
-                        self.word_space(",")?
+                        try!(self.word_space(","))
                     }
-                    self.print_ident(binding.ident)?;
-                    space(&mut self.s)?;
-                    self.word_space("=")?;
-                    self.print_type(&binding.ty)?;
+                    try!(self.print_ident(binding.ident));
+                    try!(space(&mut self.s));
+                    try!(self.word_space("="));
+                    try!(self.print_type(&binding.ty));
                     comma = true;
                 }
 
-                word(&mut self.s, ">")?
+                try!(word(&mut self.s, ">"))
             }
 
             ast::PathParameters::Parenthesized(ref data) => {
-                word(&mut self.s, "(")?;
-                self.commasep(
+                try!(word(&mut self.s, "("));
+                try!(self.commasep(
                     Inconsistent,
                     &data.inputs,
-                    |s, ty| s.print_type(&ty))?;
-                word(&mut self.s, ")")?;
+                    |s, ty| s.print_type(&ty)));
+                try!(word(&mut self.s, ")"));
 
                 match data.output {
                     None => { }
                     Some(ref ty) => {
-                        self.space_if_not_bol()?;
-                        self.word_space("->")?;
-                        self.print_type(&ty)?;
+                        try!(self.space_if_not_bol());
+                        try!(self.word_space("->"));
+                        try!(self.print_type(&ty));
                     }
                 }
             }
@@ -2446,120 +2438,133 @@ impl<'a> State<'a> {
     }
 
     pub fn print_pat(&mut self, pat: &ast::Pat) -> io::Result<()> {
-        self.maybe_print_comment(pat.span.lo)?;
-        self.ann.pre(self, NodePat(pat))?;
+        try!(self.maybe_print_comment(pat.span.lo));
+        try!(self.ann.pre(self, NodePat(pat)));
         /* Pat isn't normalized, but the beauty of it
          is that it doesn't matter */
         match pat.node {
-            PatKind::Wild => word(&mut self.s, "_")?,
+            PatKind::Wild => try!(word(&mut self.s, "_")),
             PatKind::Ident(binding_mode, ref path1, ref sub) => {
                 match binding_mode {
                     ast::BindingMode::ByRef(mutbl) => {
-                        self.word_nbsp("ref")?;
-                        self.print_mutability(mutbl)?;
+                        try!(self.word_nbsp("ref"));
+                        try!(self.print_mutability(mutbl));
                     }
                     ast::BindingMode::ByValue(ast::Mutability::Immutable) => {}
                     ast::BindingMode::ByValue(ast::Mutability::Mutable) => {
-                        self.word_nbsp("mut")?;
+                        try!(self.word_nbsp("mut"));
                     }
                 }
-                self.print_ident(path1.node)?;
-                match *sub {
-                    Some(ref p) => {
-                        word(&mut self.s, "@")?;
-                        self.print_pat(&p)?;
+                try!(self.print_ident(path1.node));
+                if let Some(ref p) = *sub {
+                    try!(word(&mut self.s, "@"));
+                    try!(self.print_pat(&p));
+                }
+            }
+            PatKind::TupleStruct(ref path, ref elts, ddpos) => {
+                try!(self.print_path(path, true, 0));
+                try!(self.popen());
+                if let Some(ddpos) = ddpos {
+                    try!(self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)));
+                    if ddpos != 0 {
+                        try!(self.word_space(","));
                     }
-                    None => ()
-                }
-            }
-            PatKind::TupleStruct(ref path, ref args_) => {
-                self.print_path(path, true, 0)?;
-                match *args_ {
-                    None => word(&mut self.s, "(..)")?,
-                    Some(ref args) => {
-                        self.popen()?;
-                        self.commasep(Inconsistent, &args[..],
-                                          |s, p| s.print_pat(&p))?;
-                        self.pclose()?;
+                    try!(word(&mut self.s, ".."));
+                    if ddpos != elts.len() {
+                        try!(word(&mut self.s, ","));
+                        try!(self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)));
                     }
+                } else {
+                    try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
                 }
+                try!(self.pclose());
             }
-            PatKind::Path(ref path) => {
-                self.print_path(path, true, 0)?;
+            PatKind::Path(None, ref path) => {
+                try!(self.print_path(path, true, 0));
             }
-            PatKind::QPath(ref qself, ref path) => {
-                self.print_qpath(path, qself, false)?;
+            PatKind::Path(Some(ref qself), ref path) => {
+                try!(self.print_qpath(path, qself, false));
             }
             PatKind::Struct(ref path, ref fields, etc) => {
-                self.print_path(path, true, 0)?;
-                self.nbsp()?;
-                self.word_space("{")?;
-                self.commasep_cmnt(
+                try!(self.print_path(path, true, 0));
+                try!(self.nbsp());
+                try!(self.word_space("{"));
+                try!(self.commasep_cmnt(
                     Consistent, &fields[..],
                     |s, f| {
-                        s.cbox(INDENT_UNIT)?;
+                        try!(s.cbox(INDENT_UNIT));
                         if !f.node.is_shorthand {
-                            s.print_ident(f.node.ident)?;
-                            s.word_nbsp(":")?;
+                            try!(s.print_ident(f.node.ident));
+                            try!(s.word_nbsp(":"));
                         }
-                        s.print_pat(&f.node.pat)?;
+                        try!(s.print_pat(&f.node.pat));
                         s.end()
                     },
-                    |f| f.node.pat.span)?;
+                    |f| f.node.pat.span));
                 if etc {
-                    if !fields.is_empty() { self.word_space(",")?; }
-                    word(&mut self.s, "..")?;
-                }
-                space(&mut self.s)?;
-                word(&mut self.s, "}")?;
-            }
-            PatKind::Tup(ref elts) => {
-                self.popen()?;
-                self.commasep(Inconsistent,
-                                   &elts[..],
-                                   |s, p| s.print_pat(&p))?;
-                if elts.len() == 1 {
-                    word(&mut self.s, ",")?;
+                    if !fields.is_empty() { try!(self.word_space(",")); }
+                    try!(word(&mut self.s, ".."));
+                }
+                try!(space(&mut self.s));
+                try!(word(&mut self.s, "}"));
+            }
+            PatKind::Tuple(ref elts, ddpos) => {
+                try!(self.popen());
+                if let Some(ddpos) = ddpos {
+                    try!(self.commasep(Inconsistent, &elts[..ddpos], |s, p| s.print_pat(&p)));
+                    if ddpos != 0 {
+                        try!(self.word_space(","));
+                    }
+                    try!(word(&mut self.s, ".."));
+                    if ddpos != elts.len() {
+                        try!(word(&mut self.s, ","));
+                        try!(self.commasep(Inconsistent, &elts[ddpos..], |s, p| s.print_pat(&p)));
+                    }
+                } else {
+                    try!(self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(&p)));
+                    if elts.len() == 1 {
+                        try!(word(&mut self.s, ","));
+                    }
                 }
-                self.pclose()?;
+                try!(self.pclose());
             }
             PatKind::Box(ref inner) => {
-                word(&mut self.s, "box ")?;
-                self.print_pat(&inner)?;
+                try!(word(&mut self.s, "box "));
+                try!(self.print_pat(&inner));
             }
             PatKind::Ref(ref inner, mutbl) => {
-                word(&mut self.s, "&")?;
+                try!(word(&mut self.s, "&"));
                 if mutbl == ast::Mutability::Mutable {
-                    word(&mut self.s, "mut ")?;
+                    try!(word(&mut self.s, "mut "));
                 }
-                self.print_pat(&inner)?;
+                try!(self.print_pat(&inner));
             }
-            PatKind::Lit(ref e) => self.print_expr(&**e)?,
+            PatKind::Lit(ref e) => try!(self.print_expr(&**e)),
             PatKind::Range(ref begin, ref end) => {
-                self.print_expr(&begin)?;
-                space(&mut self.s)?;
-                word(&mut self.s, "...")?;
-                self.print_expr(&end)?;
+                try!(self.print_expr(&begin));
+                try!(space(&mut self.s));
+                try!(word(&mut self.s, "..."));
+                try!(self.print_expr(&end));
             }
             PatKind::Vec(ref before, ref slice, ref after) => {
-                word(&mut self.s, "[")?;
-                self.commasep(Inconsistent,
+                try!(word(&mut self.s, "["));
+                try!(self.commasep(Inconsistent,
                                    &before[..],
-                                   |s, p| s.print_pat(&p))?;
+                                   |s, p| s.print_pat(&p)));
                 if let Some(ref p) = *slice {
-                    if !before.is_empty() { self.word_space(",")?; }
+                    if !before.is_empty() { try!(self.word_space(",")); }
                     if p.node != PatKind::Wild {
-                        self.print_pat(&p)?;
+                        try!(self.print_pat(&p));
                     }
-                    word(&mut self.s, "..")?;
-                    if !after.is_empty() { self.word_space(",")?; }
+                    try!(word(&mut self.s, ".."));
+                    if !after.is_empty() { try!(self.word_space(",")); }
                 }
-                self.commasep(Inconsistent,
+                try!(self.commasep(Inconsistent,
                                    &after[..],
-                                   |s, p| s.print_pat(&p))?;
-                word(&mut self.s, "]")?;
+                                   |s, p| s.print_pat(&p)));
+                try!(word(&mut self.s, "]"));
             }
-            PatKind::Mac(ref m) => self.print_mac(m, token::Paren)?,
+            PatKind::Mac(ref m) => try!(self.print_mac(m, token::Paren)),
         }
         self.ann.post(self, NodePat(pat))
     }
@@ -2568,71 +2573,67 @@ impl<'a> State<'a> {
         // I have no idea why this check is necessary, but here it
         // is :(
         if arm.attrs.is_empty() {
-            space(&mut self.s)?;
+            try!(space(&mut self.s));
         }
-        self.cbox(INDENT_UNIT)?;
-        self.ibox(0)?;
-        self.print_outer_attributes(&arm.attrs)?;
+        try!(self.cbox(INDENT_UNIT));
+        try!(self.ibox(0));
+        try!(self.print_outer_attributes(&arm.attrs));
         let mut first = true;
         for p in &arm.pats {
             if first {
                 first = false;
             } else {
-                space(&mut self.s)?;
-                self.word_space("|")?;
+                try!(space(&mut self.s));
+                try!(self.word_space("|"));
             }
-            self.print_pat(&p)?;
+            try!(self.print_pat(&p));
         }
-        space(&mut self.s)?;
+        try!(space(&mut self.s));
         if let Some(ref e) = arm.guard {
-            self.word_space("if")?;
-            self.print_expr(&e)?;
-            space(&mut self.s)?;
+            try!(self.word_space("if"));
+            try!(self.print_expr(&e));
+            try!(space(&mut self.s));
         }
-        self.word_space("=>")?;
+        try!(self.word_space("=>"));
 
         match arm.body.node {
             ast::ExprKind::Block(ref blk) => {
                 // the block will close the pattern's ibox
-                self.print_block_unclosed_indent(&blk, INDENT_UNIT)?;
+                try!(self.print_block_unclosed_indent(&blk, INDENT_UNIT));
 
                 // If it is a user-provided unsafe block, print a comma after it
                 if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules {
-                    word(&mut self.s, ",")?;
+                    try!(word(&mut self.s, ","));
                 }
             }
             _ => {
-                self.end()?; // close the ibox for the pattern
-                self.print_expr(&arm.body)?;
-                word(&mut self.s, ",")?;
+                try!(self.end()); // close the ibox for the pattern
+                try!(self.print_expr(&arm.body));
+                try!(word(&mut self.s, ","));
             }
         }
         self.end() // close enclosing cbox
     }
 
-    // Returns whether it printed anything
-    fn print_explicit_self(&mut self,
-                           explicit_self: &ast::SelfKind,
-                           mutbl: ast::Mutability) -> io::Result<bool> {
-        self.print_mutability(mutbl)?;
-        match *explicit_self {
-            ast::SelfKind::Static => { return Ok(false); }
-            ast::SelfKind::Value(_) => {
-                word(&mut self.s, "self")?;
+    fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) -> io::Result<()> {
+        match explicit_self.node {
+            SelfKind::Value(m) => {
+                try!(self.print_mutability(m));
+                word(&mut self.s, "self")
             }
-            ast::SelfKind::Region(ref lt, m, _) => {
-                word(&mut self.s, "&")?;
-                self.print_opt_lifetime(lt)?;
-                self.print_mutability(m)?;
-                word(&mut self.s, "self")?;
+            SelfKind::Region(ref lt, m) => {
+                try!(word(&mut self.s, "&"));
+                try!(self.print_opt_lifetime(lt));
+                try!(self.print_mutability(m));
+                word(&mut self.s, "self")
             }
-            ast::SelfKind::Explicit(ref typ, _) => {
-                word(&mut self.s, "self")?;
-                self.word_space(":")?;
-                self.print_type(&typ)?;
+            SelfKind::Explicit(ref typ, m) => {
+                try!(self.print_mutability(m));
+                try!(word(&mut self.s, "self"));
+                try!(self.word_space(":"));
+                self.print_type(&typ)
             }
         }
-        return Ok(true);
     }
 
     pub fn print_fn(&mut self,
@@ -2642,34 +2643,26 @@ impl<'a> State<'a> {
                     abi: abi::Abi,
                     name: Option<ast::Ident>,
                     generics: &ast::Generics,
-                    opt_explicit_self: Option<&ast::SelfKind>,
                     vis: &ast::Visibility) -> io::Result<()> {
-        self.print_fn_header_info(unsafety, constness, abi, vis)?;
+        try!(self.print_fn_header_info(unsafety, constness, abi, vis));
 
         if let Some(name) = name {
-            self.nbsp()?;
-            self.print_ident(name)?;
+            try!(self.nbsp());
+            try!(self.print_ident(name));
         }
-        self.print_generics(generics)?;
-        self.print_fn_args_and_ret(decl, opt_explicit_self)?;
+        try!(self.print_generics(generics));
+        try!(self.print_fn_args_and_ret(decl));
         self.print_where_clause(&generics.where_clause)
     }
 
-    pub fn print_fn_args(&mut self, decl: &ast::FnDecl,
-                         _: Option<&ast::SelfKind>,
-                         is_closure: bool) -> io::Result<()> {
-        self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, is_closure))
-    }
-
-    pub fn print_fn_args_and_ret(&mut self, decl: &ast::FnDecl,
-                                 opt_explicit_self: Option<&ast::SelfKind>)
+    pub fn print_fn_args_and_ret(&mut self, decl: &ast::FnDecl)
         -> io::Result<()> {
-        self.popen()?;
-        self.print_fn_args(decl, opt_explicit_self, false)?;
+        try!(self.popen());
+        try!(self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, false)));
         if decl.variadic {
-            word(&mut self.s, ", ...")?;
+            try!(word(&mut self.s, ", ..."));
         }
-        self.pclose()?;
+        try!(self.pclose());
 
         self.print_fn_output(decl)
     }
@@ -2678,24 +2671,24 @@ impl<'a> State<'a> {
             &mut self,
             decl: &ast::FnDecl)
             -> io::Result<()> {
-        word(&mut self.s, "|")?;
-        self.print_fn_args(decl, None, true)?;
-        word(&mut self.s, "|")?;
+        try!(word(&mut self.s, "|"));
+        try!(self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, true)));
+        try!(word(&mut self.s, "|"));
 
         if let ast::FunctionRetTy::Default(..) = decl.output {
             return Ok(());
         }
 
-        self.space_if_not_bol()?;
-        self.word_space("->")?;
+        try!(self.space_if_not_bol());
+        try!(self.word_space("->"));
         match decl.output {
             ast::FunctionRetTy::Ty(ref ty) => {
-                self.print_type(&ty)?;
+                try!(self.print_type(&ty));
                 self.maybe_print_comment(ty.span.lo)
             }
             ast::FunctionRetTy::Default(..) => unreachable!(),
             ast::FunctionRetTy::None(span) => {
-                self.word_nbsp("!")?;
+                try!(self.word_nbsp("!"));
                 self.maybe_print_comment(span.lo)
             }
         }
@@ -2714,28 +2707,28 @@ impl<'a> State<'a> {
                         bounds: &[ast::TyParamBound])
                         -> io::Result<()> {
         if !bounds.is_empty() {
-            word(&mut self.s, prefix)?;
+            try!(word(&mut self.s, prefix));
             let mut first = true;
             for bound in bounds {
-                self.nbsp()?;
+                try!(self.nbsp());
                 if first {
                     first = false;
                 } else {
-                    self.word_space("+")?;
+                    try!(self.word_space("+"));
                 }
 
-                match *bound {
+                try!(match *bound {
                     TraitTyParamBound(ref tref, TraitBoundModifier::None) => {
                         self.print_poly_trait_ref(tref)
                     }
                     TraitTyParamBound(ref tref, TraitBoundModifier::Maybe) => {
-                        word(&mut self.s, "?")?;
+                        try!(word(&mut self.s, "?"));
                         self.print_poly_trait_ref(tref)
                     }
                     RegionTyParamBound(ref lt) => {
                         self.print_lifetime(lt)
                     }
-                }?
+                })
             }
             Ok(())
         } else {
@@ -2750,16 +2743,20 @@ impl<'a> State<'a> {
         self.print_name(lifetime.name)
     }
 
-    pub fn print_lifetime_def(&mut self,
-                              lifetime: &ast::LifetimeDef)
-                              -> io::Result<()>
+    pub fn print_lifetime_bounds(&mut self,
+                                 lifetime: &ast::Lifetime,
+                                 bounds: &[ast::Lifetime])
+                                 -> io::Result<()>
     {
-        self.print_lifetime(&lifetime.lifetime)?;
-        let mut sep = ":";
-        for v in &lifetime.bounds {
-            word(&mut self.s, sep)?;
-            self.print_lifetime(v)?;
-            sep = "+";
+        try!(self.print_lifetime(lifetime));
+        if !bounds.is_empty() {
+            try!(word(&mut self.s, ": "));
+            for (i, bound) in bounds.iter().enumerate() {
+                if i != 0 {
+                    try!(word(&mut self.s, " + "));
+                }
+                try!(self.print_lifetime(bound));
+            }
         }
         Ok(())
     }
@@ -2773,35 +2770,35 @@ impl<'a> State<'a> {
             return Ok(());
         }
 
-        word(&mut self.s, "<")?;
+        try!(word(&mut self.s, "<"));
 
         let mut ints = Vec::new();
         for i in 0..total {
             ints.push(i);
         }
 
-        self.commasep(Inconsistent, &ints[..], |s, &idx| {
+        try!(self.commasep(Inconsistent, &ints[..], |s, &idx| {
             if idx < generics.lifetimes.len() {
-                let lifetime = &generics.lifetimes[idx];
-                s.print_lifetime_def(lifetime)
+                let lifetime_def = &generics.lifetimes[idx];
+                s.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds)
             } else {
                 let idx = idx - generics.lifetimes.len();
                 let param = &generics.ty_params[idx];
                 s.print_ty_param(param)
             }
-        })?;
+        }));
 
-        word(&mut self.s, ">")?;
+        try!(word(&mut self.s, ">"));
         Ok(())
     }
 
     pub fn print_ty_param(&mut self, param: &ast::TyParam) -> io::Result<()> {
-        self.print_ident(param.ident)?;
-        self.print_bounds(":", &param.bounds)?;
+        try!(self.print_ident(param.ident));
+        try!(self.print_bounds(":", &param.bounds));
         match param.default {
             Some(ref default) => {
-                space(&mut self.s)?;
-                self.word_space("=")?;
+                try!(space(&mut self.s));
+                try!(self.word_space("="));
                 self.print_type(&default)
             }
             _ => Ok(())
@@ -2814,12 +2811,12 @@ impl<'a> State<'a> {
             return Ok(())
         }
 
-        space(&mut self.s)?;
-        self.word_space("where")?;
+        try!(space(&mut self.s));
+        try!(self.word_space("where"));
 
         for (i, predicate) in where_clause.predicates.iter().enumerate() {
             if i != 0 {
-                self.word_space(",")?;
+                try!(self.word_space(","));
             }
 
             match *predicate {
@@ -2827,29 +2824,20 @@ impl<'a> State<'a> {
                                                                              ref bounded_ty,
                                                                              ref bounds,
                                                                              ..}) => {
-                    self.print_formal_lifetime_list(bound_lifetimes)?;
-                    self.print_type(&bounded_ty)?;
-                    self.print_bounds(":", bounds)?;
+                    try!(self.print_formal_lifetime_list(bound_lifetimes));
+                    try!(self.print_type(&bounded_ty));
+                    try!(self.print_bounds(":", bounds));
                 }
                 ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate{ref lifetime,
                                                                                ref bounds,
                                                                                ..}) => {
-                    self.print_lifetime(lifetime)?;
-                    word(&mut self.s, ":")?;
-
-                    for (i, bound) in bounds.iter().enumerate() {
-                        self.print_lifetime(bound)?;
-
-                        if i != 0 {
-                            word(&mut self.s, ":")?;
-                        }
-                    }
+                    try!(self.print_lifetime_bounds(lifetime, bounds));
                 }
                 ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref path, ref ty, ..}) => {
-                    self.print_path(path, false, 0)?;
-                    space(&mut self.s)?;
-                    self.word_space("=")?;
-                    self.print_type(&ty)?;
+                    try!(self.print_path(path, false, 0));
+                    try!(space(&mut self.s));
+                    try!(self.word_space("="));
+                    try!(self.print_type(&ty));
                 }
             }
         }
@@ -2860,52 +2848,52 @@ impl<'a> State<'a> {
     pub fn print_view_path(&mut self, vp: &ast::ViewPath) -> io::Result<()> {
         match vp.node {
             ast::ViewPathSimple(ident, ref path) => {
-                self.print_path(path, false, 0)?;
+                try!(self.print_path(path, false, 0));
 
                 if path.segments.last().unwrap().identifier.name !=
                         ident.name {
-                    space(&mut self.s)?;
-                    self.word_space("as")?;
-                    self.print_ident(ident)?;
+                    try!(space(&mut self.s));
+                    try!(self.word_space("as"));
+                    try!(self.print_ident(ident));
                 }
 
                 Ok(())
             }
 
             ast::ViewPathGlob(ref path) => {
-                self.print_path(path, false, 0)?;
+                try!(self.print_path(path, false, 0));
                 word(&mut self.s, "::*")
             }
 
             ast::ViewPathList(ref path, ref idents) => {
                 if path.segments.is_empty() {
-                    word(&mut self.s, "{")?;
+                    try!(word(&mut self.s, "{"));
                 } else {
-                    self.print_path(path, false, 0)?;
-                    word(&mut self.s, "::{")?;
+                    try!(self.print_path(path, false, 0));
+                    try!(word(&mut self.s, "::{"));
                 }
-                self.commasep(Inconsistent, &idents[..], |s, w| {
+                try!(self.commasep(Inconsistent, &idents[..], |s, w| {
                     match w.node {
                         ast::PathListItemKind::Ident { name, rename, .. } => {
-                            s.print_ident(name)?;
+                            try!(s.print_ident(name));
                             if let Some(ident) = rename {
-                                space(&mut s.s)?;
-                                s.word_space("as")?;
-                                s.print_ident(ident)?;
+                                try!(space(&mut s.s));
+                                try!(s.word_space("as"));
+                                try!(s.print_ident(ident));
                             }
                             Ok(())
                         },
                         ast::PathListItemKind::Mod { rename, .. } => {
-                            word(&mut s.s, "self")?;
+                            try!(word(&mut s.s, "self"));
                             if let Some(ident) = rename {
-                                space(&mut s.s)?;
-                                s.word_space("as")?;
-                                s.print_ident(ident)?;
+                                try!(space(&mut s.s));
+                                try!(s.word_space("as"));
+                                try!(s.print_ident(ident));
                             }
                             Ok(())
                         }
                     }
-                })?;
+                }));
                 word(&mut self.s, "}")
             }
         }
@@ -2920,32 +2908,29 @@ impl<'a> State<'a> {
     }
 
     pub fn print_mt(&mut self, mt: &ast::MutTy) -> io::Result<()> {
-        self.print_mutability(mt.mutbl)?;
+        try!(self.print_mutability(mt.mutbl));
         self.print_type(&mt.ty)
     }
 
     pub fn print_arg(&mut self, input: &ast::Arg, is_closure: bool) -> io::Result<()> {
-        self.ibox(INDENT_UNIT)?;
+        try!(self.ibox(INDENT_UNIT));
         match input.ty.node {
-            ast::TyKind::Infer if is_closure => self.print_pat(&input.pat)?,
+            ast::TyKind::Infer if is_closure => try!(self.print_pat(&input.pat)),
             _ => {
-                let (mutbl, invalid) = match input.pat.node {
-                    PatKind::Ident(ast::BindingMode::ByValue(mutbl), ident, _) |
-                    PatKind::Ident(ast::BindingMode::ByRef(mutbl), ident, _) => {
-                        (mutbl, ident.node.name == keywords::Invalid.name())
-                    }
-                    _ => (ast::Mutability::Immutable, false)
-                };
-
                 if let Some(eself) = input.to_self() {
-                    self.print_explicit_self(&eself.node, mutbl)?;
+                    try!(self.print_explicit_self(&eself));
                 } else {
+                    let invalid = if let PatKind::Ident(_, ident, _) = input.pat.node {
+                        ident.node.name == keywords::Invalid.name()
+                    } else {
+                        false
+                    };
                     if !invalid {
-                        self.print_pat(&input.pat)?;
-                        word(&mut self.s, ":")?;
-                        space(&mut self.s)?;
+                        try!(self.print_pat(&input.pat));
+                        try!(word(&mut self.s, ":"));
+                        try!(space(&mut self.s));
                     }
-                    self.print_type(&input.ty)?;
+                    try!(self.print_type(&input.ty));
                 }
             }
         }
@@ -2957,17 +2942,17 @@ impl<'a> State<'a> {
             return Ok(());
         }
 
-        self.space_if_not_bol()?;
-        self.ibox(INDENT_UNIT)?;
-        self.word_space("->")?;
+        try!(self.space_if_not_bol());
+        try!(self.ibox(INDENT_UNIT));
+        try!(self.word_space("->"));
         match decl.output {
             ast::FunctionRetTy::None(_) =>
-                self.word_nbsp("!")?,
+                try!(self.word_nbsp("!")),
             ast::FunctionRetTy::Default(..) => unreachable!(),
             ast::FunctionRetTy::Ty(ref ty) =>
-                self.print_type(&ty)?
+                try!(self.print_type(&ty))
         }
-        self.end()?;
+        try!(self.end());
 
         match decl.output {
             ast::FunctionRetTy::Ty(ref output) => self.maybe_print_comment(output.span.lo),
@@ -2980,13 +2965,12 @@ impl<'a> State<'a> {
                        unsafety: ast::Unsafety,
                        decl: &ast::FnDecl,
                        name: Option<ast::Ident>,
-                       generics: &ast::Generics,
-                       opt_explicit_self: Option<&ast::SelfKind>)
+                       generics: &ast::Generics)
                        -> io::Result<()> {
-        self.ibox(INDENT_UNIT)?;
+        try!(self.ibox(INDENT_UNIT));
         if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
-            word(&mut self.s, "for")?;
-            self.print_generics(generics)?;
+            try!(word(&mut self.s, "for"));
+            try!(self.print_generics(generics));
         }
         let generics = ast::Generics {
             lifetimes: Vec::new(),
@@ -2996,38 +2980,36 @@ impl<'a> State<'a> {
                 predicates: Vec::new(),
             },
         };
-        self.print_fn(decl,
+        try!(self.print_fn(decl,
                       unsafety,
                       ast::Constness::NotConst,
                       abi,
                       name,
                       &generics,
-                      opt_explicit_self,
-                      &ast::Visibility::Inherited)?;
+                      &ast::Visibility::Inherited));
         self.end()
     }
 
-    pub fn maybe_print_trailing_comment(&mut self, span: codemap::Span,
+    pub fn maybe_print_trailing_comment(&mut self, span: syntax_pos::Span,
                                         next_pos: Option<BytePos>)
         -> io::Result<()> {
         let cm = match self.cm {
             Some(cm) => cm,
             _ => return Ok(())
         };
-        match self.next_comment() {
-            Some(ref cmnt) => {
-                if (*cmnt).style != comments::Trailing { return Ok(()) }
-                let span_line = cm.lookup_char_pos(span.hi);
-                let comment_line = cm.lookup_char_pos((*cmnt).pos);
-                let mut next = (*cmnt).pos + BytePos(1);
-                match next_pos { None => (), Some(p) => next = p }
-                if span.hi < (*cmnt).pos && (*cmnt).pos < next &&
-                    span_line.line == comment_line.line {
-                        self.print_comment(cmnt)?;
-                        self.cur_cmnt_and_lit.cur_cmnt += 1;
-                    }
+        if let Some(ref cmnt) = self.next_comment() {
+            if (*cmnt).style != comments::Trailing { return Ok(()) }
+            let span_line = cm.lookup_char_pos(span.hi);
+            let comment_line = cm.lookup_char_pos((*cmnt).pos);
+            let mut next = (*cmnt).pos + BytePos(1);
+            if let Some(p) = next_pos {
+                next = p;
+            }
+            if span.hi < (*cmnt).pos && (*cmnt).pos < next &&
+               span_line.line == comment_line.line {
+                self.print_comment(cmnt)?;
+                self.cur_cmnt_and_lit.cur_cmnt += 1;
             }
-            _ => ()
         }
         Ok(())
     }
@@ -3036,12 +3018,12 @@ impl<'a> State<'a> {
         // If there aren't any remaining comments, then we need to manually
         // make sure there is a line break at the end.
         if self.next_comment().is_none() {
-            hardbreak(&mut self.s)?;
+            try!(hardbreak(&mut self.s));
         }
         loop {
             match self.next_comment() {
                 Some(ref cmnt) => {
-                    self.print_comment(cmnt)?;
+                    try!(self.print_comment(cmnt));
                     self.cur_cmnt_and_lit.cur_cmnt += 1;
                 }
                 _ => break
@@ -3056,7 +3038,7 @@ impl<'a> State<'a> {
         match opt_abi {
             Some(Abi::Rust) => Ok(()),
             Some(abi) => {
-                self.word_nbsp("extern")?;
+                try!(self.word_nbsp("extern"));
                 self.word_nbsp(&abi.to_string())
             }
             None => Ok(())
@@ -3067,7 +3049,7 @@ impl<'a> State<'a> {
                                 opt_abi: Option<Abi>) -> io::Result<()> {
         match opt_abi {
             Some(abi) => {
-                self.word_nbsp("extern")?;
+                try!(self.word_nbsp("extern"));
                 self.word_nbsp(&abi.to_string())
             }
             None => Ok(())
@@ -3079,18 +3061,18 @@ impl<'a> State<'a> {
                                 constness: ast::Constness,
                                 abi: Abi,
                                 vis: &ast::Visibility) -> io::Result<()> {
-        word(&mut self.s, &visibility_qualified(vis, ""))?;
+        try!(word(&mut self.s, &visibility_qualified(vis, "")));
 
         match constness {
             ast::Constness::NotConst => {}
-            ast::Constness::Const => self.word_nbsp("const")?
+            ast::Constness::Const => try!(self.word_nbsp("const"))
         }
 
-        self.print_unsafety(unsafety)?;
+        try!(self.print_unsafety(unsafety));
 
         if abi != Abi::Rust {
-            self.word_nbsp("extern")?;
-            self.word_nbsp(&abi.to_string())?;
+            try!(self.word_nbsp("extern"));
+            try!(self.word_nbsp(&abi.to_string()));
         }
 
         word(&mut self.s, "fn")
@@ -3113,6 +3095,7 @@ mod tests {
     use ast;
     use codemap;
     use parse::token;
+    use syntax_pos;
 
     #[test]
     fn test_fun_to_string() {
@@ -3120,14 +3103,13 @@ mod tests {
 
         let decl = ast::FnDecl {
             inputs: Vec::new(),
-            output: ast::FunctionRetTy::Default(codemap::DUMMY_SP),
+            output: ast::FunctionRetTy::Default(syntax_pos::DUMMY_SP),
             variadic: false
         };
         let generics = ast::Generics::default();
         assert_eq!(fun_to_string(&decl, ast::Unsafety::Normal,
                                  ast::Constness::NotConst,
-                                 abba_ident,
-                                 None, &generics),
+                                 abba_ident, &generics),
                    "fn abba()");
     }
 
@@ -3135,7 +3117,7 @@ mod tests {
     fn test_variant_to_string() {
         let ident = token::str_to_ident("principal_skinner");
 
-        let var = codemap::respan(codemap::DUMMY_SP, ast::Variant_ {
+        let var = codemap::respan(syntax_pos::DUMMY_SP, ast::Variant_ {
             name: ident,
             attrs: Vec::new(),
             // making this up as I go.... ?
index 5e3cd0773aa45df290384225020a9b4f9902f7f7..928ffb202d0b331594608d40be1c3cc00ed4c22d 100644 (file)
@@ -44,7 +44,7 @@ struct ShowSpanVisitor<'a> {
     mode: Mode,
 }
 
-impl<'a, 'v> Visitor<'v> for ShowSpanVisitor<'a> {
+impl<'a> Visitor for ShowSpanVisitor<'a> {
     fn visit_expr(&mut self, e: &ast::Expr) {
         if let Mode::Expression = self.mode {
             self.span_diagnostic.span_warn(e.span, "expression");
index 84a7b14484828cb754de2202e43acbdbc6719d86..d1454ab06cbc8179bbb87b294777d68e2ef6f46f 100644 (file)
 
 use ast;
 use attr;
-use codemap::{DUMMY_SP, Span, ExpnInfo, NameAndSpan, MacroAttribute};
-use codemap;
-use fold::Folder;
-use fold;
+use syntax_pos::{DUMMY_SP, Span};
+use codemap::{self, ExpnInfo, NameAndSpan, MacroAttribute};
 use parse::token::{intern, InternedString, keywords};
 use parse::{token, ParseSess};
 use ptr::P;
-use util::small_vector::SmallVector;
 
 /// Craft a span that will be ignored by the stability lint's
 /// call to codemap's is_internal check.
@@ -37,33 +34,6 @@ fn ignored_span(sess: &ParseSess, sp: Span) -> Span {
     return sp;
 }
 
-pub fn maybe_inject_crates_ref(krate: ast::Crate, alt_std_name: Option<String>)
-                               -> ast::Crate {
-    if no_core(&krate) {
-        krate
-    } else {
-        let name = if no_std(&krate) {"core"} else {"std"};
-        let mut fold = CrateInjector {
-            item_name: token::str_to_ident(name),
-            crate_name: token::intern(&alt_std_name.unwrap_or(name.to_string())),
-        };
-        fold.fold_crate(krate)
-    }
-}
-
-pub fn maybe_inject_prelude(sess: &ParseSess, krate: ast::Crate) -> ast::Crate {
-    if no_core(&krate) {
-        krate
-    } else {
-        let name = if no_std(&krate) {"core"} else {"std"};
-        let mut fold = PreludeInjector {
-            span: ignored_span(sess, DUMMY_SP),
-            crate_identifier: token::str_to_ident(name),
-        };
-        fold.fold_crate(krate)
-    }
-}
-
 pub fn no_core(krate: &ast::Crate) -> bool {
     attr::contains_name(&krate.attrs, "no_core")
 }
@@ -72,102 +42,54 @@ pub fn no_std(krate: &ast::Crate) -> bool {
     attr::contains_name(&krate.attrs, "no_std") || no_core(krate)
 }
 
-fn no_prelude(attrs: &[ast::Attribute]) -> bool {
-    attr::contains_name(attrs, "no_implicit_prelude")
-}
-
-struct CrateInjector {
-    item_name: ast::Ident,
-    crate_name: ast::Name,
-}
-
-impl fold::Folder for CrateInjector {
-    fn fold_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
-        krate.module.items.insert(0, P(ast::Item {
-            id: ast::DUMMY_NODE_ID,
-            ident: self.item_name,
-            attrs: vec!(
-                attr::mk_attr_outer(attr::mk_attr_id(), attr::mk_word_item(
-                        InternedString::new("macro_use")))),
-            node: ast::ItemKind::ExternCrate(Some(self.crate_name)),
-            vis: ast::Visibility::Inherited,
-            span: DUMMY_SP
-        }));
-
-        krate
-    }
-}
-
-struct PreludeInjector {
-    span: Span,
-    crate_identifier: ast::Ident,
-}
-
-impl fold::Folder for PreludeInjector {
-    fn fold_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
-        // only add `use std::prelude::*;` if there wasn't a
-        // `#![no_implicit_prelude]` at the crate level.
-        // fold_mod() will insert glob path.
-        if !no_prelude(&krate.attrs) {
-            krate.module = self.fold_mod(krate.module);
-        }
-        krate
-    }
-
-    fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
-        if !no_prelude(&item.attrs) {
-            // only recur if there wasn't `#![no_implicit_prelude]`
-            // on this item, i.e. this means that the prelude is not
-            // implicitly imported though the whole subtree
-            fold::noop_fold_item(item, self)
-        } else {
-            SmallVector::one(item)
-        }
+pub fn maybe_inject_crates_ref(sess: &ParseSess,
+                               mut krate: ast::Crate,
+                               alt_std_name: Option<String>)
+                               -> ast::Crate {
+    if no_core(&krate) {
+        return krate;
     }
 
-    fn fold_mod(&mut self, mut mod_: ast::Mod) -> ast::Mod {
-        let prelude_path = ast::Path {
-            span: self.span,
+    let name = if no_std(&krate) { "core" } else { "std" };
+    let crate_name = token::intern(&alt_std_name.unwrap_or(name.to_string()));
+
+    krate.module.items.insert(0, P(ast::Item {
+        attrs: vec![attr::mk_attr_outer(attr::mk_attr_id(),
+                                        attr::mk_word_item(InternedString::new("macro_use")))],
+        vis: ast::Visibility::Inherited,
+        node: ast::ItemKind::ExternCrate(Some(crate_name)),
+        ident: token::str_to_ident(name),
+        id: ast::DUMMY_NODE_ID,
+        span: DUMMY_SP,
+    }));
+
+    let span = ignored_span(sess, DUMMY_SP);
+    krate.module.items.insert(0, P(ast::Item {
+        attrs: vec![ast::Attribute {
+            node: ast::Attribute_ {
+                style: ast::AttrStyle::Outer,
+                value: P(ast::MetaItem {
+                    node: ast::MetaItemKind::Word(token::intern_and_get_ident("prelude_import")),
+                    span: span,
+                }),
+                id: attr::mk_attr_id(),
+                is_sugared_doc: false,
+            },
+            span: span,
+        }],
+        vis: ast::Visibility::Inherited,
+        node: ast::ItemKind::Use(P(codemap::dummy_spanned(ast::ViewPathGlob(ast::Path {
             global: false,
-            segments: vec![
-                ast::PathSegment {
-                    identifier: self.crate_identifier,
-                    parameters: ast::PathParameters::none(),
-                },
-                ast::PathSegment {
-                    identifier: token::str_to_ident("prelude"),
-                    parameters: ast::PathParameters::none(),
-                },
-                ast::PathSegment {
-                    identifier: token::str_to_ident("v1"),
-                    parameters: ast::PathParameters::none(),
-                },
-            ],
-        };
-
-        let vp = P(codemap::dummy_spanned(ast::ViewPathGlob(prelude_path)));
-        mod_.items.insert(0, P(ast::Item {
-            id: ast::DUMMY_NODE_ID,
-            ident: keywords::Invalid.ident(),
-            node: ast::ItemKind::Use(vp),
-            attrs: vec![ast::Attribute {
-                span: self.span,
-                node: ast::Attribute_ {
-                    id: attr::mk_attr_id(),
-                    style: ast::AttrStyle::Outer,
-                    value: P(ast::MetaItem {
-                        span: self.span,
-                        node: ast::MetaItemKind::Word(
-                            token::intern_and_get_ident("prelude_import")
-                        ),
-                    }),
-                    is_sugared_doc: false,
-                },
-            }],
-            vis: ast::Visibility::Inherited,
-            span: self.span,
-        }));
-
-        fold::noop_fold_mod(mod_, self)
-    }
+            segments: vec![name, "prelude", "v1"].into_iter().map(|name| ast::PathSegment {
+                identifier: token::str_to_ident(name),
+                parameters: ast::PathParameters::none(),
+            }).collect(),
+            span: span,
+        })))),
+        id: ast::DUMMY_NODE_ID,
+        ident: keywords::Invalid.ident(),
+        span: span,
+    }));
+
+    krate
 }
index 8eeb61e0de46c3616f0ba6b4b3aeef46d7ef5288..0a60b7fd430c427277fcbe33cf3d7668388c9ae5 100644 (file)
@@ -12,6 +12,7 @@
 
 #![allow(dead_code)]
 #![allow(unused_imports)]
+
 use self::HasTestSignature::*;
 
 use std::iter;
@@ -20,12 +21,15 @@ use std::mem;
 use std::vec;
 use attr::AttrMetaMethods;
 use attr;
-use codemap::{DUMMY_SP, Span, ExpnInfo, NameAndSpan, MacroAttribute};
-use codemap;
+use syntax_pos::{self, DUMMY_SP, NO_EXPANSION, Span, FileMap, BytePos};
+use std::rc::Rc;
+
+use codemap::{self, CodeMap, ExpnInfo, NameAndSpan, MacroAttribute};
 use errors;
+use errors::snippet::{RenderedLine, SnippetData};
 use config;
 use entry::{self, EntryPointType};
-use ext::base::ExtCtxt;
+use ext::base::{ExtCtxt, DummyMacroLoader};
 use ext::build::AstBuilder;
 use ext::expand::ExpansionConfig;
 use fold::Folder;
@@ -59,7 +63,6 @@ struct TestCtxt<'a> {
     testfns: Vec<Test>,
     reexport_test_harness_main: Option<InternedString>,
     is_test_crate: bool,
-    config: ast::CrateConfig,
 
     // top-level re-export submodule, filled out after folding is finished
     toplevel_reexport: Option<ast::Ident>,
@@ -68,14 +71,9 @@ struct TestCtxt<'a> {
 // Traverse the crate, collecting all the test functions, eliding any
 // existing main functions, and synthesizing a main test harness
 pub fn modify_for_testing(sess: &ParseSess,
-                          cfg: &ast::CrateConfig,
+                          should_test: bool,
                           krate: ast::Crate,
                           span_diagnostic: &errors::Handler) -> ast::Crate {
-    // We generate the test harness when building in the 'test'
-    // configuration, either with the '--test' or '--cfg test'
-    // command line options.
-    let should_test = attr::contains_name(&krate.config, "test");
-
     // Check for #[reexport_test_harness_main = "some_name"] which
     // creates a `use some_name = __test::main;`. This needs to be
     // unconditional, so that the attribute is still marked as used in
@@ -85,9 +83,9 @@ pub fn modify_for_testing(sess: &ParseSess,
                                            "reexport_test_harness_main");
 
     if should_test {
-        generate_test_harness(sess, reexport_test_harness_main, krate, cfg, span_diagnostic)
+        generate_test_harness(sess, reexport_test_harness_main, krate, span_diagnostic)
     } else {
-        strip_test_functions(span_diagnostic, krate)
+        krate
     }
 }
 
@@ -271,24 +269,22 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec<ast::Ident>,
 fn generate_test_harness(sess: &ParseSess,
                          reexport_test_harness_main: Option<InternedString>,
                          krate: ast::Crate,
-                         cfg: &ast::CrateConfig,
                          sd: &errors::Handler) -> ast::Crate {
     // Remove the entry points
     let mut cleaner = EntryPointCleaner { depth: 0 };
     let krate = cleaner.fold_crate(krate);
 
-    let mut feature_gated_cfgs = vec![];
+    let mut loader = DummyMacroLoader;
     let mut cx: TestCtxt = TestCtxt {
         sess: sess,
         span_diagnostic: sd,
-        ext_cx: ExtCtxt::new(sess, cfg.clone(),
+        ext_cx: ExtCtxt::new(sess, vec![],
                              ExpansionConfig::default("test".to_string()),
-                             &mut feature_gated_cfgs),
+                             &mut loader),
         path: Vec::new(),
         testfns: Vec::new(),
         reexport_test_harness_main: reexport_test_harness_main,
         is_test_crate: is_test_crate(&krate),
-        config: krate.config.clone(),
         toplevel_reexport: None,
     };
     cx.ext_cx.crate_root = Some("std");
@@ -312,16 +308,6 @@ fn generate_test_harness(sess: &ParseSess,
     return res;
 }
 
-fn strip_test_functions(diagnostic: &errors::Handler, krate: ast::Crate)
-                        -> ast::Crate {
-    // When not compiling with --test we should not compile the
-    // #[test] functions
-    config::strip_items(diagnostic, krate, |attrs| {
-        !attr::contains_name(&attrs[..], "test") &&
-        !attr::contains_name(&attrs[..], "bench")
-    })
-}
-
 /// Craft a span that will be ignored by the stability lint's
 /// call to codemap's is_internal check.
 /// The expanded code calls some unstable functions in the test crate.
@@ -492,7 +478,7 @@ fn mk_main(cx: &mut TestCtxt) -> P<ast::Item> {
     let main_attr = ecx.attribute(sp, main_meta);
     // pub fn main() { ... }
     let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![]));
-    let main_body = ecx.block_all(sp, vec![call_test_main], None);
+    let main_body = ecx.block(sp, vec![call_test_main]);
     let main = ast::ItemKind::Fn(ecx.fn_decl(vec![], main_ret_ty),
                            ast::Unsafety::Normal,
                            ast::Constness::NotConst,
@@ -622,10 +608,10 @@ fn mk_test_descs(cx: &TestCtxt) -> P<ast::Expr> {
                     mk_test_desc_and_fn_rec(cx, test)
                 }).collect()),
                 span: DUMMY_SP,
-                attrs: None,
+                attrs: ast::ThinVec::new(),
             })),
         span: DUMMY_SP,
-        attrs: None,
+        attrs: ast::ThinVec::new(),
     })
 }
 
diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs
new file mode 100644 (file)
index 0000000..35377d1
--- /dev/null
@@ -0,0 +1,210 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! # Token Trees
+//! TokenTrees are syntactic forms for dealing with tokens. The description below is
+//! more complete; in short a TokenTree is a single token, a delimited sequence of token
+//! trees, or a sequence with repetition for list splicing as part of macro expansion.
+
+use ast::{AttrStyle};
+use codemap::{Span};
+use ext::base;
+use ext::tt::macro_parser;
+use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
+use parse::lexer;
+use parse::token;
+
+/// A delimited sequence of token trees
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub struct Delimited {
+    /// The type of delimiter
+    pub delim: token::DelimToken,
+    /// The span covering the opening delimiter
+    pub open_span: Span,
+    /// The delimited sequence of token trees
+    pub tts: Vec<TokenTree>,
+    /// The span covering the closing delimiter
+    pub close_span: Span,
+}
+
+impl Delimited {
+    /// Returns the opening delimiter as a token.
+    pub fn open_token(&self) -> token::Token {
+        token::OpenDelim(self.delim)
+    }
+
+    /// Returns the closing delimiter as a token.
+    pub fn close_token(&self) -> token::Token {
+        token::CloseDelim(self.delim)
+    }
+
+    /// Returns the opening delimiter as a token tree.
+    pub fn open_tt(&self) -> TokenTree {
+        TokenTree::Token(self.open_span, self.open_token())
+    }
+
+    /// Returns the closing delimiter as a token tree.
+    pub fn close_tt(&self) -> TokenTree {
+        TokenTree::Token(self.close_span, self.close_token())
+    }
+}
+
+/// A sequence of token trees
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub struct SequenceRepetition {
+    /// The sequence of token trees
+    pub tts: Vec<TokenTree>,
+    /// The optional separator
+    pub separator: Option<token::Token>,
+    /// Whether the sequence can be repeated zero (*), or one or more times (+)
+    pub op: KleeneOp,
+    /// The number of `MatchNt`s that appear in the sequence (and subsequences)
+    pub num_captures: usize,
+}
+
+/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star)
+/// for token sequences.
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
+pub enum KleeneOp {
+    ZeroOrMore,
+    OneOrMore,
+}
+
+/// When the main rust parser encounters a syntax-extension invocation, it
+/// parses the arguments to the invocation as a token-tree. This is a very
+/// loose structure, such that all sorts of different AST-fragments can
+/// be passed to syntax extensions using a uniform type.
+///
+/// If the syntax extension is an MBE macro, it will attempt to match its
+/// LHS token tree against the provided token tree, and if it finds a
+/// match, will transcribe the RHS token tree, splicing in any captured
+/// macro_parser::matched_nonterminals into the `SubstNt`s it finds.
+///
+/// The RHS of an MBE macro is the only place `SubstNt`s are substituted.
+/// Nothing special happens to misnamed or misplaced `SubstNt`s.
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub enum TokenTree {
+    /// A single token
+    Token(Span, token::Token),
+    /// A delimited sequence of token trees
+    Delimited(Span, Delimited),
+
+    // This only makes sense in MBE macros.
+
+    /// A kleene-style repetition sequence with a span
+    // FIXME(eddyb) #12938 Use DST.
+    Sequence(Span, SequenceRepetition),
+}
+
+impl TokenTree {
+    pub fn len(&self) -> usize {
+        match *self {
+            TokenTree::Token(_, token::DocComment(name)) => {
+                match doc_comment_style(&name.as_str()) {
+                    AttrStyle::Outer => 2,
+                    AttrStyle::Inner => 3
+                }
+            }
+            TokenTree::Token(_, token::SpecialVarNt(..)) => 2,
+            TokenTree::Token(_, token::MatchNt(..)) => 3,
+            TokenTree::Delimited(_, ref delimed) => {
+                delimed.tts.len() + 2
+            }
+            TokenTree::Sequence(_, ref seq) => {
+                seq.tts.len()
+            }
+            TokenTree::Token(..) => 0
+        }
+    }
+
+    pub fn get_tt(&self, index: usize) -> TokenTree {
+        match (self, index) {
+            (&TokenTree::Token(sp, token::DocComment(_)), 0) => {
+                TokenTree::Token(sp, token::Pound)
+            }
+            (&TokenTree::Token(sp, token::DocComment(name)), 1)
+            if doc_comment_style(&name.as_str()) == AttrStyle::Inner => {
+                TokenTree::Token(sp, token::Not)
+            }
+            (&TokenTree::Token(sp, token::DocComment(name)), _) => {
+                let stripped = strip_doc_comment_decoration(&name.as_str());
+
+                // Searches for the occurrences of `"#*` and returns the minimum number of `#`s
+                // required to wrap the text.
+                let num_of_hashes = stripped.chars().scan(0, |cnt, x| {
+                    *cnt = if x == '"' {
+                        1
+                    } else if *cnt != 0 && x == '#' {
+                        *cnt + 1
+                    } else {
+                        0
+                    };
+                    Some(*cnt)
+                }).max().unwrap_or(0);
+
+                TokenTree::Delimited(sp, Delimited {
+                    delim: token::Bracket,
+                    open_span: sp,
+                    tts: vec![TokenTree::Token(sp, token::Ident(token::str_to_ident("doc"))),
+                              TokenTree::Token(sp, token::Eq),
+                              TokenTree::Token(sp, token::Literal(
+                                  token::StrRaw(token::intern(&stripped), num_of_hashes), None))],
+                    close_span: sp,
+                })
+            }
+            (&TokenTree::Delimited(_, ref delimed), _) => {
+                if index == 0 {
+                    return delimed.open_tt();
+                }
+                if index == delimed.tts.len() + 1 {
+                    return delimed.close_tt();
+                }
+                delimed.tts[index - 1].clone()
+            }
+            (&TokenTree::Token(sp, token::SpecialVarNt(var)), _) => {
+                let v = [TokenTree::Token(sp, token::Dollar),
+                         TokenTree::Token(sp, token::Ident(token::str_to_ident(var.as_str())))];
+                v[index].clone()
+            }
+            (&TokenTree::Token(sp, token::MatchNt(name, kind)), _) => {
+                let v = [TokenTree::Token(sp, token::SubstNt(name)),
+                         TokenTree::Token(sp, token::Colon),
+                         TokenTree::Token(sp, token::Ident(kind))];
+                v[index].clone()
+            }
+            (&TokenTree::Sequence(_, ref seq), _) => {
+                seq.tts[index].clone()
+            }
+            _ => panic!("Cannot expand a token tree")
+        }
+    }
+
+    /// Returns the `Span` corresponding to this token tree.
+    pub fn get_span(&self) -> Span {
+        match *self {
+            TokenTree::Token(span, _)     => span,
+            TokenTree::Delimited(span, _) => span,
+            TokenTree::Sequence(span, _)  => span,
+        }
+    }
+
+    /// Use this token tree as a matcher to parse given tts.
+    pub fn parse(cx: &base::ExtCtxt, mtch: &[TokenTree], tts: &[TokenTree])
+                 -> macro_parser::NamedParseResult {
+        // `None` is because we're not interpolating
+        let arg_rdr = lexer::new_tt_reader_with_doc_flag(&cx.parse_sess().span_diagnostic,
+                                                         None,
+                                                         None,
+                                                         tts.iter().cloned().collect(),
+                                                         true);
+        macro_parser::parse(cx.parse_sess(), cx.cfg(), arg_rdr, mtch)
+    }
+}
+
index 8e20358027b228c2b28bfaa751ac0536d9806608..7295b36af0fe9f983ff452b27c8983bd7e1d52a4 100644 (file)
@@ -47,9 +47,8 @@ impl<T: Eq + Hash + Clone + 'static> Interner<T> {
 
     pub fn intern(&self, val: T) -> Name {
         let mut map = self.map.borrow_mut();
-        match (*map).get(&val) {
-            Some(&idx) => return idx,
-            None => (),
+        if let Some(&idx) = (*map).get(&val) {
+            return idx;
         }
 
         let mut vect = self.vect.borrow_mut();
@@ -161,9 +160,8 @@ impl StrInterner {
 
     pub fn intern(&self, val: &str) -> Name {
         let mut map = self.map.borrow_mut();
-        match map.get(val) {
-            Some(&idx) => return idx,
-            None => (),
+        if let Some(&idx) = map.get(val) {
+            return idx;
         }
 
         let new_idx = Name(self.len() as u32);
index e692ec4452cdd4ed4cb448e80f3b75502fbf7ab3..14244bbdddf28c05cf1ab6c04bbf5d29e54f1cb2 100644 (file)
@@ -12,7 +12,7 @@
 
 use visit::*;
 use ast::*;
-use codemap::Span;
+use syntax_pos::Span;
 
 pub struct NodeCounter {
     pub count: usize,
@@ -26,137 +26,129 @@ impl NodeCounter {
     }
 }
 
-impl<'v> Visitor<'v> for NodeCounter {
+impl Visitor for NodeCounter {
     fn visit_ident(&mut self, span: Span, ident: Ident) {
         self.count += 1;
         walk_ident(self, span, ident);
     }
-    fn visit_mod(&mut self, m: &'v Mod, _s: Span, _n: NodeId) {
+    fn visit_mod(&mut self, m: &Mod, _s: Span, _n: NodeId) {
         self.count += 1;
         walk_mod(self, m)
     }
-    fn visit_foreign_item(&mut self, i: &'v ForeignItem) {
+    fn visit_foreign_item(&mut self, i: &ForeignItem) {
         self.count += 1;
         walk_foreign_item(self, i)
     }
-    fn visit_item(&mut self, i: &'v Item) {
+    fn visit_item(&mut self, i: &Item) {
         self.count += 1;
         walk_item(self, i)
     }
-    fn visit_local(&mut self, l: &'v Local) {
+    fn visit_local(&mut self, l: &Local) {
         self.count += 1;
         walk_local(self, l)
     }
-    fn visit_block(&mut self, b: &'v Block) {
+    fn visit_block(&mut self, b: &Block) {
         self.count += 1;
         walk_block(self, b)
     }
-    fn visit_stmt(&mut self, s: &'v Stmt) {
+    fn visit_stmt(&mut self, s: &Stmt) {
         self.count += 1;
         walk_stmt(self, s)
     }
-    fn visit_arm(&mut self, a: &'v Arm) {
+    fn visit_arm(&mut self, a: &Arm) {
         self.count += 1;
         walk_arm(self, a)
     }
-    fn visit_pat(&mut self, p: &'v Pat) {
+    fn visit_pat(&mut self, p: &Pat) {
         self.count += 1;
         walk_pat(self, p)
     }
-    fn visit_decl(&mut self, d: &'v Decl) {
-        self.count += 1;
-        walk_decl(self, d)
-    }
-    fn visit_expr(&mut self, ex: &'v Expr) {
+    fn visit_expr(&mut self, ex: &Expr) {
         self.count += 1;
         walk_expr(self, ex)
     }
-    fn visit_ty(&mut self, t: &'v Ty) {
+    fn visit_ty(&mut self, t: &Ty) {
         self.count += 1;
         walk_ty(self, t)
     }
-    fn visit_generics(&mut self, g: &'v Generics) {
+    fn visit_generics(&mut self, g: &Generics) {
         self.count += 1;
         walk_generics(self, g)
     }
-    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Block, s: Span, _: NodeId) {
+    fn visit_fn(&mut self, fk: FnKind, fd: &FnDecl, b: &Block, s: Span, _: NodeId) {
         self.count += 1;
         walk_fn(self, fk, fd, b, s)
     }
-    fn visit_trait_item(&mut self, ti: &'v TraitItem) {
+    fn visit_trait_item(&mut self, ti: &TraitItem) {
         self.count += 1;
         walk_trait_item(self, ti)
     }
-    fn visit_impl_item(&mut self, ii: &'v ImplItem) {
+    fn visit_impl_item(&mut self, ii: &ImplItem) {
         self.count += 1;
         walk_impl_item(self, ii)
     }
-    fn visit_trait_ref(&mut self, t: &'v TraitRef) {
+    fn visit_trait_ref(&mut self, t: &TraitRef) {
         self.count += 1;
         walk_trait_ref(self, t)
     }
-    fn visit_ty_param_bound(&mut self, bounds: &'v TyParamBound) {
+    fn visit_ty_param_bound(&mut self, bounds: &TyParamBound) {
         self.count += 1;
         walk_ty_param_bound(self, bounds)
     }
-    fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef, m: &'v TraitBoundModifier) {
+    fn visit_poly_trait_ref(&mut self, t: &PolyTraitRef, m: &TraitBoundModifier) {
         self.count += 1;
         walk_poly_trait_ref(self, t, m)
     }
-    fn visit_variant_data(&mut self, s: &'v VariantData, _: Ident,
-                        _: &'v Generics, _: NodeId, _: Span) {
+    fn visit_variant_data(&mut self, s: &VariantData, _: Ident,
+                          _: &Generics, _: NodeId, _: Span) {
         self.count += 1;
         walk_struct_def(self, s)
     }
-    fn visit_struct_field(&mut self, s: &'v StructField) {
+    fn visit_struct_field(&mut self, s: &StructField) {
         self.count += 1;
         walk_struct_field(self, s)
     }
-    fn visit_enum_def(&mut self, enum_definition: &'v EnumDef,
-                      generics: &'v Generics, item_id: NodeId, _: Span) {
+    fn visit_enum_def(&mut self, enum_definition: &EnumDef,
+                      generics: &Generics, item_id: NodeId, _: Span) {
         self.count += 1;
         walk_enum_def(self, enum_definition, generics, item_id)
     }
-    fn visit_variant(&mut self, v: &'v Variant, g: &'v Generics, item_id: NodeId) {
+    fn visit_variant(&mut self, v: &Variant, g: &Generics, item_id: NodeId) {
         self.count += 1;
         walk_variant(self, v, g, item_id)
     }
-    fn visit_lifetime(&mut self, lifetime: &'v Lifetime) {
+    fn visit_lifetime(&mut self, lifetime: &Lifetime) {
         self.count += 1;
         walk_lifetime(self, lifetime)
     }
-    fn visit_lifetime_def(&mut self, lifetime: &'v LifetimeDef) {
+    fn visit_lifetime_def(&mut self, lifetime: &LifetimeDef) {
         self.count += 1;
         walk_lifetime_def(self, lifetime)
     }
-    fn visit_explicit_self(&mut self, es: &'v ExplicitSelf) {
-        self.count += 1;
-        walk_explicit_self(self, es)
-    }
-    fn visit_mac(&mut self, _mac: &'v Mac) {
+    fn visit_mac(&mut self, _mac: &Mac) {
         self.count += 1;
         walk_mac(self, _mac)
     }
-    fn visit_path(&mut self, path: &'v Path, _id: NodeId) {
+    fn visit_path(&mut self, path: &Path, _id: NodeId) {
         self.count += 1;
         walk_path(self, path)
     }
-    fn visit_path_list_item(&mut self, prefix: &'v Path, item: &'v PathListItem) {
+    fn visit_path_list_item(&mut self, prefix: &Path, item: &PathListItem) {
         self.count += 1;
         walk_path_list_item(self, prefix, item)
     }
-    fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &'v PathParameters) {
+    fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &PathParameters) {
         self.count += 1;
         walk_path_parameters(self, path_span, path_parameters)
     }
-    fn visit_assoc_type_binding(&mut self, type_binding: &'v TypeBinding) {
+    fn visit_assoc_type_binding(&mut self, type_binding: &TypeBinding) {
         self.count += 1;
         walk_assoc_type_binding(self, type_binding)
     }
-    fn visit_attribute(&mut self, _attr: &'v Attribute) {
+    fn visit_attribute(&mut self, _attr: &Attribute) {
         self.count += 1;
     }
-    fn visit_macro_def(&mut self, macro_def: &'v MacroDef) {
+    fn visit_macro_def(&mut self, macro_def: &MacroDef) {
         self.count += 1;
         walk_macro_def(self, macro_def)
     }
index 8358af69b6666dc0a5b7f2c85ba1863c12b6113a..f59428bf536cfdd73e7e0196a9ddd5ef025c3493 100644 (file)
@@ -14,12 +14,13 @@ use parse::{lexer, new_parser_from_source_str};
 use parse::parser::Parser;
 use parse::token;
 use ptr::P;
+use tokenstream;
 use std::iter::Peekable;
 
 /// Map a string to tts, using a made-up filename:
-pub fn string_to_tts(source_str: String) -> Vec<ast::TokenTree> {
+pub fn string_to_tts(source_str: String) -> Vec<tokenstream::TokenTree> {
     let ps = ParseSess::new();
-    filemap_to_tts(&ps, ps.codemap().new_filemap("bogofile".to_string(), source_str))
+    filemap_to_tts(&ps, ps.codemap().new_filemap("bogofile".to_string(), None, source_str))
 }
 
 /// Map string to parser (via tts)
index 8b07b21c578c9e05c46e6d6cbbb1db0f48ac3fd5..893646f121f0c63b1fd1d60ed81399ee7d24ee5c 100644 (file)
@@ -29,6 +29,16 @@ enum SmallVectorRepr<T> {
     Many(Vec<T>),
 }
 
+impl<T> Into<Vec<T>> for SmallVector<T> {
+    fn into(self) -> Vec<T> {
+        match self.repr {
+            Zero => Vec::new(),
+            One(t) => vec![t],
+            Many(vec) => vec,
+        }
+    }
+}
+
 impl<T> FromIterator<T> for SmallVector<T> {
     fn from_iter<I: IntoIterator<Item=T>>(iter: I) -> SmallVector<T> {
         let mut v = SmallVector::zero();
@@ -136,6 +146,15 @@ impl<T> SmallVector<T> {
     }
 
     pub fn is_empty(&self) -> bool { self.len() == 0 }
+
+    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> SmallVector<U> {
+        let repr = match self.repr {
+            Zero => Zero,
+            One(t) => One(f(t)),
+            Many(vec) => Many(vec.into_iter().map(f).collect()),
+        };
+        SmallVector { repr: repr }
+    }
 }
 
 impl<T> IntoIterator for SmallVector<T> {
diff --git a/src/libsyntax/util/thin_vec.rs b/src/libsyntax/util/thin_vec.rs
new file mode 100644 (file)
index 0000000..546686b
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/// A vector type optimized for cases where this size is usually 0 (c.f. `SmallVector`).
+/// The `Option<Box<..>>` wrapping allows us to represent a zero sized vector with `None`,
+/// which uses only a single (null) pointer.
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
+pub struct ThinVec<T>(Option<Box<Vec<T>>>);
+
+impl<T> ThinVec<T> {
+    pub fn new() -> Self {
+        ThinVec(None)
+    }
+}
+
+impl<T> From<Vec<T>> for ThinVec<T> {
+    fn from(vec: Vec<T>) -> Self {
+        if vec.is_empty() {
+            ThinVec(None)
+        } else {
+            ThinVec(Some(Box::new(vec)))
+        }
+    }
+}
+
+impl<T> Into<Vec<T>> for ThinVec<T> {
+    fn into(self) -> Vec<T> {
+        match self {
+            ThinVec(None) => Vec::new(),
+            ThinVec(Some(vec)) => *vec,
+        }
+    }
+}
+
+impl<T> ::std::ops::Deref for ThinVec<T> {
+    type Target = [T];
+    fn deref(&self) -> &[T] {
+        match *self {
+            ThinVec(None) => &[],
+            ThinVec(Some(ref vec)) => vec,
+        }
+    }
+}
+
+impl<T> Extend<T> for ThinVec<T> {
+    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
+        match *self {
+            ThinVec(Some(ref mut vec)) => vec.extend(iter),
+            ThinVec(None) => *self = iter.into_iter().collect::<Vec<_>>().into(),
+        }
+    }
+}
index f50a480e5e55a538081e4e1587717b4a015fd20f..1fc4e54d21807c26145f6d10e98e7d85f34eed15 100644 (file)
@@ -25,8 +25,8 @@
 
 use abi::Abi;
 use ast::*;
-use attr::ThinAttributesExt;
-use codemap::Span;
+use syntax_pos::Span;
+use codemap::Spanned;
 
 #[derive(Copy, Clone, PartialEq, Eq)]
 pub enum FnKind<'a> {
@@ -49,60 +49,56 @@ pub enum FnKind<'a> {
 /// explicitly, you need to override each method.  (And you also need
 /// to monitor future changes to `Visitor` in case a new method with a
 /// new default implementation gets introduced.)
-pub trait Visitor<'v> : Sized {
+pub trait Visitor: Sized {
     fn visit_name(&mut self, _span: Span, _name: Name) {
         // Nothing to do.
     }
     fn visit_ident(&mut self, span: Span, ident: Ident) {
         walk_ident(self, span, ident);
     }
-    fn visit_mod(&mut self, m: &'v Mod, _s: Span, _n: NodeId) { walk_mod(self, m) }
-    fn visit_foreign_item(&mut self, i: &'v ForeignItem) { walk_foreign_item(self, i) }
-    fn visit_item(&mut self, i: &'v Item) { walk_item(self, i) }
-    fn visit_local(&mut self, l: &'v Local) { walk_local(self, l) }
-    fn visit_block(&mut self, b: &'v Block) { walk_block(self, b) }
-    fn visit_stmt(&mut self, s: &'v Stmt) { walk_stmt(self, s) }
-    fn visit_arm(&mut self, a: &'v Arm) { walk_arm(self, a) }
-    fn visit_pat(&mut self, p: &'v Pat) { walk_pat(self, p) }
-    fn visit_decl(&mut self, d: &'v Decl) { walk_decl(self, d) }
-    fn visit_expr(&mut self, ex: &'v Expr) { walk_expr(self, ex) }
-    fn visit_expr_post(&mut self, _ex: &'v Expr) { }
-    fn visit_ty(&mut self, t: &'v Ty) { walk_ty(self, t) }
-    fn visit_generics(&mut self, g: &'v Generics) { walk_generics(self, g) }
-    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, b: &'v Block, s: Span, _: NodeId) {
+    fn visit_mod(&mut self, m: &Mod, _s: Span, _n: NodeId) { walk_mod(self, m) }
+    fn visit_foreign_item(&mut self, i: &ForeignItem) { walk_foreign_item(self, i) }
+    fn visit_item(&mut self, i: &Item) { walk_item(self, i) }
+    fn visit_local(&mut self, l: &Local) { walk_local(self, l) }
+    fn visit_block(&mut self, b: &Block) { walk_block(self, b) }
+    fn visit_stmt(&mut self, s: &Stmt) { walk_stmt(self, s) }
+    fn visit_arm(&mut self, a: &Arm) { walk_arm(self, a) }
+    fn visit_pat(&mut self, p: &Pat) { walk_pat(self, p) }
+    fn visit_expr(&mut self, ex: &Expr) { walk_expr(self, ex) }
+    fn visit_expr_post(&mut self, _ex: &Expr) { }
+    fn visit_ty(&mut self, t: &Ty) { walk_ty(self, t) }
+    fn visit_generics(&mut self, g: &Generics) { walk_generics(self, g) }
+    fn visit_fn(&mut self, fk: FnKind, fd: &FnDecl, b: &Block, s: Span, _: NodeId) {
         walk_fn(self, fk, fd, b, s)
     }
-    fn visit_trait_item(&mut self, ti: &'v TraitItem) { walk_trait_item(self, ti) }
-    fn visit_impl_item(&mut self, ii: &'v ImplItem) { walk_impl_item(self, ii) }
-    fn visit_trait_ref(&mut self, t: &'v TraitRef) { walk_trait_ref(self, t) }
-    fn visit_ty_param_bound(&mut self, bounds: &'v TyParamBound) {
+    fn visit_trait_item(&mut self, ti: &TraitItem) { walk_trait_item(self, ti) }
+    fn visit_impl_item(&mut self, ii: &ImplItem) { walk_impl_item(self, ii) }
+    fn visit_trait_ref(&mut self, t: &TraitRef) { walk_trait_ref(self, t) }
+    fn visit_ty_param_bound(&mut self, bounds: &TyParamBound) {
         walk_ty_param_bound(self, bounds)
     }
-    fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef, m: &'v TraitBoundModifier) {
+    fn visit_poly_trait_ref(&mut self, t: &PolyTraitRef, m: &TraitBoundModifier) {
         walk_poly_trait_ref(self, t, m)
     }
-    fn visit_variant_data(&mut self, s: &'v VariantData, _: Ident,
-                        _: &'v Generics, _: NodeId, _: Span) {
+    fn visit_variant_data(&mut self, s: &VariantData, _: Ident,
+                          _: &Generics, _: NodeId, _: Span) {
         walk_struct_def(self, s)
     }
-    fn visit_struct_field(&mut self, s: &'v StructField) { walk_struct_field(self, s) }
-    fn visit_enum_def(&mut self, enum_definition: &'v EnumDef,
-                      generics: &'v Generics, item_id: NodeId, _: Span) {
+    fn visit_struct_field(&mut self, s: &StructField) { walk_struct_field(self, s) }
+    fn visit_enum_def(&mut self, enum_definition: &EnumDef,
+                      generics: &Generics, item_id: NodeId, _: Span) {
         walk_enum_def(self, enum_definition, generics, item_id)
     }
-    fn visit_variant(&mut self, v: &'v Variant, g: &'v Generics, item_id: NodeId) {
+    fn visit_variant(&mut self, v: &Variant, g: &Generics, item_id: NodeId) {
         walk_variant(self, v, g, item_id)
     }
-    fn visit_lifetime(&mut self, lifetime: &'v Lifetime) {
+    fn visit_lifetime(&mut self, lifetime: &Lifetime) {
         walk_lifetime(self, lifetime)
     }
-    fn visit_lifetime_def(&mut self, lifetime: &'v LifetimeDef) {
+    fn visit_lifetime_def(&mut self, lifetime: &LifetimeDef) {
         walk_lifetime_def(self, lifetime)
     }
-    fn visit_explicit_self(&mut self, es: &'v ExplicitSelf) {
-        walk_explicit_self(self, es)
-    }
-    fn visit_mac(&mut self, _mac: &'v Mac) {
+    fn visit_mac(&mut self, _mac: &Mac) {
         panic!("visit_mac disabled by default");
         // NB: see note about macros above.
         // if you really want a visitor that
@@ -110,26 +106,26 @@ pub trait Visitor<'v> : Sized {
         // definition in your trait impl:
         // visit::walk_mac(self, _mac)
     }
-    fn visit_path(&mut self, path: &'v Path, _id: NodeId) {
+    fn visit_path(&mut self, path: &Path, _id: NodeId) {
         walk_path(self, path)
     }
-    fn visit_path_list_item(&mut self, prefix: &'v Path, item: &'v PathListItem) {
+    fn visit_path_list_item(&mut self, prefix: &Path, item: &PathListItem) {
         walk_path_list_item(self, prefix, item)
     }
-    fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v PathSegment) {
+    fn visit_path_segment(&mut self, path_span: Span, path_segment: &PathSegment) {
         walk_path_segment(self, path_span, path_segment)
     }
-    fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &'v PathParameters) {
+    fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &PathParameters) {
         walk_path_parameters(self, path_span, path_parameters)
     }
-    fn visit_assoc_type_binding(&mut self, type_binding: &'v TypeBinding) {
+    fn visit_assoc_type_binding(&mut self, type_binding: &TypeBinding) {
         walk_assoc_type_binding(self, type_binding)
     }
-    fn visit_attribute(&mut self, _attr: &'v Attribute) {}
-    fn visit_macro_def(&mut self, macro_def: &'v MacroDef) {
+    fn visit_attribute(&mut self, _attr: &Attribute) {}
+    fn visit_macro_def(&mut self, macro_def: &MacroDef) {
         walk_macro_def(self, macro_def)
     }
-    fn visit_vis(&mut self, vis: &'v Visibility) {
+    fn visit_vis(&mut self, vis: &Visibility) {
         walk_vis(self, vis)
     }
 }
@@ -148,89 +144,74 @@ macro_rules! walk_list {
     }
 }
 
-pub fn walk_opt_name<'v, V: Visitor<'v>>(visitor: &mut V, span: Span, opt_name: Option<Name>) {
-    for name in opt_name {
+pub fn walk_opt_name<V: Visitor>(visitor: &mut V, span: Span, opt_name: Option<Name>) {
+    if let Some(name) = opt_name {
         visitor.visit_name(span, name);
     }
 }
 
-pub fn walk_opt_ident<'v, V: Visitor<'v>>(visitor: &mut V, span: Span, opt_ident: Option<Ident>) {
-    for ident in opt_ident {
+pub fn walk_opt_ident<V: Visitor>(visitor: &mut V, span: Span, opt_ident: Option<Ident>) {
+    if let Some(ident) = opt_ident {
         visitor.visit_ident(span, ident);
     }
 }
 
-pub fn walk_ident<'v, V: Visitor<'v>>(visitor: &mut V, span: Span, ident: Ident) {
+pub fn walk_opt_sp_ident<V: Visitor>(visitor: &mut V, opt_sp_ident: &Option<Spanned<Ident>>) {
+    if let Some(ref sp_ident) = *opt_sp_ident {
+        visitor.visit_ident(sp_ident.span, sp_ident.node);
+    }
+}
+
+pub fn walk_ident<V: Visitor>(visitor: &mut V, span: Span, ident: Ident) {
     visitor.visit_name(span, ident.name);
 }
 
-pub fn walk_crate<'v, V: Visitor<'v>>(visitor: &mut V, krate: &'v Crate) {
+pub fn walk_crate<V: Visitor>(visitor: &mut V, krate: &Crate) {
     visitor.visit_mod(&krate.module, krate.span, CRATE_NODE_ID);
     walk_list!(visitor, visit_attribute, &krate.attrs);
     walk_list!(visitor, visit_macro_def, &krate.exported_macros);
 }
 
-pub fn walk_macro_def<'v, V: Visitor<'v>>(visitor: &mut V, macro_def: &'v MacroDef) {
+pub fn walk_macro_def<V: Visitor>(visitor: &mut V, macro_def: &MacroDef) {
     visitor.visit_ident(macro_def.span, macro_def.ident);
     walk_opt_ident(visitor, macro_def.span, macro_def.imported_from);
     walk_list!(visitor, visit_attribute, &macro_def.attrs);
 }
 
-pub fn walk_mod<'v, V: Visitor<'v>>(visitor: &mut V, module: &'v Mod) {
+pub fn walk_mod<V: Visitor>(visitor: &mut V, module: &Mod) {
     walk_list!(visitor, visit_item, &module.items);
 }
 
-pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local) {
+pub fn walk_local<V: Visitor>(visitor: &mut V, local: &Local) {
+    for attr in local.attrs.iter() {
+        visitor.visit_attribute(attr);
+    }
     visitor.visit_pat(&local.pat);
     walk_list!(visitor, visit_ty, &local.ty);
     walk_list!(visitor, visit_expr, &local.init);
 }
 
-pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) {
+pub fn walk_lifetime<V: Visitor>(visitor: &mut V, lifetime: &Lifetime) {
     visitor.visit_name(lifetime.span, lifetime.name);
 }
 
-pub fn walk_lifetime_def<'v, V: Visitor<'v>>(visitor: &mut V,
-                                              lifetime_def: &'v LifetimeDef) {
+pub fn walk_lifetime_def<V: Visitor>(visitor: &mut V, lifetime_def: &LifetimeDef) {
     visitor.visit_lifetime(&lifetime_def.lifetime);
     walk_list!(visitor, visit_lifetime, &lifetime_def.bounds);
 }
 
-pub fn walk_explicit_self<'v, V: Visitor<'v>>(visitor: &mut V,
-                                              explicit_self: &'v ExplicitSelf) {
-    match explicit_self.node {
-        SelfKind::Static => {},
-        SelfKind::Value(ident) => {
-            visitor.visit_ident(explicit_self.span, ident)
-        }
-        SelfKind::Region(ref opt_lifetime, _, ident) => {
-            visitor.visit_ident(explicit_self.span, ident);
-            walk_list!(visitor, visit_lifetime, opt_lifetime);
-        }
-        SelfKind::Explicit(ref typ, ident) => {
-            visitor.visit_ident(explicit_self.span, ident);
-            visitor.visit_ty(typ)
-        }
-    }
-}
-
-pub fn walk_poly_trait_ref<'v, V>(visitor: &mut V,
-                                  trait_ref: &'v PolyTraitRef,
-                                  _modifier: &'v TraitBoundModifier)
-    where V: Visitor<'v>
+pub fn walk_poly_trait_ref<V>(visitor: &mut V, trait_ref: &PolyTraitRef, _: &TraitBoundModifier)
+    where V: Visitor,
 {
     walk_list!(visitor, visit_lifetime_def, &trait_ref.bound_lifetimes);
     visitor.visit_trait_ref(&trait_ref.trait_ref);
 }
 
-pub fn walk_trait_ref<'v,V>(visitor: &mut V,
-                                   trait_ref: &'v TraitRef)
-    where V: Visitor<'v>
-{
+pub fn walk_trait_ref<V: Visitor>(visitor: &mut V, trait_ref: &TraitRef) {
     visitor.visit_path(&trait_ref.path, trait_ref.ref_id)
 }
 
-pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
+pub fn walk_item<V: Visitor>(visitor: &mut V, item: &Item) {
     visitor.visit_vis(&item.vis);
     visitor.visit_ident(item.span, item.ident);
     match item.node {
@@ -247,12 +228,9 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
                     visitor.visit_path(path, item.id);
                 }
                 ViewPathList(ref prefix, ref list) => {
-                    if !list.is_empty() {
-                        for item in list {
-                            visitor.visit_path_list_item(prefix, item)
-                        }
-                    } else {
-                        visitor.visit_path(prefix, item.id);
+                    visitor.visit_path(prefix, item.id);
+                    for item in list {
+                        visitor.visit_path_list_item(prefix, item)
                     }
                 }
             }
@@ -312,17 +290,16 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
     walk_list!(visitor, visit_attribute, &item.attrs);
 }
 
-pub fn walk_enum_def<'v, V: Visitor<'v>>(visitor: &mut V,
-                                         enum_definition: &'v EnumDef,
-                                         generics: &'v Generics,
-                                         item_id: NodeId) {
+pub fn walk_enum_def<V: Visitor>(visitor: &mut V,
+                                 enum_definition: &EnumDef,
+                                 generics: &Generics,
+                                 item_id: NodeId) {
     walk_list!(visitor, visit_variant, &enum_definition.variants, generics, item_id);
 }
 
-pub fn walk_variant<'v, V: Visitor<'v>>(visitor: &mut V,
-                                        variant: &'v Variant,
-                                        generics: &'v Generics,
-                                        item_id: NodeId) {
+pub fn walk_variant<V>(visitor: &mut V, variant: &Variant, generics: &Generics, item_id: NodeId)
+    where V: Visitor,
+{
     visitor.visit_ident(variant.span, variant.node.name);
     visitor.visit_variant_data(&variant.node.data, variant.node.name,
                              generics, item_id, variant.span);
@@ -330,7 +307,7 @@ pub fn walk_variant<'v, V: Visitor<'v>>(visitor: &mut V,
     walk_list!(visitor, visit_attribute, &variant.node.attrs);
 }
 
-pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
+pub fn walk_ty<V: Visitor>(visitor: &mut V, typ: &Ty) {
     match typ.node {
         TyKind::Vec(ref ty) | TyKind::Paren(ref ty) => {
             visitor.visit_ty(ty)
@@ -369,39 +346,32 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
         TyKind::Typeof(ref expression) => {
             visitor.visit_expr(expression)
         }
-        TyKind::Infer => {}
+        TyKind::Infer | TyKind::ImplicitSelf => {}
         TyKind::Mac(ref mac) => {
             visitor.visit_mac(mac)
         }
     }
 }
 
-pub fn walk_path<'v, V: Visitor<'v>>(visitor: &mut V, path: &'v Path) {
+pub fn walk_path<V: Visitor>(visitor: &mut V, path: &Path) {
     for segment in &path.segments {
         visitor.visit_path_segment(path.span, segment);
     }
 }
 
-pub fn walk_path_list_item<'v, V: Visitor<'v>>(visitor: &mut V, prefix: &'v Path,
-                                               item: &'v PathListItem) {
-    for segment in &prefix.segments {
-        visitor.visit_path_segment(prefix.span, segment);
-    }
-
+pub fn walk_path_list_item<V: Visitor>(visitor: &mut V, _prefix: &Path, item: &PathListItem) {
     walk_opt_ident(visitor, item.span, item.node.name());
     walk_opt_ident(visitor, item.span, item.node.rename());
 }
 
-pub fn walk_path_segment<'v, V: Visitor<'v>>(visitor: &mut V,
-                                             path_span: Span,
-                                             segment: &'v PathSegment) {
+pub fn walk_path_segment<V: Visitor>(visitor: &mut V, path_span: Span, segment: &PathSegment) {
     visitor.visit_ident(path_span, segment.identifier);
     visitor.visit_path_parameters(path_span, &segment.parameters);
 }
 
-pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V,
-                                                _path_span: Span,
-                                                path_parameters: &'v PathParameters) {
+pub fn walk_path_parameters<V>(visitor: &mut V, _path_span: Span, path_parameters: &PathParameters)
+    where V: Visitor,
+{
     match *path_parameters {
         PathParameters::AngleBracketed(ref data) => {
             walk_list!(visitor, visit_ty, &data.types);
@@ -415,25 +385,21 @@ pub fn walk_path_parameters<'v, V: Visitor<'v>>(visitor: &mut V,
     }
 }
 
-pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>(visitor: &mut V,
-                                                   type_binding: &'v TypeBinding) {
+pub fn walk_assoc_type_binding<V: Visitor>(visitor: &mut V, type_binding: &TypeBinding) {
     visitor.visit_ident(type_binding.span, type_binding.ident);
     visitor.visit_ty(&type_binding.ty);
 }
 
-pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
+pub fn walk_pat<V: Visitor>(visitor: &mut V, pattern: &Pat) {
     match pattern.node {
-        PatKind::TupleStruct(ref path, ref opt_children) => {
-            visitor.visit_path(path, pattern.id);
-            if let Some(ref children) = *opt_children {
-                walk_list!(visitor, visit_pat, children);
-            }
-        }
-        PatKind::Path(ref path) => {
+        PatKind::TupleStruct(ref path, ref children, _) => {
             visitor.visit_path(path, pattern.id);
+            walk_list!(visitor, visit_pat, children);
         }
-        PatKind::QPath(ref qself, ref path) => {
-            visitor.visit_ty(&qself.ty);
+        PatKind::Path(ref opt_qself, ref path) => {
+            if let Some(ref qself) = *opt_qself {
+                visitor.visit_ty(&qself.ty);
+            }
             visitor.visit_path(path, pattern.id)
         }
         PatKind::Struct(ref path, ref fields, _) => {
@@ -443,7 +409,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
                 visitor.visit_pat(&field.node.pat)
             }
         }
-        PatKind::Tup(ref tuple_elements) => {
+        PatKind::Tuple(ref tuple_elements, _) => {
             walk_list!(visitor, visit_pat, tuple_elements);
         }
         PatKind::Box(ref subpattern) |
@@ -469,8 +435,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
     }
 }
 
-pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V,
-                                             foreign_item: &'v ForeignItem) {
+pub fn walk_foreign_item<V: Visitor>(visitor: &mut V, foreign_item: &ForeignItem) {
     visitor.visit_vis(&foreign_item.vis);
     visitor.visit_ident(foreign_item.span, foreign_item.ident);
 
@@ -485,8 +450,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V,
     walk_list!(visitor, visit_attribute, &foreign_item.attrs);
 }
 
-pub fn walk_ty_param_bound<'v, V: Visitor<'v>>(visitor: &mut V,
-                                               bound: &'v TyParamBound) {
+pub fn walk_ty_param_bound<V: Visitor>(visitor: &mut V, bound: &TyParamBound) {
     match *bound {
         TraitTyParamBound(ref typ, ref modifier) => {
             visitor.visit_poly_trait_ref(typ, modifier);
@@ -497,7 +461,7 @@ pub fn walk_ty_param_bound<'v, V: Visitor<'v>>(visitor: &mut V,
     }
 }
 
-pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics) {
+pub fn walk_generics<V: Visitor>(visitor: &mut V, generics: &Generics) {
     for param in &generics.ty_params {
         visitor.visit_ident(param.span, param.ident);
         walk_list!(visitor, visit_ty_param_bound, &param.bounds);
@@ -531,13 +495,13 @@ pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics
     }
 }
 
-pub fn walk_fn_ret_ty<'v, V: Visitor<'v>>(visitor: &mut V, ret_ty: &'v FunctionRetTy) {
+pub fn walk_fn_ret_ty<V: Visitor>(visitor: &mut V, ret_ty: &FunctionRetTy) {
     if let FunctionRetTy::Ty(ref output_ty) = *ret_ty {
         visitor.visit_ty(output_ty)
     }
 }
 
-pub fn walk_fn_decl<'v, V: Visitor<'v>>(visitor: &mut V, function_declaration: &'v FnDecl) {
+pub fn walk_fn_decl<V: Visitor>(visitor: &mut V, function_declaration: &FnDecl) {
     for argument in &function_declaration.inputs {
         visitor.visit_pat(&argument.pat);
         visitor.visit_ty(&argument.ty)
@@ -545,31 +509,27 @@ pub fn walk_fn_decl<'v, V: Visitor<'v>>(visitor: &mut V, function_declaration: &
     walk_fn_ret_ty(visitor, &function_declaration.output)
 }
 
-pub fn walk_fn_kind<'v, V: Visitor<'v>>(visitor: &mut V,
-                                        function_kind: FnKind<'v>) {
+pub fn walk_fn_kind<V: Visitor>(visitor: &mut V, function_kind: FnKind) {
     match function_kind {
         FnKind::ItemFn(_, generics, _, _, _, _) => {
             visitor.visit_generics(generics);
         }
         FnKind::Method(_, ref sig, _) => {
             visitor.visit_generics(&sig.generics);
-            visitor.visit_explicit_self(&sig.explicit_self);
         }
         FnKind::Closure => {}
     }
 }
 
-pub fn walk_fn<'v, V: Visitor<'v>>(visitor: &mut V,
-                                   function_kind: FnKind<'v>,
-                                   function_declaration: &'v FnDecl,
-                                   function_body: &'v Block,
-                                   _span: Span) {
-    walk_fn_decl(visitor, function_declaration);
-    walk_fn_kind(visitor, function_kind);
-    visitor.visit_block(function_body)
+pub fn walk_fn<V>(visitor: &mut V, kind: FnKind, declaration: &FnDecl, body: &Block, _span: Span)
+    where V: Visitor,
+{
+    walk_fn_decl(visitor, declaration);
+    walk_fn_kind(visitor, kind);
+    visitor.visit_block(body)
 }
 
-pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v TraitItem) {
+pub fn walk_trait_item<V: Visitor>(visitor: &mut V, trait_item: &TraitItem) {
     visitor.visit_ident(trait_item.span, trait_item.ident);
     walk_list!(visitor, visit_attribute, &trait_item.attrs);
     match trait_item.node {
@@ -578,7 +538,6 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai
             walk_list!(visitor, visit_expr, default);
         }
         TraitItemKind::Method(ref sig, None) => {
-            visitor.visit_explicit_self(&sig.explicit_self);
             visitor.visit_generics(&sig.generics);
             walk_fn_decl(visitor, &sig.decl);
         }
@@ -590,10 +549,13 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai
             walk_list!(visitor, visit_ty_param_bound, bounds);
             walk_list!(visitor, visit_ty, default);
         }
+        TraitItemKind::Macro(ref mac) => {
+            visitor.visit_mac(mac);
+        }
     }
 }
 
-pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem) {
+pub fn walk_impl_item<V: Visitor>(visitor: &mut V, impl_item: &ImplItem) {
     visitor.visit_vis(&impl_item.vis);
     visitor.visit_ident(impl_item.span, impl_item.ident);
     walk_list!(visitor, visit_attribute, &impl_item.attrs);
@@ -615,51 +577,46 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt
     }
 }
 
-pub fn walk_struct_def<'v, V: Visitor<'v>>(visitor: &mut V,
-                                           struct_definition: &'v VariantData) {
+pub fn walk_struct_def<V: Visitor>(visitor: &mut V, struct_definition: &VariantData) {
     walk_list!(visitor, visit_struct_field, struct_definition.fields());
 }
 
-pub fn walk_struct_field<'v, V: Visitor<'v>>(visitor: &mut V,
-                                             struct_field: &'v StructField) {
+pub fn walk_struct_field<V: Visitor>(visitor: &mut V, struct_field: &StructField) {
     visitor.visit_vis(&struct_field.vis);
     walk_opt_ident(visitor, struct_field.span, struct_field.ident);
     visitor.visit_ty(&struct_field.ty);
     walk_list!(visitor, visit_attribute, &struct_field.attrs);
 }
 
-pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block) {
+pub fn walk_block<V: Visitor>(visitor: &mut V, block: &Block) {
     walk_list!(visitor, visit_stmt, &block.stmts);
-    walk_list!(visitor, visit_expr, &block.expr);
 }
 
-pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt) {
+pub fn walk_stmt<V: Visitor>(visitor: &mut V, statement: &Stmt) {
     match statement.node {
-        StmtKind::Decl(ref declaration, _) => visitor.visit_decl(declaration),
-        StmtKind::Expr(ref expression, _) | StmtKind::Semi(ref expression, _) => {
+        StmtKind::Local(ref local) => visitor.visit_local(local),
+        StmtKind::Item(ref item) => visitor.visit_item(item),
+        StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => {
             visitor.visit_expr(expression)
         }
-        StmtKind::Mac(ref mac, _, ref attrs) => {
+        StmtKind::Mac(ref mac) => {
+            let (ref mac, _, ref attrs) = **mac;
             visitor.visit_mac(mac);
-            for attr in attrs.as_attr_slice() {
+            for attr in attrs.iter() {
                 visitor.visit_attribute(attr);
             }
         }
     }
 }
 
-pub fn walk_decl<'v, V: Visitor<'v>>(visitor: &mut V, declaration: &'v Decl) {
-    match declaration.node {
-        DeclKind::Local(ref local) => visitor.visit_local(local),
-        DeclKind::Item(ref item) => visitor.visit_item(item),
-    }
-}
-
-pub fn walk_mac<'v, V: Visitor<'v>>(_: &mut V, _: &'v Mac) {
+pub fn walk_mac<V: Visitor>(_: &mut V, _: &Mac) {
     // Empty!
 }
 
-pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
+pub fn walk_expr<V: Visitor>(visitor: &mut V, expression: &Expr) {
+    for attr in expression.attrs.iter() {
+        visitor.visit_attribute(attr);
+    }
     match expression.node {
         ExprKind::Box(ref subexpression) => {
             visitor.visit_expr(subexpression)
@@ -712,10 +669,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             visitor.visit_block(if_block);
             walk_list!(visitor, visit_expr, optional_else);
         }
-        ExprKind::While(ref subexpression, ref block, opt_ident) => {
+        ExprKind::While(ref subexpression, ref block, ref opt_sp_ident) => {
             visitor.visit_expr(subexpression);
             visitor.visit_block(block);
-            walk_opt_ident(visitor, expression.span, opt_ident)
+            walk_opt_sp_ident(visitor, opt_sp_ident);
         }
         ExprKind::IfLet(ref pattern, ref subexpression, ref if_block, ref optional_else) => {
             visitor.visit_pat(pattern);
@@ -723,21 +680,21 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             visitor.visit_block(if_block);
             walk_list!(visitor, visit_expr, optional_else);
         }
-        ExprKind::WhileLet(ref pattern, ref subexpression, ref block, opt_ident) => {
+        ExprKind::WhileLet(ref pattern, ref subexpression, ref block, ref opt_sp_ident) => {
             visitor.visit_pat(pattern);
             visitor.visit_expr(subexpression);
             visitor.visit_block(block);
-            walk_opt_ident(visitor, expression.span, opt_ident)
+            walk_opt_sp_ident(visitor, opt_sp_ident);
         }
-        ExprKind::ForLoop(ref pattern, ref subexpression, ref block, opt_ident) => {
+        ExprKind::ForLoop(ref pattern, ref subexpression, ref block, ref opt_sp_ident) => {
             visitor.visit_pat(pattern);
             visitor.visit_expr(subexpression);
             visitor.visit_block(block);
-            walk_opt_ident(visitor, expression.span, opt_ident)
+            walk_opt_sp_ident(visitor, opt_sp_ident);
         }
-        ExprKind::Loop(ref block, opt_ident) => {
+        ExprKind::Loop(ref block, ref opt_sp_ident) => {
             visitor.visit_block(block);
-            walk_opt_ident(visitor, expression.span, opt_ident)
+            walk_opt_sp_ident(visitor, opt_sp_ident);
         }
         ExprKind::Match(ref subexpression, ref arms) => {
             visitor.visit_expr(subexpression);
@@ -780,10 +737,8 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             }
             visitor.visit_path(path, expression.id)
         }
-        ExprKind::Break(ref opt_sp_ident) | ExprKind::Again(ref opt_sp_ident) => {
-            for sp_ident in opt_sp_ident {
-                visitor.visit_ident(sp_ident.span, sp_ident.node);
-            }
+        ExprKind::Break(ref opt_sp_ident) | ExprKind::Continue(ref opt_sp_ident) => {
+            walk_opt_sp_ident(visitor, opt_sp_ident);
         }
         ExprKind::Ret(ref optional_expression) => {
             walk_list!(visitor, visit_expr, optional_expression);
@@ -808,14 +763,14 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
     visitor.visit_expr_post(expression)
 }
 
-pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm) {
+pub fn walk_arm<V: Visitor>(visitor: &mut V, arm: &Arm) {
     walk_list!(visitor, visit_pat, &arm.pats);
     walk_list!(visitor, visit_expr, &arm.guard);
     visitor.visit_expr(&arm.body);
     walk_list!(visitor, visit_attribute, &arm.attrs);
 }
 
-pub fn walk_vis<'v, V: Visitor<'v>>(visitor: &mut V, vis: &'v Visibility) {
+pub fn walk_vis<V: Visitor>(visitor: &mut V, vis: &Visibility) {
     if let Visibility::Restricted { ref path, id } = *vis {
         visitor.visit_path(path, id);
     }
index 671f3e4a7e3302cf265464adb617a4096c77e840..040c6c8ebff26db4dc39724b2117cfa0cae9675b 100644 (file)
@@ -12,3 +12,5 @@ crate-type = ["dylib"]
 fmt_macros = { path = "../libfmt_macros" }
 log = { path = "../liblog" }
 syntax = { path = "../libsyntax" }
+syntax_pos = { path = "../libsyntax_pos" }
+rustc_errors = { path = "../librustc_errors" }
\ No newline at end of file
index 50d2b9d31fe010b396ad1dff085d8307387bf455..56a8c28ffedc2fe6547bca57842d320da96d388a 100644 (file)
@@ -15,7 +15,6 @@ use self::State::*;
 
 use syntax::ast;
 use syntax::codemap;
-use syntax::codemap::Span;
 use syntax::ext::base;
 use syntax::ext::base::*;
 use syntax::feature_gate;
@@ -23,6 +22,8 @@ use syntax::parse::token::intern;
 use syntax::parse::{self, token};
 use syntax::ptr::P;
 use syntax::ast::AsmDialect;
+use syntax_pos::Span;
+use syntax::tokenstream;
 
 enum State {
     Asm,
@@ -48,7 +49,7 @@ impl State {
 
 const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
 
-pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                        -> Box<base::MacResult+'cx> {
     if !cx.ecfg.enable_asm() {
         feature_gate::emit_feature_err(
@@ -62,8 +63,8 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
     // parsed as `asm!(z)` with `z = "x": y` which is type ascription.
     let first_colon = tts.iter().position(|tt| {
         match *tt {
-            ast::TokenTree::Token(_, token::Colon) |
-            ast::TokenTree::Token(_, token::ModSep) => true,
+            tokenstream::TokenTree::Token(_, token::Colon) |
+            tokenstream::TokenTree::Token(_, token::ModSep) => true,
             _ => false
         }
     }).unwrap_or(tts.len());
@@ -260,6 +261,6 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
             expn_id: expn_id,
         }),
         span: sp,
-        attrs: None,
+        attrs: ast::ThinVec::new(),
     }))
 }
index bae0462b8d33044f3141bffa53e22468a891c75e..dbf23328f41fe3394a95359f95ab35283f57ecc0 100644 (file)
 /// a literal `true` or `false` based on whether the given cfg matches the
 /// current compilation environment.
 
-use syntax::ast;
-use syntax::codemap::Span;
 use syntax::ext::base::*;
 use syntax::ext::base;
 use syntax::ext::build::AstBuilder;
 use syntax::attr;
+use syntax::tokenstream;
 use syntax::parse::token;
-use syntax::config::CfgDiagReal;
+use syntax_pos::Span;
 
 pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
                        sp: Span,
-                       tts: &[ast::TokenTree])
+                       tts: &[tokenstream::TokenTree])
                        -> Box<base::MacResult+'static> {
     let mut p = cx.new_parser_from_tts(tts);
     let cfg = panictry!(p.parse_meta_item());
@@ -33,12 +32,6 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
         return DummyResult::expr(sp);
     }
 
-    let matches_cfg = {
-        let mut diag = CfgDiagReal {
-            diag: &cx.parse_sess.span_diagnostic,
-            feature_gated_cfgs: cx.feature_gated_cfgs,
-        };
-        attr::cfg_matches(&cx.cfg, &cfg, &mut diag)
-    };
+    let matches_cfg = attr::cfg_matches(&cx.cfg, &cfg, cx.parse_sess, cx.ecfg.features);
     MacEager::expr(cx.expr_bool(sp, matches_cfg))
 }
index db731adf7943bbca07c4cdc62a28a437eadde021..22c4aeefbd169c03fa2fea10cd7144999faec45a 100644 (file)
@@ -9,16 +9,17 @@
 // except according to those terms.
 
 use syntax::ast;
-use syntax::codemap;
 use syntax::ext::base;
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
+use syntax_pos;
+use syntax::tokenstream;
 
 use std::string::String;
 
 pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
-                         sp: codemap::Span,
-                         tts: &[ast::TokenTree])
+                         sp: syntax_pos::Span,
+                         tts: &[tokenstream::TokenTree])
                          -> Box<base::MacResult+'static> {
     let es = match base::get_exprs_from_tts(cx, sp, tts) {
         Some(e) => e,
index dce808756cf6a0a2ee0a89cb425a00b2ba75d981..870413a7f61b0526823f23356f5726e7170a3b52 100644 (file)
@@ -8,16 +8,17 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use syntax::ast::{self, TokenTree};
-use syntax::codemap::Span;
+use syntax::ast;
 use syntax::ext::base::*;
 use syntax::ext::base;
 use syntax::feature_gate;
 use syntax::parse::token;
 use syntax::parse::token::str_to_ident;
 use syntax::ptr::P;
+use syntax_pos::Span;
+use syntax::tokenstream::TokenTree;
 
-pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
+pub fn expand_syntax_ext<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree])
                               -> Box<base::MacResult+'cx> {
     if !cx.ecfg.enable_concat_idents() {
         feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
@@ -52,22 +53,36 @@ pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
     }
     let res = str_to_ident(&res_str);
 
-    let e = P(ast::Expr {
-        id: ast::DUMMY_NODE_ID,
-        node: ast::ExprKind::Path(None,
-            ast::Path {
-                 span: sp,
-                 global: false,
-                 segments: vec!(
-                    ast::PathSegment {
-                        identifier: res,
-                        parameters: ast::PathParameters::none(),
-                    }
-                )
-            }
-        ),
-        span: sp,
-        attrs: None,
-    });
-    MacEager::expr(e)
+    struct Result { ident: ast::Ident, span: Span };
+
+    impl Result {
+        fn path(&self) -> ast::Path {
+            let segment = ast::PathSegment {
+                identifier: self.ident,
+                parameters: ast::PathParameters::none()
+            };
+            ast::Path { span: self.span, global: false, segments: vec![segment] }
+        }
+    }
+
+    impl base::MacResult for Result {
+        fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
+            Some(P(ast::Expr {
+                id: ast::DUMMY_NODE_ID,
+                node: ast::ExprKind::Path(None, self.path()),
+                span: self.span,
+                attrs: ast::ThinVec::new(),
+            }))
+        }
+
+        fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> {
+            Some(P(ast::Ty {
+                id: ast::DUMMY_NODE_ID,
+                node: ast::TyKind::Path(None, self.path()),
+                span: self.span,
+            }))
+        }
+    }
+
+    Box::new(Result { ident: res, span: sp })
 }
index 9bc0e08811071c42ed5aaa26e982ecaad9eea236..36818e000b55bf0d3f94fdcd73325f77122ac202 100644 (file)
@@ -12,8 +12,8 @@ use deriving::generic::*;
 use deriving::generic::ty::*;
 
 use syntax::ast::MetaItem;
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
+use syntax_pos::Span;
 
 pub fn expand_deriving_unsafe_bound(cx: &mut ExtCtxt,
                                     span: Span,
index 30fe0f2db8a1ce34dc5adeaad2d9218a1fcec207..1e47ebb85837a761a9596bfafcb1369ba783db7e 100644 (file)
@@ -13,11 +13,11 @@ use deriving::generic::ty::*;
 
 use syntax::ast::{Expr, ItemKind, Generics, MetaItem, VariantData};
 use syntax::attr;
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 #[derive(PartialEq)]
 enum Mode { Deep, Shallow }
@@ -145,12 +145,10 @@ fn cs_clone(
 
     match mode {
         Mode::Shallow => {
-            cx.expr_block(cx.block(trait_span,
-                                   all_fields.iter()
-                                             .map(subcall)
-                                             .map(|e| cx.stmt_expr(e))
-                                             .collect(),
-                                   Some(cx.expr_deref(trait_span, cx.expr_self(trait_span)))))
+            let mut stmts: Vec<_> =
+                all_fields.iter().map(subcall).map(|e| cx.stmt_expr(e)).collect();
+            stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
+            cx.expr_block(cx.block(trait_span, stmts))
         }
         Mode::Deep => {
             match *vdata {
index 8bd12c393370de4940da298a1abd9752877bc4a9..9c5072eeb3e0b79dc9ba58c4b447eba4efc146a9 100644 (file)
@@ -12,11 +12,11 @@ use deriving::generic::*;
 use deriving::generic::ty::*;
 
 use syntax::ast::{MetaItem, Expr};
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 pub fn expand_deriving_eq(cx: &mut ExtCtxt,
                           span: Span,
@@ -30,7 +30,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
                 // create `a.<method>(); b.<method>(); c.<method>(); ...`
                 // (where method is `assert_receiver_is_total_eq`)
                 let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect();
-                let block = cx.block(span, stmts, None);
+                let block = cx.block(span, stmts);
                 cx.expr_block(block)
             },
             Box::new(|cx, sp, _, _| {
index 6133adb8fc5d10fe1a5aa7dbdbabd8d0b47fc1ab..cbd7ac0eadad039ae287dc25781ed596f0edee3a 100644 (file)
@@ -12,11 +12,11 @@ use deriving::generic::*;
 use deriving::generic::ty::*;
 
 use syntax::ast::{MetaItem, Expr, self};
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 pub fn expand_deriving_ord(cx: &mut ExtCtxt,
                            span: Span,
index e5890d7213bedfda21c74596d8211b1ab467c1b8..b5a8167fb555093be14b8940764d6c4d33f2bd43 100644 (file)
@@ -12,11 +12,11 @@ use deriving::generic::*;
 use deriving::generic::ty::*;
 
 use syntax::ast::{MetaItem, Expr, BinOpKind};
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt,
                                   span: Span,
index cfc6dbe5cd03074101a804c8e8e8d4c502b5c0b1..26c14ae934f72796e58c20b6727d04603ff50bdc 100644 (file)
@@ -14,11 +14,11 @@ use deriving::generic::*;
 use deriving::generic::ty::*;
 
 use syntax::ast::{MetaItem, Expr, BinOpKind, self};
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
                                    span: Span,
index d86eae820a884c5764b32c9b60d5ea28342ec891..34c872bef11d10916698f2a57a6475dfe798fa66 100644 (file)
@@ -13,11 +13,11 @@ use deriving::generic::ty::*;
 
 use syntax::ast;
 use syntax::ast::{MetaItem, Expr};
-use syntax::codemap::{Span, respan, DUMMY_SP};
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
 use syntax::ptr::P;
+use syntax_pos::{Span, DUMMY_SP};
 
 pub fn expand_deriving_debug(cx: &mut ExtCtxt,
                             span: Span,
@@ -78,7 +78,7 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
 
     let fmt = substr.nonself_args[0].clone();
 
-    let stmts = match *substr.fields {
+    let mut stmts = match *substr.fields {
         Struct(_, ref fields) | EnumMatching(_, _, ref fields) => {
             let mut stmts = vec![];
             if !is_struct {
@@ -136,7 +136,8 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
                                    token::str_to_ident("finish"),
                                    vec![]);
 
-    let block = cx.block(span, stmts, Some(expr));
+    stmts.push(cx.stmt_expr(expr));
+    let block = cx.block(span, stmts);
     cx.expr_block(block)
 }
 
@@ -149,8 +150,11 @@ fn stmt_let_undescore(cx: &mut ExtCtxt,
         init: Some(expr),
         id: ast::DUMMY_NODE_ID,
         span: sp,
-        attrs: None,
+        attrs: ast::ThinVec::new(),
     });
-    let decl = respan(sp, ast::DeclKind::Local(local));
-    respan(sp, ast::StmtKind::Decl(P(decl), ast::DUMMY_NODE_ID))
+    ast::Stmt {
+        id: ast::DUMMY_NODE_ID,
+        node: ast::StmtKind::Local(local),
+        span: sp,
+    }
 }
index 04888d046ad2d6f652e18062e32fbef5542a6752..488402c48f70a985bde3775011b26719ec601ee2 100644 (file)
@@ -16,12 +16,12 @@ use deriving::generic::ty::*;
 
 use syntax::ast;
 use syntax::ast::{MetaItem, Expr, Mutability};
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token::InternedString;
 use syntax::parse::token;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 pub fn expand_deriving_rustc_decodable(cx: &mut ExtCtxt,
                                        span: Span,
index a6a4830fab7f80ed8cfa30f770330b31f646029c..2711ccba81914ec7e05a5962455e9e4492e3bdaf 100644 (file)
@@ -12,11 +12,11 @@ use deriving::generic::*;
 use deriving::generic::ty::*;
 
 use syntax::ast::{MetaItem, Expr};
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 pub fn expand_deriving_default(cx: &mut ExtCtxt,
                                span: Span,
index 66672305829b9d3413d62fe899366dc29df28676..ad3786212475eaa76f23a82963fab1270827aa13 100644 (file)
@@ -93,11 +93,11 @@ use deriving::generic::*;
 use deriving::generic::ty::*;
 
 use syntax::ast::{MetaItem, Expr, ExprKind, Mutability};
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt,Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 pub fn expand_deriving_rustc_encodable(cx: &mut ExtCtxt,
                                        span: Span,
@@ -285,7 +285,7 @@ fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
                 cx.expr_str(trait_span, substr.type_ident.name.as_str()),
                 blk
             ));
-            cx.expr_block(cx.block(trait_span, vec!(me), Some(ret)))
+            cx.expr_block(cx.block(trait_span, vec![me, cx.stmt_expr(ret)]))
         }
 
         _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)")
index 20fb4bf32ccb7671687348f3359779802d085269..f33898109cc57e3110aa40a13b1ac7cfeaefe1da 100644 (file)
@@ -197,12 +197,12 @@ use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
-use syntax::codemap::{self, DUMMY_SP};
-use syntax::codemap::Span;
-use syntax::errors::Handler;
+use syntax::codemap::{self, respan};
 use syntax::util::move_map::MoveMap;
-use syntax::parse::token::{intern, keywords, InternedString};
+use syntax::parse::token::{keywords, InternedString};
 use syntax::ptr::P;
+use syntax_pos::{Span, DUMMY_SP};
+use errors::Handler;
 
 use self::ty::{LifetimeBounds, Path, Ptr, PtrTy, Self_, Ty};
 
@@ -345,25 +345,25 @@ pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
 /// This method helps to extract all the type parameters referenced from a
 /// type. For a type parameter `<T>`, it looks for either a `TyPath` that
 /// is not global and starts with `T`, or a `TyQPath`.
-fn find_type_parameters(ty: &ast::Ty, ty_param_names: &[ast::Name]) -> Vec<P<ast::Ty>> {
+fn find_type_parameters(ty: &ast::Ty, ty_param_names: &[ast::Name], span: Span, cx: &ExtCtxt)
+                        -> Vec<P<ast::Ty>> {
     use syntax::visit;
 
-    struct Visitor<'a> {
+    struct Visitor<'a, 'b: 'a> {
+        cx: &'a ExtCtxt<'b>,
+        span: Span,
         ty_param_names: &'a [ast::Name],
         types: Vec<P<ast::Ty>>,
     }
 
-    impl<'a> visit::Visitor<'a> for Visitor<'a> {
-        fn visit_ty(&mut self, ty: &'a ast::Ty) {
+    impl<'a, 'b> visit::Visitor for Visitor<'a, 'b> {
+        fn visit_ty(&mut self, ty: &ast::Ty) {
             match ty.node {
                 ast::TyKind::Path(_, ref path) if !path.global => {
-                    match path.segments.first() {
-                        Some(segment) => {
-                            if self.ty_param_names.contains(&segment.identifier.name) {
-                                self.types.push(P(ty.clone()));
-                            }
+                    if let Some(segment) = path.segments.first() {
+                        if self.ty_param_names.contains(&segment.identifier.name) {
+                            self.types.push(P(ty.clone()));
                         }
-                        None => {}
                     }
                 }
                 _ => {}
@@ -371,11 +371,18 @@ fn find_type_parameters(ty: &ast::Ty, ty_param_names: &[ast::Name]) -> Vec<P<ast
 
             visit::walk_ty(self, ty)
         }
+
+        fn visit_mac(&mut self, mac: &ast::Mac) {
+            let span = Span { expn_id: self.span.expn_id, ..mac.span };
+            self.cx.span_err(span, "`derive` cannot be used on items with type macros");
+        }
     }
 
     let mut visitor = Visitor {
         ty_param_names: ty_param_names,
         types: Vec::new(),
+        span: span,
+        cx: cx,
     };
 
     visit::Visitor::visit_ty(&mut visitor, ty);
@@ -556,7 +563,7 @@ impl<'a> TraitDef<'a> {
 
             let mut processed_field_types = HashSet::new();
             for field_ty in field_tys {
-                let tys = find_type_parameters(&field_ty, &ty_param_names);
+                let tys = find_type_parameters(&field_ty, &ty_param_names, self.span, cx);
 
                 for ty in tys {
                     // if we have already handled this type, skip it
@@ -806,25 +813,21 @@ impl<'a> MethodDef<'a> {
                                trait_: &TraitDef,
                                type_ident: Ident,
                                generics: &Generics)
-        -> (ast::ExplicitSelf, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) {
+        -> (Option<ast::ExplicitSelf>, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) {
 
         let mut self_args = Vec::new();
         let mut nonself_args = Vec::new();
         let mut arg_tys = Vec::new();
         let mut nonstatic = false;
 
-        let ast_explicit_self = match self.explicit_self {
-            Some(ref self_ptr) => {
-                let (self_expr, explicit_self) =
-                    ty::get_explicit_self(cx, trait_.span, self_ptr);
+        let ast_explicit_self = self.explicit_self.as_ref().map(|self_ptr| {
+            let (self_expr, explicit_self) = ty::get_explicit_self(cx, trait_.span, self_ptr);
 
-                self_args.push(self_expr);
-                nonstatic = true;
+            self_args.push(self_expr);
+            nonstatic = true;
 
-                explicit_self
-            }
-            None => codemap::respan(trait_.span, ast::SelfKind::Static),
-        };
+            explicit_self
+        });
 
         for (i, ty) in self.args.iter().enumerate() {
             let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics);
@@ -857,24 +860,20 @@ impl<'a> MethodDef<'a> {
                      type_ident: Ident,
                      generics: &Generics,
                      abi: Abi,
-                     explicit_self: ast::ExplicitSelf,
+                     explicit_self: Option<ast::ExplicitSelf>,
                      arg_types: Vec<(Ident, P<ast::Ty>)> ,
                      body: P<Expr>) -> ast::ImplItem {
 
         // create the generics that aren't for Self
         let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics);
 
-        let self_arg = match explicit_self.node {
-            ast::SelfKind::Static => None,
-            // creating fresh self id
-            _ => Some(ast::Arg::from_self(explicit_self.clone(), trait_.span,
-                                          ast::Mutability::Immutable)),
-        };
         let args = {
-            let args = arg_types.into_iter().map(|(name, ty)| {
-                    cx.arg(trait_.span, name, ty)
-                });
-            self_arg.into_iter().chain(args).collect()
+            let self_args = explicit_self.map(|explicit_self| {
+                ast::Arg::from_self(explicit_self, respan(trait_.span, keywords::SelfValue.ident()))
+            });
+            let nonself_args = arg_types.into_iter()
+                                        .map(|(name, ty)| cx.arg(trait_.span, name, ty));
+            self_args.into_iter().chain(nonself_args).collect()
         };
 
         let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident);
@@ -900,7 +899,6 @@ impl<'a> MethodDef<'a> {
             node: ast::ImplItemKind::Method(ast::MethodSig {
                 generics: fn_generics,
                 abi: abi,
-                explicit_self: explicit_self,
                 unsafety: unsafety,
                 constness: ast::Constness::NotConst,
                 decl: fn_decl
@@ -1341,8 +1339,8 @@ impl<'a> MethodDef<'a> {
             //  }
             let all_match = cx.expr_match(sp, match_arg, match_arms);
             let arm_expr = cx.expr_if(sp, discriminant_test, all_match, Some(arm_expr));
-            cx.expr_block(
-                cx.block_all(sp, index_let_stmts, Some(arm_expr)))
+            index_let_stmts.push(cx.stmt_expr(arm_expr));
+            cx.expr_block(cx.block(sp, index_let_stmts))
         } else if variants.is_empty() {
             // As an additional wrinkle, For a zero-variant enum A,
             // currently the compiler
@@ -1429,31 +1427,13 @@ impl<'a> MethodDef<'a> {
 
 // general helper methods.
 impl<'a> TraitDef<'a> {
-    fn set_expn_info(&self,
-                     cx: &mut ExtCtxt,
-                     mut to_set: Span) -> Span {
-        let trait_name = match self.path.path.last() {
-            None => cx.span_bug(self.span, "trait with empty path in generic `derive`"),
-            Some(name) => *name
-        };
-        to_set.expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
-            call_site: to_set,
-            callee: codemap::NameAndSpan {
-                format: codemap::MacroAttribute(intern(&format!("derive({})", trait_name))),
-                span: Some(self.span),
-                allow_internal_unstable: false,
-            }
-        });
-        to_set
-    }
-
     fn summarise_struct(&self,
                         cx: &mut ExtCtxt,
                         struct_def: &VariantData) -> StaticFields {
         let mut named_idents = Vec::new();
         let mut just_spans = Vec::new();
         for field in struct_def.fields(){
-            let sp = self.set_expn_info(cx, field.span);
+            let sp = Span { expn_id: self.span.expn_id, ..field.span };
             match field.ident {
                 Some(ident) => named_idents.push((ident, sp)),
                 _ => just_spans.push(sp),
@@ -1495,7 +1475,7 @@ impl<'a> TraitDef<'a> {
         let mut paths = Vec::new();
         let mut ident_exprs = Vec::new();
         for (i, struct_field) in struct_def.fields().iter().enumerate() {
-            let sp = self.set_expn_info(cx, struct_field.span);
+            let sp = Span { expn_id: self.span.expn_id, ..struct_field.span };
             let ident = cx.ident_of(&format!("{}_{}", prefix, i));
             paths.push(codemap::Spanned{span: sp, node: ident});
             let val = cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp,ident)));
index e31d45d91a59f4ef8baf5feb1dad48180ed11b0a..626fbaada5cbf1e6745f2c246d1aa8a12d984e39 100644 (file)
@@ -15,12 +15,12 @@ pub use self::PtrTy::*;
 pub use self::Ty::*;
 
 use syntax::ast;
-use syntax::ast::{Expr,Generics,Ident};
+use syntax::ast::{Expr, Generics, Ident, SelfKind};
 use syntax::ext::base::ExtCtxt;
 use syntax::ext::build::AstBuilder;
-use syntax::codemap::{Span,respan};
-use syntax::parse::token::keywords;
+use syntax::codemap::respan;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 /// The types of pointers
 #[derive(Clone, Eq, PartialEq)]
@@ -258,12 +258,11 @@ impl<'a> LifetimeBounds<'a> {
 
 pub fn get_explicit_self(cx: &ExtCtxt, span: Span, self_ptr: &Option<PtrTy>)
     -> (P<Expr>, ast::ExplicitSelf) {
-    // this constructs a fresh `self` path, which will match the fresh `self` binding
-    // created below.
+    // this constructs a fresh `self` path
     let self_path = cx.expr_self(span);
     match *self_ptr {
         None => {
-            (self_path, respan(span, ast::SelfKind::Value(keywords::SelfValue.ident())))
+            (self_path, respan(span, SelfKind::Value(ast::Mutability::Immutable)))
         }
         Some(ref ptr) => {
             let self_ty = respan(
@@ -271,7 +270,7 @@ pub fn get_explicit_self(cx: &ExtCtxt, span: Span, self_ptr: &Option<PtrTy>)
                 match *ptr {
                     Borrowed(ref lt, mutbl) => {
                         let lt = lt.map(|s| cx.lifetime(span, cx.ident_of(s).name));
-                        ast::SelfKind::Region(lt, mutbl, keywords::SelfValue.ident())
+                        SelfKind::Region(lt, mutbl)
                     }
                     Raw(_) => cx.span_bug(span, "attempted to use *self in deriving definition")
                 });
index fd449372cb37640288895cb2794e09efbc161bad..0fad96c84ef3d2a859569768a4115995ebfc54b0 100644 (file)
@@ -13,10 +13,10 @@ use deriving::generic::*;
 use deriving::generic::ty::*;
 
 use syntax::ast::{MetaItem, Expr, Mutability};
-use syntax::codemap::Span;
 use syntax::ext::base::{ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 pub fn expand_deriving_hash(cx: &mut ExtCtxt,
                             span: Span,
@@ -99,5 +99,5 @@ fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure)
         stmts.push(call_hash(span, self_.clone()));
     }
 
-    cx.expr_block(cx.block(trait_span, stmts, None))
+    cx.expr_block(cx.block(trait_span, stmts))
 }
index 91c272c59c4a68ecbc40bba92e6ebc747a17ad10..169e8073661976cf56c709ac1b1aa3bcb641d3a5 100644 (file)
@@ -16,9 +16,10 @@ use syntax::ext::base::{ExtCtxt, SyntaxEnv, Annotatable};
 use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier};
 use syntax::ext::build::AstBuilder;
 use syntax::feature_gate;
-use syntax::codemap::Span;
+use syntax::codemap;
 use syntax::parse::token::{intern, intern_and_get_ident};
 use syntax::ptr::P;
+use syntax_pos::Span;
 
 macro_rules! pathvec {
     ($($x:ident)::+) => (
@@ -94,37 +95,7 @@ fn expand_derive(cx: &mut ExtCtxt,
             }
 
             let mut found_partial_eq = false;
-            let mut found_eq = false;
-
-            // This span is **very** sensitive and crucial to
-            // getting the stability behavior we want. What we are
-            // doing is marking the generated `#[derive_*]` with the
-            // span of the `#[deriving(...)]` attribute (the
-            // entire attribute, not just the `PartialEq` or `Eq`
-            // part), but with the current backtrace. The current
-            // backtrace will contain a topmost entry that IS this
-            // `#[deriving(...)]` attribute and with the
-            // "allow-unstable" flag set to true.
-            //
-            // Note that we do NOT use the span of the `Eq`
-            // text itself. You might think this is
-            // equivalent, because the `Eq` appears within the
-            // `#[deriving(Eq)]` attribute, and hence we would
-            // inherit the "allows unstable" from the
-            // backtrace.  But in fact this is not always the
-            // case. The actual source text that led to
-            // deriving can be `#[$attr]`, for example, where
-            // `$attr == deriving(Eq)`. In that case, the
-            // "#[derive_*]" would be considered to
-            // originate not from the deriving call but from
-            // text outside the deriving call, and hence would
-            // be forbidden from using unstable
-            // content.
-            //
-            // See tests src/run-pass/rfc1445 for
-            // examples. --nmatsakis
-            let span = Span { expn_id: cx.backtrace(), .. span };
-            assert!(cx.parse_sess.codemap().span_allows_unstable(span));
+            let mut eq_span = None;
 
             for titem in traits.iter().rev() {
                 let tname = match titem.node {
@@ -144,8 +115,19 @@ fn expand_derive(cx: &mut ExtCtxt,
                     continue;
                 }
 
+                let span = Span {
+                    expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
+                        call_site: titem.span,
+                        callee: codemap::NameAndSpan {
+                            format: codemap::MacroAttribute(intern(&format!("derive({})", tname))),
+                            span: Some(titem.span),
+                            allow_internal_unstable: true,
+                        },
+                    }), ..titem.span
+                };
+
                 if &tname[..] == "Eq" {
-                    found_eq = true;
+                    eq_span = Some(span);
                 } else if &tname[..] == "PartialEq" {
                     found_partial_eq = true;
                 }
@@ -157,12 +139,13 @@ fn expand_derive(cx: &mut ExtCtxt,
 
             // RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
             // `#[structural_match]` attribute.
-            if found_partial_eq && found_eq {
-                debug!("inserting structural_match with span {:?}", span);
-                let structural_match = intern_and_get_ident("structural_match");
-                item.attrs.push(cx.attribute(span,
-                                             cx.meta_word(span,
-                                                          structural_match)));
+            if let Some(eq_span) = eq_span {
+                if found_partial_eq {
+                    let structural_match = intern_and_get_ident("structural_match");
+                    item.attrs.push(cx.attribute(eq_span,
+                                                 cx.meta_word(eq_span,
+                                                              structural_match)));
+                }
             }
 
             item
@@ -315,8 +298,7 @@ fn call_intrinsic(cx: &ExtCtxt,
     let call = cx.expr_call_global(span, path, args);
 
     cx.expr_block(P(ast::Block {
-        stmts: vec![],
-        expr: Some(call),
+        stmts: vec![cx.stmt_expr(call)],
         id: ast::DUMMY_NODE_ID,
         rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
         span: span }))
index 63ec9cac07317d9f0a3eb8766b0ff7c42123f534..c6c4b6135c681e1eec0e83b0e05b8679cd006bb4 100644 (file)
  */
 
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::ext::base::*;
 use syntax::ext::base;
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
+use syntax_pos::Span;
+use syntax::tokenstream;
 
 use std::env;
 
-pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                               -> Box<base::MacResult+'cx> {
     let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") {
         None => return DummyResult::expr(sp),
@@ -56,7 +57,7 @@ pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenT
     MacEager::expr(e)
 }
 
-pub fn expand_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+pub fn expand_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
                        -> Box<base::MacResult+'cx> {
     let mut exprs = match get_exprs_from_tts(cx, sp, tts) {
         Some(ref exprs) if exprs.is_empty() => {
@@ -87,12 +88,9 @@ pub fn expand_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
         }
     };
 
-    match exprs.next() {
-        None => {}
-        Some(_) => {
-            cx.span_err(sp, "env! takes 1 or 2 arguments");
-            return DummyResult::expr(sp);
-        }
+    if let Some(_) = exprs.next() {
+        cx.span_err(sp, "env! takes 1 or 2 arguments");
+        return DummyResult::expr(sp);
     }
 
     let e = match env::var(&var[..]) {
index abfa65580646dd51df6d8b8c49ce0e4c464cff72..dc572e652c6713de2597706a78dc074e3014f00d 100644 (file)
@@ -14,13 +14,14 @@ use self::Position::*;
 use fmt_macros as parse;
 
 use syntax::ast;
-use syntax::codemap::{Span, respan, DUMMY_SP};
 use syntax::ext::base::*;
 use syntax::ext::base;
 use syntax::ext::build::AstBuilder;
 use syntax::fold::Folder;
 use syntax::parse::token::{self, keywords};
 use syntax::ptr::P;
+use syntax_pos::{Span, DUMMY_SP};
+use syntax::tokenstream;
 
 use std::collections::HashMap;
 
@@ -80,7 +81,7 @@ struct Context<'a, 'b:'a> {
 /// Some((fmtstr, unnamed arguments, ordering of named arguments,
 ///       named arguments))
 /// ```
-fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
+fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
               -> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<String>,
                          HashMap<String, P<ast::Expr>>)> {
     let mut args = Vec::new();
@@ -125,16 +126,13 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
 
             panictry!(p.expect(&token::Eq));
             let e = panictry!(p.parse_expr());
-            match names.get(name) {
-                None => {}
-                Some(prev) => {
-                    ecx.struct_span_err(e.span,
-                                        &format!("duplicate argument named `{}`",
-                                                 name))
-                       .span_note(prev.span, "previously here")
-                       .emit();
-                    continue
-                }
+            if let Some(prev) = names.get(name) {
+                ecx.struct_span_err(e.span,
+                                    &format!("duplicate argument named `{}`",
+                                             name))
+                    .span_note(prev.span, "previously here")
+                    .emit();
+                continue;
             }
             order.push(name.to_string());
             names.insert(name.to_string(), e);
@@ -441,12 +439,14 @@ impl<'a, 'b> Context<'a, 'b> {
 
         let name = ecx.ident_of(name);
         let item = ecx.item(sp, name, vec![], st);
-        let decl = respan(sp, ast::DeclKind::Item(item));
+        let stmt = ast::Stmt {
+            id: ast::DUMMY_NODE_ID,
+            node: ast::StmtKind::Item(item),
+            span: sp,
+        };
 
         // Wrap the declaration in a block so that it forms a single expression.
-        ecx.expr_block(ecx.block(sp,
-            vec![respan(sp, ast::StmtKind::Decl(P(decl), ast::DUMMY_NODE_ID))],
-            Some(ecx.expr_ident(sp, name))))
+        ecx.expr_block(ecx.block(sp, vec![stmt, ecx.stmt_expr(ecx.expr_ident(sp, name))]))
     }
 
     /// Actually builds the expression which the iformat! block will be expanded
@@ -606,7 +606,7 @@ impl<'a, 'b> Context<'a, 'b> {
 }
 
 pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
-                               tts: &[ast::TokenTree])
+                               tts: &[tokenstream::TokenTree])
                                -> Box<base::MacResult+'cx> {
 
     match parse_args(ecx, sp, tts) {
@@ -662,13 +662,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
             Some(piece) => {
                 if !parser.errors.is_empty() { break }
                 cx.verify_piece(&piece);
-                match cx.trans_piece(&piece) {
-                    Some(piece) => {
-                        let s = cx.trans_literal_string();
-                        cx.str_pieces.push(s);
-                        cx.pieces.push(piece);
-                    }
-                    None => {}
+                if let Some(piece) = cx.trans_piece(&piece) {
+                    let s = cx.trans_literal_string();
+                    cx.str_pieces.push(s);
+                    cx.pieces.push(piece);
                 }
             }
             None => break
index 8f5362b4d2895c270ca10440b0459d76b9793089..17b200bac58c5440cead5e7f212a1f0f580fcc25 100644 (file)
@@ -26,6 +26,8 @@ extern crate fmt_macros;
 #[macro_use] extern crate log;
 #[macro_use]
 extern crate syntax;
+extern crate syntax_pos;
+extern crate rustc_errors as errors;
 
 use syntax::ext::base::{MacroExpanderFn, NormalTT};
 use syntax::ext::base::{SyntaxEnv, SyntaxExtension};
index ee944abb645dc8b86cb04b98645732b716fbc4cf..9645c5bb42723dbdc43831a5fec0ef321ee3e83c 100644 (file)
@@ -8,15 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use syntax::ast;
-use syntax::codemap;
 use syntax::ext::base;
 use syntax::feature_gate;
 use syntax::print;
+use syntax::tokenstream;
+use syntax_pos;
 
 pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt,
-                              sp: codemap::Span,
-                              tts: &[ast::TokenTree])
+                              sp: syntax_pos::Span,
+                              tts: &[tokenstream::TokenTree])
                               -> Box<base::MacResult+'cx> {
     if !cx.ecfg.enable_log_syntax() {
         feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
index 7b1e985442adb93d9f577c4019b5a1678e798ae8..ad396d38de9f56b7a2470a9bd6be91c760919187 100644 (file)
@@ -8,13 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use syntax::ast::TokenTree;
-use syntax::codemap::Span;
 use syntax::ext::base::ExtCtxt;
 use syntax::ext::base;
 use syntax::feature_gate;
 use syntax::parse::token::keywords;
-
+use syntax_pos::Span;
+use syntax::tokenstream::TokenTree;
 
 pub fn expand_trace_macros(cx: &mut ExtCtxt,
                            sp: Span,
diff --git a/src/libsyntax_pos/Cargo.toml b/src/libsyntax_pos/Cargo.toml
new file mode 100644 (file)
index 0000000..760aaa8
--- /dev/null
@@ -0,0 +1,12 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "syntax_pos"
+version = "0.0.0"
+
+[lib]
+name = "syntax_pos"
+path = "lib.rs"
+crate-type = ["dylib"]
+
+[dependencies]
+serialize = { path = "../libserialize" }
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
new file mode 100644 (file)
index 0000000..39bb595
--- /dev/null
@@ -0,0 +1,667 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! The source positions and related helper functions
+//!
+//! # Note
+//!
+//! This API is completely unstable and subject to change.
+
+#![crate_name = "syntax_pos"]
+#![unstable(feature = "rustc_private", issue = "27812")]
+#![crate_type = "dylib"]
+#![crate_type = "rlib"]
+#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+      html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
+      html_root_url = "https://doc.rust-lang.org/nightly/")]
+#![cfg_attr(not(stage0), deny(warnings))]
+
+#![feature(custom_attribute)]
+#![allow(unused_attributes)]
+#![feature(rustc_private)]
+#![feature(staged_api)]
+#![feature(question_mark)]
+
+use std::cell::{Cell, RefCell};
+use std::ops::{Add, Sub};
+use std::rc::Rc;
+use std::cmp;
+
+use std::fmt;
+
+use serialize::{Encodable, Decodable, Encoder, Decoder};
+
+extern crate serialize;
+extern crate serialize as rustc_serialize; // used by deriving
+
+pub type FileName = String;
+
+/// Spans represent a region of code, used for error reporting. Positions in spans
+/// are *absolute* positions from the beginning of the codemap, not positions
+/// relative to FileMaps. Methods on the CodeMap can be used to relate spans back
+/// to the original source.
+/// You must be careful if the span crosses more than one file - you will not be
+/// able to use many of the functions on spans in codemap and you cannot assume
+/// that the length of the span = hi - lo; there may be space in the BytePos
+/// range between files.
+#[derive(Clone, Copy, Hash, PartialEq, Eq)]
+pub struct Span {
+    pub lo: BytePos,
+    pub hi: BytePos,
+    /// Information about where the macro came from, if this piece of
+    /// code was created by a macro expansion.
+    pub expn_id: ExpnId
+}
+
+/// A collection of spans. Spans have two orthogonal attributes:
+///
+/// - they can be *primary spans*. In this case they are the locus of
+///   the error, and would be rendered with `^^^`.
+/// - they can have a *label*. In this case, the label is written next
+///   to the mark in the snippet when we render.
+#[derive(Clone)]
+pub struct MultiSpan {
+    primary_spans: Vec<Span>,
+    span_labels: Vec<(Span, String)>,
+}
+
+impl Span {
+    /// Returns a new span representing just the end-point of this span
+    pub fn end_point(self) -> Span {
+        let lo = cmp::max(self.hi.0 - 1, self.lo.0);
+        Span { lo: BytePos(lo), hi: self.hi, expn_id: self.expn_id}
+    }
+
+    /// Returns `self` if `self` is not the dummy span, and `other` otherwise.
+    pub fn substitute_dummy(self, other: Span) -> Span {
+        if self.source_equal(&DUMMY_SP) { other } else { self }
+    }
+
+    pub fn contains(self, other: Span) -> bool {
+        self.lo <= other.lo && other.hi <= self.hi
+    }
+
+    /// Return true if the spans are equal with regards to the source text.
+    ///
+    /// Use this instead of `==` when either span could be generated code,
+    /// and you only care that they point to the same bytes of source text.
+    pub fn source_equal(&self, other: &Span) -> bool {
+        self.lo == other.lo && self.hi == other.hi
+    }
+
+    /// Returns `Some(span)`, a union of `self` and `other`, on overlap.
+    pub fn merge(self, other: Span) -> Option<Span> {
+        if self.expn_id != other.expn_id {
+            return None;
+        }
+
+        if (self.lo <= other.lo && self.hi > other.lo) ||
+           (self.lo >= other.lo && self.lo < other.hi) {
+            Some(Span {
+                lo: cmp::min(self.lo, other.lo),
+                hi: cmp::max(self.hi, other.hi),
+                expn_id: self.expn_id,
+            })
+        } else {
+            None
+        }
+    }
+
+    /// Returns `Some(span)`, where the start is trimmed by the end of `other`
+    pub fn trim_start(self, other: Span) -> Option<Span> {
+        if self.hi > other.hi {
+            Some(Span { lo: cmp::max(self.lo, other.hi), .. self })
+        } else {
+            None
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct SpanLabel {
+    /// The span we are going to include in the final snippet.
+    pub span: Span,
+
+    /// Is this a primary span? This is the "locus" of the message,
+    /// and is indicated with a `^^^^` underline, versus `----`.
+    pub is_primary: bool,
+
+    /// What label should we attach to this span (if any)?
+    pub label: Option<String>,
+}
+
+impl Encodable for Span {
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        s.emit_struct("Span", 2, |s| {
+            s.emit_struct_field("lo", 0, |s| {
+                self.lo.encode(s)
+            })?;
+
+            s.emit_struct_field("hi", 1, |s| {
+                self.hi.encode(s)
+            })
+        })
+    }
+}
+
+impl Decodable for Span {
+    fn decode<D: Decoder>(d: &mut D) -> Result<Span, D::Error> {
+        d.read_struct("Span", 2, |d| {
+            let lo = d.read_struct_field("lo", 0, |d| {
+                BytePos::decode(d)
+            })?;
+
+            let hi = d.read_struct_field("hi", 1, |d| {
+                BytePos::decode(d)
+            })?;
+
+            Ok(mk_sp(lo, hi))
+        })
+    }
+}
+
+fn default_span_debug(span: Span, f: &mut fmt::Formatter) -> fmt::Result {
+    write!(f, "Span {{ lo: {:?}, hi: {:?}, expn_id: {:?} }}",
+           span.lo, span.hi, span.expn_id)
+}
+
+impl fmt::Debug for Span {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        SPAN_DEBUG.with(|span_debug| span_debug.get()(*self, f))
+    }
+}
+
+pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION };
+
+// Generic span to be used for code originating from the command line
+pub const COMMAND_LINE_SP: Span = Span { lo: BytePos(0),
+                                         hi: BytePos(0),
+                                         expn_id: COMMAND_LINE_EXPN };
+
+impl MultiSpan {
+    pub fn new() -> MultiSpan {
+        MultiSpan {
+            primary_spans: vec![],
+            span_labels: vec![]
+        }
+    }
+
+    pub fn from_span(primary_span: Span) -> MultiSpan {
+        MultiSpan {
+            primary_spans: vec![primary_span],
+            span_labels: vec![]
+        }
+    }
+
+    pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
+        MultiSpan {
+            primary_spans: vec,
+            span_labels: vec![]
+        }
+    }
+
+    pub fn push_span_label(&mut self, span: Span, label: String) {
+        self.span_labels.push((span, label));
+    }
+
+    /// Selects the first primary span (if any)
+    pub fn primary_span(&self) -> Option<Span> {
+        self.primary_spans.first().cloned()
+    }
+
+    /// Returns all primary spans.
+    pub fn primary_spans(&self) -> &[Span] {
+        &self.primary_spans
+    }
+
+    /// Returns the strings to highlight. We always ensure that there
+    /// is an entry for each of the primary spans -- for each primary
+    /// span P, if there is at least one label with span P, we return
+    /// those labels (marked as primary). But otherwise we return
+    /// `SpanLabel` instances with empty labels.
+    pub fn span_labels(&self) -> Vec<SpanLabel> {
+        let is_primary = |span| self.primary_spans.contains(&span);
+        let mut span_labels = vec![];
+
+        for &(span, ref label) in &self.span_labels {
+            span_labels.push(SpanLabel {
+                span: span,
+                is_primary: is_primary(span),
+                label: Some(label.clone())
+            });
+        }
+
+        for &span in &self.primary_spans {
+            if !span_labels.iter().any(|sl| sl.span == span) {
+                span_labels.push(SpanLabel {
+                    span: span,
+                    is_primary: true,
+                    label: None
+                });
+            }
+        }
+
+        span_labels
+    }
+}
+
+impl From<Span> for MultiSpan {
+    fn from(span: Span) -> MultiSpan {
+        MultiSpan::from_span(span)
+    }
+}
+
+#[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy)]
+pub struct ExpnId(pub u32);
+
+pub const NO_EXPANSION: ExpnId = ExpnId(!0);
+// For code appearing from the command line
+pub const COMMAND_LINE_EXPN: ExpnId = ExpnId(!1);
+
+impl ExpnId {
+    pub fn from_u32(id: u32) -> ExpnId {
+        ExpnId(id)
+    }
+
+    pub fn into_u32(self) -> u32 {
+        self.0
+    }
+}
+
+/// Identifies an offset of a multi-byte character in a FileMap
+#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Eq, PartialEq)]
+pub struct MultiByteChar {
+    /// The absolute offset of the character in the CodeMap
+    pub pos: BytePos,
+    /// The number of bytes, >=2
+    pub bytes: usize,
+}
+
+/// A single source in the CodeMap.
+pub struct FileMap {
+    /// The name of the file that the source came from, source that doesn't
+    /// originate from files has names between angle brackets by convention,
+    /// e.g. `<anon>`
+    pub name: FileName,
+    /// The absolute path of the file that the source came from.
+    pub abs_path: Option<FileName>,
+    /// The complete source code
+    pub src: Option<Rc<String>>,
+    /// The start position of this source in the CodeMap
+    pub start_pos: BytePos,
+    /// The end position of this source in the CodeMap
+    pub end_pos: BytePos,
+    /// Locations of lines beginnings in the source code
+    pub lines: RefCell<Vec<BytePos>>,
+    /// Locations of multi-byte characters in the source code
+    pub multibyte_chars: RefCell<Vec<MultiByteChar>>,
+}
+
+impl Encodable for FileMap {
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        s.emit_struct("FileMap", 6, |s| {
+            s.emit_struct_field("name", 0, |s| self.name.encode(s))?;
+            s.emit_struct_field("abs_path", 1, |s| self.abs_path.encode(s))?;
+            s.emit_struct_field("start_pos", 2, |s| self.start_pos.encode(s))?;
+            s.emit_struct_field("end_pos", 3, |s| self.end_pos.encode(s))?;
+            s.emit_struct_field("lines", 4, |s| {
+                let lines = self.lines.borrow();
+                // store the length
+                s.emit_u32(lines.len() as u32)?;
+
+                if !lines.is_empty() {
+                    // In order to preserve some space, we exploit the fact that
+                    // the lines list is sorted and individual lines are
+                    // probably not that long. Because of that we can store lines
+                    // as a difference list, using as little space as possible
+                    // for the differences.
+                    let max_line_length = if lines.len() == 1 {
+                        0
+                    } else {
+                        lines.windows(2)
+                             .map(|w| w[1] - w[0])
+                             .map(|bp| bp.to_usize())
+                             .max()
+                             .unwrap()
+                    };
+
+                    let bytes_per_diff: u8 = match max_line_length {
+                        0 ... 0xFF => 1,
+                        0x100 ... 0xFFFF => 2,
+                        _ => 4
+                    };
+
+                    // Encode the number of bytes used per diff.
+                    bytes_per_diff.encode(s)?;
+
+                    // Encode the first element.
+                    lines[0].encode(s)?;
+
+                    let diff_iter = (&lines[..]).windows(2)
+                                                .map(|w| (w[1] - w[0]));
+
+                    match bytes_per_diff {
+                        1 => for diff in diff_iter { (diff.0 as u8).encode(s)? },
+                        2 => for diff in diff_iter { (diff.0 as u16).encode(s)? },
+                        4 => for diff in diff_iter { diff.0.encode(s)? },
+                        _ => unreachable!()
+                    }
+                }
+
+                Ok(())
+            })?;
+            s.emit_struct_field("multibyte_chars", 5, |s| {
+                (*self.multibyte_chars.borrow()).encode(s)
+            })
+        })
+    }
+}
+
+impl Decodable for FileMap {
+    fn decode<D: Decoder>(d: &mut D) -> Result<FileMap, D::Error> {
+
+        d.read_struct("FileMap", 6, |d| {
+            let name: String = d.read_struct_field("name", 0, |d| Decodable::decode(d))?;
+            let abs_path: Option<String> =
+                d.read_struct_field("abs_path", 1, |d| Decodable::decode(d))?;
+            let start_pos: BytePos = d.read_struct_field("start_pos", 2, |d| Decodable::decode(d))?;
+            let end_pos: BytePos = d.read_struct_field("end_pos", 3, |d| Decodable::decode(d))?;
+            let lines: Vec<BytePos> = d.read_struct_field("lines", 4, |d| {
+                let num_lines: u32 = Decodable::decode(d)?;
+                let mut lines = Vec::with_capacity(num_lines as usize);
+
+                if num_lines > 0 {
+                    // Read the number of bytes used per diff.
+                    let bytes_per_diff: u8 = Decodable::decode(d)?;
+
+                    // Read the first element.
+                    let mut line_start: BytePos = Decodable::decode(d)?;
+                    lines.push(line_start);
+
+                    for _ in 1..num_lines {
+                        let diff = match bytes_per_diff {
+                            1 => d.read_u8()? as u32,
+                            2 => d.read_u16()? as u32,
+                            4 => d.read_u32()?,
+                            _ => unreachable!()
+                        };
+
+                        line_start = line_start + BytePos(diff);
+
+                        lines.push(line_start);
+                    }
+                }
+
+                Ok(lines)
+            })?;
+            let multibyte_chars: Vec<MultiByteChar> =
+                d.read_struct_field("multibyte_chars", 5, |d| Decodable::decode(d))?;
+            Ok(FileMap {
+                name: name,
+                abs_path: abs_path,
+                start_pos: start_pos,
+                end_pos: end_pos,
+                src: None,
+                lines: RefCell::new(lines),
+                multibyte_chars: RefCell::new(multibyte_chars)
+            })
+        })
+    }
+}
+
+impl fmt::Debug for FileMap {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "FileMap({})", self.name)
+    }
+}
+
+impl FileMap {
+    /// EFFECT: register a start-of-line offset in the
+    /// table of line-beginnings.
+    /// UNCHECKED INVARIANT: these offsets must be added in the right
+    /// order and must be in the right places; there is shared knowledge
+    /// about what ends a line between this file and parse.rs
+    /// WARNING: pos param here is the offset relative to start of CodeMap,
+    /// and CodeMap will append a newline when adding a filemap without a newline at the end,
+    /// so the safe way to call this is with value calculated as
+    /// filemap.start_pos + newline_offset_relative_to_the_start_of_filemap.
+    pub fn next_line(&self, pos: BytePos) {
+        // the new charpos must be > the last one (or it's the first one).
+        let mut lines = self.lines.borrow_mut();
+        let line_len = lines.len();
+        assert!(line_len == 0 || ((*lines)[line_len - 1] < pos));
+        lines.push(pos);
+    }
+
+    /// get a line from the list of pre-computed line-beginnings.
+    /// line-number here is 0-based.
+    pub fn get_line(&self, line_number: usize) -> Option<&str> {
+        match self.src {
+            Some(ref src) => {
+                let lines = self.lines.borrow();
+                lines.get(line_number).map(|&line| {
+                    let begin: BytePos = line - self.start_pos;
+                    let begin = begin.to_usize();
+                    // We can't use `lines.get(line_number+1)` because we might
+                    // be parsing when we call this function and thus the current
+                    // line is the last one we have line info for.
+                    let slice = &src[begin..];
+                    match slice.find('\n') {
+                        Some(e) => &slice[..e],
+                        None => slice
+                    }
+                })
+            }
+            None => None
+        }
+    }
+
+    pub fn record_multibyte_char(&self, pos: BytePos, bytes: usize) {
+        assert!(bytes >=2 && bytes <= 4);
+        let mbc = MultiByteChar {
+            pos: pos,
+            bytes: bytes,
+        };
+        self.multibyte_chars.borrow_mut().push(mbc);
+    }
+
+    pub fn is_real_file(&self) -> bool {
+        !(self.name.starts_with("<") &&
+          self.name.ends_with(">"))
+    }
+
+    pub fn is_imported(&self) -> bool {
+        self.src.is_none()
+    }
+
+    pub fn count_lines(&self) -> usize {
+        self.lines.borrow().len()
+    }
+}
+
+// _____________________________________________________________________________
+// Pos, BytePos, CharPos
+//
+
+pub trait Pos {
+    fn from_usize(n: usize) -> Self;
+    fn to_usize(&self) -> usize;
+}
+
+/// A byte offset. Keep this small (currently 32-bits), as AST contains
+/// a lot of them.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+pub struct BytePos(pub u32);
+
+/// A character offset. Because of multibyte utf8 characters, a byte offset
+/// is not equivalent to a character offset. The CodeMap will convert BytePos
+/// values to CharPos values as necessary.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+pub struct CharPos(pub usize);
+
+// FIXME: Lots of boilerplate in these impls, but so far my attempts to fix
+// have been unsuccessful
+
+impl Pos for BytePos {
+    fn from_usize(n: usize) -> BytePos { BytePos(n as u32) }
+    fn to_usize(&self) -> usize { let BytePos(n) = *self; n as usize }
+}
+
+impl Add for BytePos {
+    type Output = BytePos;
+
+    fn add(self, rhs: BytePos) -> BytePos {
+        BytePos((self.to_usize() + rhs.to_usize()) as u32)
+    }
+}
+
+impl Sub for BytePos {
+    type Output = BytePos;
+
+    fn sub(self, rhs: BytePos) -> BytePos {
+        BytePos((self.to_usize() - rhs.to_usize()) as u32)
+    }
+}
+
+impl Encodable for BytePos {
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        s.emit_u32(self.0)
+    }
+}
+
+impl Decodable for BytePos {
+    fn decode<D: Decoder>(d: &mut D) -> Result<BytePos, D::Error> {
+        Ok(BytePos(d.read_u32()?))
+    }
+}
+
+impl Pos for CharPos {
+    fn from_usize(n: usize) -> CharPos { CharPos(n) }
+    fn to_usize(&self) -> usize { let CharPos(n) = *self; n }
+}
+
+impl Add for CharPos {
+    type Output = CharPos;
+
+    fn add(self, rhs: CharPos) -> CharPos {
+        CharPos(self.to_usize() + rhs.to_usize())
+    }
+}
+
+impl Sub for CharPos {
+    type Output = CharPos;
+
+    fn sub(self, rhs: CharPos) -> CharPos {
+        CharPos(self.to_usize() - rhs.to_usize())
+    }
+}
+
+// _____________________________________________________________________________
+// Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
+//
+
+/// A source code location used for error reporting
+#[derive(Debug)]
+pub struct Loc {
+    /// Information about the original source
+    pub file: Rc<FileMap>,
+    /// The (1-based) line number
+    pub line: usize,
+    /// The (0-based) column offset
+    pub col: CharPos
+}
+
+/// A source code location used as the result of lookup_char_pos_adj
+// Actually, *none* of the clients use the filename *or* file field;
+// perhaps they should just be removed.
+#[derive(Debug)]
+pub struct LocWithOpt {
+    pub filename: FileName,
+    pub line: usize,
+    pub col: CharPos,
+    pub file: Option<Rc<FileMap>>,
+}
+
+// used to be structural records. Better names, anyone?
+#[derive(Debug)]
+pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize }
+#[derive(Debug)]
+pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct LineInfo {
+    /// Index of line, starting from 0.
+    pub line_index: usize,
+
+    /// Column in line where span begins, starting from 0.
+    pub start_col: CharPos,
+
+    /// Column in line where span ends, starting from 0, exclusive.
+    pub end_col: CharPos,
+}
+
+pub struct FileLines {
+    pub file: Rc<FileMap>,
+    pub lines: Vec<LineInfo>
+}
+
+thread_local!(pub static SPAN_DEBUG: Cell<fn(Span, &mut fmt::Formatter) -> fmt::Result> =
+                Cell::new(default_span_debug));
+
+/* assuming that we're not in macro expansion */
+pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
+    Span {lo: lo, hi: hi, expn_id: NO_EXPANSION}
+}
+
+pub struct MacroBacktrace {
+    /// span where macro was applied to generate this code
+    pub call_site: Span,
+
+    /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
+    pub macro_decl_name: String,
+
+    /// span where macro was defined (if known)
+    pub def_site_span: Option<Span>,
+}
+
+// _____________________________________________________________________________
+// SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions
+//
+
+pub type FileLinesResult = Result<FileLines, SpanLinesError>;
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum SpanLinesError {
+    IllFormedSpan(Span),
+    DistinctSources(DistinctSources),
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum SpanSnippetError {
+    IllFormedSpan(Span),
+    DistinctSources(DistinctSources),
+    MalformedForCodemap(MalformedCodemapPositions),
+    SourceNotAvailable { filename: String }
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct DistinctSources {
+    pub begin: (String, BytePos),
+    pub end: (String, BytePos)
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct MalformedCodemapPositions {
+    pub name: String,
+    pub source_len: usize,
+    pub begin_pos: BytePos,
+    pub end_pos: BytePos
+}
+
index 88be3ade83922b399abb116253c386c8dcc58d1b..c90c93e75acd87f930f76db4dc0fb43691a72132 100644 (file)
@@ -747,12 +747,9 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu
             PadOnRight => t.desc.name.as_slice().len(),
         }
     }
-    match tests.iter().max_by_key(|t| len_if_padded(*t)) {
-        Some(t) => {
-            let n = t.desc.name.as_slice();
-            st.max_name_len = n.len();
-        }
-        None => {}
+    if let Some(t) = tests.iter().max_by_key(|t| len_if_padded(*t)) {
+        let n = t.desc.name.as_slice();
+        st.max_name_len = n.len();
     }
     run_tests(opts, tests, |x| callback(&x, &mut st))?;
     return st.write_run_finish();
index 335b6d67209dedcde22a3ecbcaf0a1cc0298094d..1883f0aba23eb940acc0b97a08a95e047be4040a 100644 (file)
@@ -11,7 +11,7 @@
 #![allow(missing_docs)]
 #![allow(deprecated)] // Float
 
-use std::cmp::Ordering::{self, Less, Greater, Equal};
+use std::cmp::Ordering::{self, Equal, Greater, Less};
 use std::mem;
 
 fn local_cmp(x: f64, y: f64) -> Ordering {
@@ -35,7 +35,6 @@ fn local_sort(v: &mut [f64]) {
 
 /// Trait that provides simple descriptive statistics on a univariate set of numeric samples.
 pub trait Stats {
-
     /// Sum of the samples.
     ///
     /// Note: this method sacrifices performance at the altar of accuracy
index 3ff61fd93d758df3b30bbe69173a26107b8e65e2..add45ccb362e96bc2348c9938415ddee5c00ac79 100644 (file)
@@ -27,4 +27,3 @@ extern crate libc;
 mod libunwind;
 #[cfg(not(target_env = "msvc"))]
 pub use libunwind::*;
-
index ce74e5de3d4600473f4f8fa857286c2f0316029d..aadfe202afe796ba6d05257e5df80196e0aff51d 100644 (file)
@@ -38,7 +38,7 @@ pub enum _Unwind_State {
     _US_UNWIND_FRAME_RESUME = 2,
     _US_ACTION_MASK = 3,
     _US_FORCE_UNWIND = 8,
-    _US_END_OF_STACK = 16
+    _US_END_OF_STACK = 16,
 }
 
 #[repr(C)]
@@ -59,9 +59,8 @@ pub type _Unwind_Exception_Class = u64;
 
 pub type _Unwind_Word = libc::uintptr_t;
 
-pub type _Unwind_Trace_Fn =
-        extern fn(ctx: *mut _Unwind_Context,
-                  arg: *mut libc::c_void) -> _Unwind_Reason_Code;
+pub type _Unwind_Trace_Fn = extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut libc::c_void)
+                                          -> _Unwind_Reason_Code;
 
 #[cfg(target_arch = "x86")]
 pub const unwinder_private_data_size: usize = 5;
@@ -97,9 +96,8 @@ pub struct _Unwind_Exception {
 
 pub enum _Unwind_Context {}
 
-pub type _Unwind_Exception_Cleanup_Fn =
-        extern "C" fn(unwind_code: _Unwind_Reason_Code,
-                      exception: *mut _Unwind_Exception);
+pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code,
+                                                      exception: *mut _Unwind_Exception);
 
 #[cfg_attr(any(all(target_os = "linux", not(target_env = "musl")),
                target_os = "freebsd",
@@ -127,20 +125,18 @@ pub type _Unwind_Exception_Cleanup_Fn =
 #[cfg_attr(all(target_os = "windows", target_env = "gnu"),
            link(name = "gcc_eh"))]
 #[cfg(not(cargobuild))]
-extern {}
+extern "C" {}
 
-extern {
+extern "C" {
     // iOS on armv7 uses SjLj exceptions and requires to link
     // against corresponding routine (..._SjLj_...)
     #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
     #[unwind]
-    pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception)
-                                  -> _Unwind_Reason_Code;
+    pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
 
     #[cfg(all(target_os = "ios", target_arch = "arm"))]
     #[unwind]
-    fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception)
-                                   -> _Unwind_Reason_Code;
+    fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
 
     pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
 
@@ -151,19 +147,18 @@ extern {
     #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
     pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
                              trace_argument: *mut libc::c_void)
-                -> _Unwind_Reason_Code;
+                             -> _Unwind_Reason_Code;
 
     // available since GCC 4.2.0, should be fine for our purpose
     #[cfg(all(not(all(target_os = "android", target_arch = "arm")),
               not(all(target_os = "linux", target_arch = "arm"))))]
     pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
                              ip_before_insn: *mut libc::c_int)
-                -> libc::uintptr_t;
+                             -> libc::uintptr_t;
 
     #[cfg(all(not(target_os = "android"),
               not(all(target_os = "linux", target_arch = "arm"))))]
-    pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
-        -> *mut libc::c_void;
+    pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) -> *mut libc::c_void;
 }
 
 // ... and now we just providing access to SjLj counterspart
@@ -171,8 +166,7 @@ extern {
 // (see also comment above regarding _Unwind_RaiseException)
 #[cfg(all(target_os = "ios", target_arch = "arm"))]
 #[inline]
-pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception)
-                                     -> _Unwind_Reason_Code {
+pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code {
     _Unwind_SjLj_RaiseException(exc)
 }
 
@@ -207,18 +201,20 @@ pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
     }
 
     type _Unwind_Word = libc::c_uint;
-    extern {
+    extern "C" {
         fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
                            klass: _Unwind_VRS_RegClass,
                            word: _Unwind_Word,
                            repr: _Unwind_VRS_DataRepresentation,
                            data: *mut libc::c_void)
-            -> _Unwind_VRS_Result;
+                           -> _Unwind_VRS_Result;
     }
 
     let mut val: _Unwind_Word = 0;
     let ptr = &mut val as *mut _Unwind_Word;
-    let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15,
+    let _ = _Unwind_VRS_Get(ctx,
+                            _Unwind_VRS_RegClass::_UVRSC_CORE,
+                            15,
                             _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
                             ptr as *mut libc::c_void);
     (val & !1) as libc::uintptr_t
@@ -230,8 +226,7 @@ pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
           all(target_os = "linux", target_arch = "arm")))]
 pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
                                 ip_before_insn: *mut libc::c_int)
-    -> libc::uintptr_t
-{
+                                -> libc::uintptr_t {
     *ip_before_insn = 0;
     _Unwind_GetIP(ctx)
 }
@@ -240,8 +235,6 @@ pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
 // a no-op
 #[cfg(any(target_os = "android",
           all(target_os = "linux", target_arch = "arm")))]
-pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
-    -> *mut libc::c_void
-{
+pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) -> *mut libc::c_void {
     pc
 }
index e2731dab41a2dd627c25f80bba5e6c51b7595225..8cd7315b418c1a95474e3d8cfebc73bc2ce14f83 100644 (file)
@@ -1154,13 +1154,13 @@ char_link(hoedown_buffer *ob, hoedown_document *doc, uint8_t *data, size_t offse
                while (i < size) {
                        if (data[i] == '\\') i += 2;
                        else if (data[i] == '(' && i != 0) {
-                               nb_p++; i++;
+                               nb_p++;
                        }
                        else if (data[i] == ')') {
                                if (nb_p == 0) break;
-                               else nb_p--; i++;
+                               else nb_p--;
                        } else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break;
-                       else i++;
+                       i++;
                }
 
                if (i >= size) goto cleanup;
diff --git a/src/rt/hoedown/src/html_blocks.c b/src/rt/hoedown/src/html_blocks.c
deleted file mode 100644 (file)
index f5e9dce..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/* ANSI-C code produced by gperf version 3.0.3 */
-/* Command-line: gperf -L ANSI-C -N hoedown_find_block_tag -c -C -E -S 1 --ignore-case -m100 html_block_names.gperf  */
-/* Computed positions: -k'1-2' */
-
-#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
-      && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
-      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
-      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
-      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
-      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
-      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
-      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
-      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
-      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
-      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
-      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
-      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
-      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
-      && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
-      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
-      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
-      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
-      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
-      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
-      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
-      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
-      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
-/* The character set is not based on ISO-646.  */
-#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
-#endif
-
-/* maximum key range = 24, duplicates = 0 */
-
-#ifndef GPERF_DOWNCASE
-#define GPERF_DOWNCASE 1
-static unsigned char gperf_downcase[256] =
-  {
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
-     15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
-     30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
-     45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
-     60,  61,  62,  63,  64,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106,
-    107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
-    122,  91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103, 104,
-    105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
-    120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
-    135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
-    150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
-    165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
-    180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
-    195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
-    210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
-    225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
-    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
-    255
-  };
-#endif
-
-#ifndef GPERF_CASE_STRNCMP
-#define GPERF_CASE_STRNCMP 1
-static int
-gperf_case_strncmp (register const char *s1, register const char *s2, register unsigned int n)
-{
-  for (; n > 0;)
-    {
-      unsigned char c1 = gperf_downcase[(unsigned char)*s1++];
-      unsigned char c2 = gperf_downcase[(unsigned char)*s2++];
-      if (c1 != 0 && c1 == c2)
-        {
-          n--;
-          continue;
-        }
-      return (int)c1 - (int)c2;
-    }
-  return 0;
-}
-#endif
-
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-static unsigned int
-hash (register const char *str, register unsigned int len)
-{
-  static const unsigned char asso_values[] =
-    {
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      22, 21, 19, 18, 16,  0, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25,  1, 25,  0, 25,
-       1,  0,  0, 13,  0, 25, 25, 11,  2,  1,
-       0, 25, 25,  5,  0,  2, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25,  1, 25,
-       0, 25,  1,  0,  0, 13,  0, 25, 25, 11,
-       2,  1,  0, 25, 25,  5,  0,  2, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-      25, 25, 25, 25, 25, 25, 25
-    };
-  register int hval = (int)len;
-
-  switch (hval)
-    {
-      default:
-        hval += asso_values[(unsigned char)str[1]+1];
-      /*FALLTHROUGH*/
-      case 1:
-        hval += asso_values[(unsigned char)str[0]];
-        break;
-    }
-  return hval;
-}
-
-#ifdef __GNUC__
-__inline
-#ifdef __GNUC_STDC_INLINE__
-__attribute__ ((__gnu_inline__))
-#endif
-#endif
-const char *
-hoedown_find_block_tag (register const char *str, register unsigned int len)
-{
-  enum
-    {
-      TOTAL_KEYWORDS = 24,
-      MIN_WORD_LENGTH = 1,
-      MAX_WORD_LENGTH = 10,
-      MIN_HASH_VALUE = 1,
-      MAX_HASH_VALUE = 24
-    };
-
-  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
-    {
-      register int key = hash (str, len);
-
-      if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)
-        {
-          register const char *resword;
-
-          switch (key - 1)
-            {
-              case 0:
-                resword = "p";
-                goto compare;
-              case 1:
-                resword = "h6";
-                goto compare;
-              case 2:
-                resword = "div";
-                goto compare;
-              case 3:
-                resword = "del";
-                goto compare;
-              case 4:
-                resword = "form";
-                goto compare;
-              case 5:
-                resword = "table";
-                goto compare;
-              case 6:
-                resword = "figure";
-                goto compare;
-              case 7:
-                resword = "pre";
-                goto compare;
-              case 8:
-                resword = "fieldset";
-                goto compare;
-              case 9:
-                resword = "noscript";
-                goto compare;
-              case 10:
-                resword = "script";
-                goto compare;
-              case 11:
-                resword = "style";
-                goto compare;
-              case 12:
-                resword = "dl";
-                goto compare;
-              case 13:
-                resword = "ol";
-                goto compare;
-              case 14:
-                resword = "ul";
-                goto compare;
-              case 15:
-                resword = "math";
-                goto compare;
-              case 16:
-                resword = "ins";
-                goto compare;
-              case 17:
-                resword = "h5";
-                goto compare;
-              case 18:
-                resword = "iframe";
-                goto compare;
-              case 19:
-                resword = "h4";
-                goto compare;
-              case 20:
-                resword = "h3";
-                goto compare;
-              case 21:
-                resword = "blockquote";
-                goto compare;
-              case 22:
-                resword = "h2";
-                goto compare;
-              case 23:
-                resword = "h1";
-                goto compare;
-            }
-          return 0;
-        compare:
-          if ((((unsigned char)*str ^ (unsigned char)*resword) & ~32) == 0 && !gperf_case_strncmp (str, resword, len) && resword[len] == '\0')
-            return resword;
-        }
-    }
-  return 0;
-}
index 2b803b06d099394684d3ce4527dcae77db91b669..2daca9378a4a9211af931f2a28ce40330ae99dac 100644 (file)
@@ -575,7 +575,10 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
       {
         mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
         r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
-        for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8;
+        for ( i = 0; i <= 143; ++i) *p++ = 8;
+        for ( ; i <= 255; ++i) *p++ = 9;
+        for ( ; i <= 279; ++i) *p++ = 7;
+        for ( ; i <= 287; ++i) *p++ = 8;
       }
       else
       {
@@ -1393,7 +1396,10 @@ static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahe
         if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break;
       TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
     }
-    if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break;
+    if (!dist) break;
+    p = s; q = d->m_dict + probe_pos;
+    for (probe_len = 0; probe_len < max_match_len; probe_len++)
+        if (*p++ != *q++) break;
     if (probe_len > match_len)
     {
       *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return;
index d1b6fe6655ac6619851b9e1b2fad8bc5f5f9c597..150abc226e6859a323b117a36df4d8d6e2f6b368 100644 (file)
@@ -27,8 +27,7 @@
 #![allow(non_camel_case_types)]
 
 #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
-pub mod eh_frames
-{
+pub mod eh_frames {
     #[no_mangle]
     #[link_section = ".eh_frame"]
     // Marks beginning of the stack frame unwind info section
@@ -40,7 +39,7 @@ pub mod eh_frames
 
     // Unwind info registration/deregistration routines.
     // See the docs of `unwind` module in libstd.
-    extern {
+    extern "C" {
         fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8);
         fn rust_eh_unregister_frames(eh_frame_begin: *const u8, object: *mut u8);
     }
@@ -58,8 +57,7 @@ pub mod eh_frames
     }
 
     // MSVC-specific init/uninit routine registration
-    pub mod ms_init
-    {
+    pub mod ms_init {
         // .CRT$X?? sections are roughly analogous to ELF's .init_array and .fini_array,
         // except that they exploit the fact that linker will sort them alphabitically,
         // so e.g. sections with names between .CRT$XIA and .CRT$XIZ are guaranteed to be
index 5e4e13ebd05e4ea13c994a50f4e8275a2aee5843..915c2355b04849608e939ebbccd59198ec434c1a 100644 (file)
@@ -14,8 +14,7 @@
 #![no_std]
 
 #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
-pub mod eh_frames
-{
+pub mod eh_frames {
     // Terminate the frame unwind info section with a 0 as a sentinel;
     // this would be the 'length' field in a real FDE.
     #[no_mangle]
diff --git a/src/rust-installer/test/rust-installer-v1/README.md b/src/rust-installer/test/rust-installer-v1/README.md
new file mode 100644 (file)
index 0000000..f541504
--- /dev/null
@@ -0,0 +1,30 @@
+A generator for the install.sh script commonly used to install Rust in
+Unix environments. It is used By Rust, Cargo, and is intended to be
+used by a future combined installer of Rust + Cargo.
+
+# Usage
+
+```
+./gen-installer.sh --product-name=Rust \
+                   --verify-bin=rustc \
+                   --rel-manifest-dir=rustlib \
+                   --success-message=Rust-is-ready-to-roll. \
+                   --image-dir=./install-image \
+                   --work-dir=./temp \
+                   --output-dir=./dist \
+                   --non-installed-prefixes="foo,bin/bar,lib/baz" \
+                   --package-name=rustc-nightly-i686-apple-darwin
+```
+
+Or, to just generate the script.
+
+```
+./gen-install-script.sh --product-name=Rust \
+                        --verify-bin=rustc \
+                        --rel-manifest-dir=rustlib \
+                        --success-message=Rust-is-ready-to-roll. \
+                        --output-script=install.sh
+```
+
+*Note: the dashes in `success-message` are converted to spaces. The
+script's argument handling is broken with spaces.*
\ No newline at end of file
diff --git a/src/rust-installer/test/rust-installer-v1/gen-install-script.sh b/src/rust-installer/test/rust-installer-v1/gen-install-script.sh
new file mode 100755 (executable)
index 0000000..4c7e327
--- /dev/null
@@ -0,0 +1,253 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "gen-install-script: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "gen-install-script: WARNING: $1"
+}
+
+err() {
+    echo "gen-install-script: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "gen-install-script: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "gen-install-script: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+msg "looking for install programs"
+msg
+
+need_cmd sed
+need_cmd chmod
+need_cmd cat
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+valopt product-name "Product" "The name of the product, for display"
+valopt verify-bin "program" "The command to run with --version to verify the install works"
+valopt rel-manifest-dir "${CFG_VERIFY_BIN}lib" "The directory under lib/ where the manifest lives"
+valopt success-message "Installed." "The string to print after successful installation"
+valopt output-script "${CFG_SRC_DIR}/install.sh" "The name of the output script"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+# Replace dashes in the success message with spaces (our arg handling botches spaces)
+CFG_SUCCESS_MESSAGE=`echo "$CFG_SUCCESS_MESSAGE" | sed "s/-/ /g"`
+
+SCRIPT_TEMPLATE=`cat "${CFG_SRC_DIR}/install-template.sh"`
+
+# Using /bin/echo because under sh emulation dash *seems* to escape \n, which screws up the template
+SCRIPT=`/bin/echo "${SCRIPT_TEMPLATE}"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_PRODUCT_NAME%%/${CFG_PRODUCT_NAME}/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_VERIFY_BIN%%/${CFG_VERIFY_BIN}/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_REL_MANIFEST_DIR%%/${CFG_REL_MANIFEST_DIR}/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_SUCCESS_MESSAGE%%/\"${CFG_SUCCESS_MESSAGE}\"/"`
+
+/bin/echo "${SCRIPT}" > "${CFG_OUTPUT_SCRIPT}"
+need_ok "couldn't write script"
+chmod u+x "${CFG_OUTPUT_SCRIPT}"
+need_ok "couldn't chmod script"
diff --git a/src/rust-installer/test/rust-installer-v1/gen-installer.sh b/src/rust-installer/test/rust-installer-v1/gen-installer.sh
new file mode 100755 (executable)
index 0000000..c88eebe
--- /dev/null
@@ -0,0 +1,303 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "gen-installer: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "gen-installer: WARNING: $1"
+}
+
+err() {
+    echo "gen-installer: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "gen-installer: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "gen-installer: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+msg "looking for programs"
+msg
+
+need_cmd tar
+need_cmd cp
+need_cmd rm
+need_cmd mkdir
+need_cmd echo
+need_cmd tr
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+valopt product-name "Product" "The name of the product, for display"
+valopt package-name "package" "The name of the package, tarball"
+valopt verify-bin "program" "The command to run with --version to verify the install works"
+valopt rel-manifest-dir "${CFG_VERIFY_BIN}lib" "The directory under lib/ where the manifest lives"
+valopt success-message "Installed." "The string to print after successful installation"
+valopt non-installed-prefixes "" "Path prefixes that should be included but not installed"
+valopt image-dir "./install-image" "The directory containing the installation medium"
+valopt work-dir "./workdir" "The directory to do temporary work"
+valopt output-dir "./dist" "The location to put the final image and tarball"
+opt tarball 1 "Disable tarball generation, leaving output in the temp dir"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+GEN_INSTALL_SCRIPT="$CFG_SRC_DIR/gen-install-script.sh"
+
+if [ ! -d "$CFG_IMAGE_DIR" ]
+then
+    err "image dir $CFG_IMAGE_DIR does not exist"
+fi
+
+mkdir -p "$CFG_WORK_DIR"
+need_ok "couldn't create work dir"
+
+rm -Rf "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't delete work package dir"
+
+mkdir -p "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't create work package dir"
+
+cp -r "$CFG_IMAGE_DIR/"* "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't copy source image"
+
+# Split non_installed_files into lines for later iteration
+NON_INSTALLED_PREFIXES=`echo "$CFG_NON_INSTALLED_PREFIXES" | tr "," "\n"`
+
+# Create the manifest
+MANIFEST=`(cd "$CFG_WORK_DIR/$CFG_PACKAGE_NAME" && find . -type f | sed 's/^\.\///') | sort`
+
+# Remove non-installed files from manifest
+NON_INSTALLED_PREFIXES=`echo "$CFG_NON_INSTALLED_PREFIXES" | tr "," " "`
+for prefix in $NON_INSTALLED_PREFIXES; do
+    MANIFEST=`echo "$MANIFEST" | sed /^$prefix/d`
+done
+
+MANIFEST_NAME="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/lib/$CFG_REL_MANIFEST_DIR/manifest.in"
+
+mkdir -p "$CFG_WORK_DIR/$CFG_PACKAGE_NAME/lib/$CFG_REL_MANIFEST_DIR"
+need_ok "couldn't create manifest dir"
+
+# Write the manifest
+echo "$MANIFEST" > "$MANIFEST_NAME"
+
+# Generate the install script
+"$CFG_SRC_DIR/gen-install-script.sh" \
+    --product-name="$CFG_PRODUCT_NAME" \
+    --verify-bin="$CFG_VERIFY_BIN" \
+    --rel-manifest-dir="$CFG_REL_MANIFEST_DIR" \
+    --success-message="$CFG_SUCCESS_MESSAGE" \
+    --output-script="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/install.sh"
+
+need_ok "failed to generate install script"    
+
+mkdir -p "$CFG_OUTPUT_DIR"
+need_ok "couldn't create output dir"
+
+rm -Rf "$CFG_OUTPUT_DIR/$CFG_PACKAGE_NAME.tar.gz"
+need_ok "couldn't delete old tarball"
+
+# Make a tarball
+tar -czf "$CFG_OUTPUT_DIR/$CFG_PACKAGE_NAME.tar.gz" -C "$CFG_WORK_DIR" "$CFG_PACKAGE_NAME"
+need_ok "failed to tar"
diff --git a/src/rust-installer/test/rust-installer-v1/install-template.sh b/src/rust-installer/test/rust-installer-v1/install-template.sh
new file mode 100644 (file)
index 0000000..545de67
--- /dev/null
@@ -0,0 +1,554 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "install: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "install: WARNING: $1"
+}
+
+err() {
+    echo "install: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "install: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "install: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+absolutify() {
+    FILE_PATH="${1}"
+    FILE_PATH_DIRNAME="$(dirname ${FILE_PATH})"
+    FILE_PATH_BASENAME="$(basename ${FILE_PATH})"
+    FILE_ABS_PATH="$(cd ${FILE_PATH_DIRNAME} && pwd)"
+    FILE_PATH="${FILE_ABS_PATH}/${FILE_PATH_BASENAME}"
+    # This is the return value
+    ABSOLUTIFIED="${FILE_PATH}"
+}
+
+msg "looking for install programs"
+msg
+
+need_cmd mkdir
+need_cmd printf
+need_cmd cut
+need_cmd grep
+need_cmd uname
+need_cmd tr
+need_cmd sed
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+# Check for mingw or cygwin in order to special case $CFG_LIBDIR_RELATIVE.
+# This logic is duplicated from configure in order to get the correct libdir
+# for Windows installs.
+CFG_OSTYPE=$(uname -s)
+
+case $CFG_OSTYPE in
+
+    Linux)
+        CFG_OSTYPE=unknown-linux-gnu
+        ;;
+
+    FreeBSD)
+        CFG_OSTYPE=unknown-freebsd
+        ;;
+
+    DragonFly)
+        CFG_OSTYPE=unknown-dragonfly
+        ;;
+
+    Darwin)
+        CFG_OSTYPE=apple-darwin
+        ;;
+
+    MINGW*)
+        # msys' `uname` does not print gcc configuration, but prints msys
+        # configuration. so we cannot believe `uname -m`:
+        # msys1 is always i686 and msys2 is always x86_64.
+        # instead, msys defines $MSYSTEM which is MINGW32 on i686 and
+        # MINGW64 on x86_64.
+        CFG_CPUTYPE=i686
+        CFG_OSTYPE=pc-windows-gnu
+        if [ "$MSYSTEM" = MINGW64 ]
+        then
+            CFG_CPUTYPE=x86_64
+        fi
+        ;;
+
+    MSYS*)
+        CFG_OSTYPE=pc-windows-gnu
+        ;;
+
+# Thad's Cygwin identifers below
+
+#   Vista 32 bit
+    CYGWIN_NT-6.0)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=i686
+        ;;
+
+#   Vista 64 bit
+    CYGWIN_NT-6.0-WOW64)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=x86_64
+        ;;
+
+#   Win 7 32 bit
+    CYGWIN_NT-6.1)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=i686
+        ;;
+
+#   Win 7 64 bit
+    CYGWIN_NT-6.1-WOW64)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=x86_64
+        ;;
+esac
+
+OPTIONS=""
+BOOL_OPTIONS=""
+VAL_OPTIONS=""
+
+if [ "$CFG_OSTYPE" = "pc-windows-gnu" ]
+then
+    CFG_LD_PATH_VAR=PATH
+    CFG_OLD_LD_PATH_VAR=$PATH
+elif [ "$CFG_OSTYPE" = "apple-darwin" ]
+then
+    CFG_LD_PATH_VAR=DYLD_LIBRARY_PATH
+    CFG_OLD_LD_PATH_VAR=$DYLD_LIBRARY_PATH
+else
+    CFG_LD_PATH_VAR=LD_LIBRARY_PATH
+    CFG_OLD_LD_PATH_VAR=$LD_LIBRARY_PATH
+fi
+
+flag uninstall "only uninstall from the installation prefix"
+valopt destdir "" "set installation root"
+opt verify 1 "verify that the installed binaries run correctly"
+valopt prefix "/usr/local" "set installation prefix"
+# NB This isn't quite the same definition as in `configure`.
+# just using 'lib' instead of configure's CFG_LIBDIR_RELATIVE
+valopt libdir "${CFG_DESTDIR}${CFG_PREFIX}/lib" "install libraries"
+valopt mandir "${CFG_DESTDIR}${CFG_PREFIX}/share/man" "install man pages in PATH"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+
+
+# Template configuration.
+# These names surrounded by '%%` are replaced by sed when generating install.sh
+
+# Rust or Cargo
+TEMPLATE_PRODUCT_NAME=%%TEMPLATE_PRODUCT_NAME%%
+# rustc or cargo
+TEMPLATE_VERIFY_BIN=%%TEMPLATE_VERIFY_BIN%%
+# rustlib or cargo
+TEMPLATE_REL_MANIFEST_DIR=%%TEMPLATE_REL_MANIFEST_DIR%%
+# 'Rust is ready to roll.' or 'Cargo is cool to cruise.'
+TEMPLATE_SUCCESS_MESSAGE=%%TEMPLATE_SUCCESS_MESSAGE%%
+
+# OK, let's get installing ...
+
+# Sanity check: can we run the binaries?
+if [ -z "${CFG_DISABLE_VERIFY}" ]
+then
+    # Don't do this if uninstalling. Failure here won't help in any way.
+    if [ -z "${CFG_UNINSTALL}" ]
+    then
+        msg "verifying platform can run binaries"
+        export $CFG_LD_PATH_VAR="${CFG_SRC_DIR}/lib:$CFG_OLD_LD_PATH_VAR"
+        "${CFG_SRC_DIR}/bin/${TEMPLATE_VERIFY_BIN}" --version 2> /dev/null 1> /dev/null
+        if [ $? -ne 0 ]
+        then
+            err "can't execute rustc binary on this platform"
+        fi
+        export $CFG_LD_PATH_VAR="$CFG_OLD_LD_PATH_VAR"
+    fi
+fi
+
+# Sanity check: can we can write to the destination?
+msg "verifying destination is writable"
+umask 022 && mkdir -p "${CFG_LIBDIR}"
+need_ok "can't write to destination. consider \`sudo\`."
+touch "${CFG_LIBDIR}/rust-install-probe" > /dev/null
+if [ $? -ne 0 ]
+then
+    err "can't write to destination. consider \`sudo\`."
+fi
+rm -f "${CFG_LIBDIR}/rust-install-probe"
+need_ok "failed to remove install probe"
+
+# Sanity check: don't install to the directory containing the installer.
+# That would surely cause chaos.
+msg "verifying destination is not the same as source"
+INSTALLER_DIR="$(cd $(dirname $0) && pwd)"
+PREFIX_DIR="$(cd ${CFG_PREFIX} && pwd)"
+if [ "${INSTALLER_DIR}" = "${PREFIX_DIR}" ]
+then
+    err "can't install to same directory as installer"
+fi
+
+# Using an absolute path to libdir in a few places so that the status
+# messages are consistently using absolute paths.
+absolutify "${CFG_LIBDIR}"
+ABS_LIBDIR="${ABSOLUTIFIED}"
+
+# The file name of the manifest we're going to create during install
+INSTALLED_MANIFEST="${ABS_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}/manifest"
+
+# First, uninstall from the installation prefix.
+# Errors are warnings - try to rm everything in the manifest even if some fail.
+if [ -f "${INSTALLED_MANIFEST}" ]
+then
+    msg
+
+    # Iterate through installed manifest and remove files
+    while read p; do
+        # The installed manifest contains absolute paths
+        msg "removing $p"
+        if [ -f "$p" ]
+        then
+            rm -f "$p"
+            if [ $? -ne 0 ]
+            then
+                warn "failed to remove $p"
+            fi
+        else
+            warn "supposedly installed file $p does not exist!"
+        fi
+    done < "${INSTALLED_MANIFEST}"
+
+    # If we fail to remove $TEMPLATE_REL_MANIFEST_DIR below, then the
+    # installed manifest will still be full; the installed manifest
+    # needs to be empty before install.
+    msg "removing ${INSTALLED_MANIFEST}"
+    rm -f "${INSTALLED_MANIFEST}"
+    # For the above reason, this is a hard error
+    need_ok "failed to remove installed manifest"
+
+    # Remove $TEMPLATE_REL_MANIFEST_DIR directory
+    msg "removing ${ABS_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+    rm -Rf "${ABS_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+    if [ $? -ne 0 ]
+    then
+        warn "failed to remove ${TEMPLATE_REL_MANIFEST_DIR}"
+    fi
+else
+    # There's no manifest. If we were asked to uninstall, then that's a problem.
+    if [ -n "${CFG_UNINSTALL}" ]
+    then
+        err "unable to find installation manifest at ${CFG_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+    fi
+fi
+
+# If we're only uninstalling then exit
+if [ -n "${CFG_UNINSTALL}" ]
+then
+    echo
+    echo "    ${TEMPLATE_PRODUCT_NAME} is uninstalled."
+    echo
+    exit 0
+fi
+
+# Create the installed manifest, which we will fill in with absolute file paths
+mkdir -p "${CFG_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+need_ok "failed to create ${TEMPLATE_REL_MANIFEST_DIR}"
+touch "${INSTALLED_MANIFEST}"
+need_ok "failed to create installed manifest"
+
+msg
+
+# Now install, iterate through the new manifest and copy files
+while read p; do
+
+    # Decide the destination of the file
+    FILE_INSTALL_PATH="${CFG_DESTDIR}${CFG_PREFIX}/$p"
+
+    if echo "$p" | grep "^lib/" > /dev/null
+    then
+        pp=`echo $p | sed 's/^lib\///'`
+        FILE_INSTALL_PATH="${CFG_LIBDIR}/$pp"
+    fi
+
+    if echo "$p" | grep "^share/man/" > /dev/null
+    then
+        pp=`echo $p | sed 's/^share\/man\///'`
+        FILE_INSTALL_PATH="${CFG_MANDIR}/$pp"
+    fi
+
+    # Make sure there's a directory for it
+    umask 022 && mkdir -p "$(dirname ${FILE_INSTALL_PATH})"
+    need_ok "directory creation failed"
+
+    # Make the path absolute so we can uninstall it later without
+    # starting from the installation cwd
+    absolutify "${FILE_INSTALL_PATH}"
+    FILE_INSTALL_PATH="${ABSOLUTIFIED}"
+
+    # Install the file
+    msg "installing ${FILE_INSTALL_PATH}"
+    if echo "$p" | grep "^bin/" > /dev/null
+    then
+        install -m755 "${CFG_SRC_DIR}/$p" "${FILE_INSTALL_PATH}"
+    else
+        install -m644 "${CFG_SRC_DIR}/$p" "${FILE_INSTALL_PATH}"
+    fi
+    need_ok "file creation failed"
+
+    # Update the manifest
+    echo "${FILE_INSTALL_PATH}" >> "${INSTALLED_MANIFEST}"
+    need_ok "failed to update manifest"
+
+# The manifest lists all files to install
+done < "${CFG_SRC_DIR}/lib/${TEMPLATE_REL_MANIFEST_DIR}/manifest.in"
+
+msg
+
+# Run ldconfig to make dynamic libraries available to the linker
+if [ "$CFG_OSTYPE" = "unknown-linux-gnu" ]
+    then
+    ldconfig
+    if [ $? -ne 0 ]
+    then
+        warn "failed to run ldconfig."
+        warn "this may happen when not installing as root and may be fine"
+    fi
+fi
+
+# Sanity check: can we run the installed binaries?
+#
+# As with the verification above, make sure the right LD_LIBRARY_PATH-equivalent
+# is in place. Try first without this variable, and if that fails try again with
+# the variable. If the second time tries, print a hopefully helpful message to
+# add something to the appropriate environment variable.
+if [ -z "${CFG_DISABLE_VERIFY}" ]
+then
+    msg "verifying installed binaries are executable"
+    "${CFG_PREFIX}/bin/${TEMPLATE_VERIFY_BIN}" --version 2> /dev/null 1> /dev/null
+    if [ $? -ne 0 ]
+    then
+        export $CFG_LD_PATH_VAR="${CFG_PREFIX}/lib:$CFG_OLD_LD_PATH_VAR"
+        "${CFG_PREFIX}/bin/${TEMPLATE_VERIFY_BIN}" --version > /dev/null
+        if [ $? -ne 0 ]
+        then
+            ERR="can't execute installed binaries. "
+            ERR="${ERR}installation may be broken. "
+            ERR="${ERR}if this is expected then rerun install.sh with \`--disable-verify\` "
+            ERR="${ERR}or \`make install\` with \`--disable-verify-install\`"
+            err "${ERR}"
+        else
+            echo
+            echo "    Note: please ensure '${CFG_PREFIX}/lib' is added to ${CFG_LD_PATH_VAR}"
+        fi
+    fi
+fi
+
+echo
+echo "    ${TEMPLATE_SUCCESS_MESSAGE}"
+echo
+
+
diff --git a/src/rust-installer/test/rust-installer-v2/.gitmodules b/src/rust-installer/test/rust-installer-v2/.gitmodules
new file mode 100644 (file)
index 0000000..7485a91
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "test/rust-installer-v1"]
+       path = test/rust-installer-v1
+       url = https://github.com/rust-lang/rust-installer
diff --git a/src/rust-installer/test/rust-installer-v2/.travis.yml b/src/rust-installer/test/rust-installer-v2/.travis.yml
new file mode 100644 (file)
index 0000000..7e34188
--- /dev/null
@@ -0,0 +1,2 @@
+script:
+  - ./test.sh
diff --git a/src/rust-installer/test/rust-installer-v2/README.md b/src/rust-installer/test/rust-installer-v2/README.md
new file mode 100644 (file)
index 0000000..e2588f2
--- /dev/null
@@ -0,0 +1,74 @@
+[![Build Status](https://travis-ci.org/rust-lang/rust-installer.svg?branch=master)](https://travis-ci.org/rust-lang/rust-installer)
+
+A generator for the install.sh script commonly used to install Rust in
+Unix environments. It is used By Rust, Cargo, and is intended to be
+used by a future combined installer of Rust + Cargo.
+
+# Usage
+
+```
+./gen-installer.sh --product-name=Rust \
+                   --verify-bin=rustc \
+                   --rel-manifest-dir=rustlib \
+                   --success-message=Rust-is-ready-to-roll. \
+                   --image-dir=./install-image \
+                   --work-dir=./temp \
+                   --output-dir=./dist \
+                   --non-installed-prefixes=foo,bin/bar,lib/baz \
+                   --package-name=rustc-nightly-i686-apple-darwin \
+                   --component-name=rustc \
+                   --legacy-manifest-dirs=rustlib \
+                   --bulk-dirs=share/doc
+```
+
+Or, to just generate the script.
+
+```
+./gen-install-script.sh --product-name=Rust \
+                        --verify-bin=rustc \
+                        --rel-manifest-dir=rustlib \
+                        --success-message=Rust-is-ready-to-roll. \
+                        --output-script=install.sh \
+                        --legacy-manifest-dirs=rustlib
+```
+
+*Note: the dashes in `success-message` are converted to spaces. The
+script's argument handling is broken with spaces.*
+
+To combine installers.
+
+```
+./combine-installers.sh --product-name=Rust \
+                        --verify-bin=rustc \
+                        --rel-manifest-dir=rustlib \
+                        --success-message=Rust-is-ready-to-roll. \
+                        --work-dir=./temp \
+                        --output-dir=./dist \
+                        --non-installed-overlay=./overlay \
+                        --package-name=rustc-nightly-i686-apple-darwin \
+                        --legacy-manifest-dirs=rustlib \
+                        --input-tarballs=./rustc.tar.gz,cargo.tar.gz
+```
+
+# Future work
+
+* Make install.sh not have to be customized, pull it's data from a
+  config file.
+* Install an uninstall.sh script.
+* Allow components to be selected during install.
+* Allow components to be modified from an existing install.
+* Be more resiliant to installation failures, particularly if the disk
+  is full.
+* Pre-install and post-uninstall scripts.
+* Make the verify-bin a per-component option.
+* Be more thoughtful about overwriting existing files.
+* Allow components to depend on or contradict other components.
+* Sanity check that expected destination dirs (bin, lib, share exist)?
+* Add --docdir flag. Is there a standard name for this?
+* Remove empty directories on uninstall.
+* Detect mismatches in --prefix, --mandir, etc. in follow-on
+  installs/uninstalls.
+* Fix argument handling for spaces.
+* Add --bindir.
+* Store components in their own directories for cases where they
+  contain the same files.
\ No newline at end of file
diff --git a/src/rust-installer/test/rust-installer-v2/combine-installers.sh b/src/rust-installer/test/rust-installer-v2/combine-installers.sh
new file mode 100644 (file)
index 0000000..d135d2b
--- /dev/null
@@ -0,0 +1,347 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "combine-installers: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "combine-installers: WARNING: $1"
+}
+
+err() {
+    echo "combine-installers: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "combine-installers: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "combine-installers: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+msg "looking for programs"
+msg
+
+need_cmd tar
+need_cmd cp
+need_cmd rm
+need_cmd mkdir
+need_cmd echo
+need_cmd tr
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+valopt product-name "Product" "The name of the product, for display"
+valopt package-name "package" "The name of the package, tarball"
+valopt verify-bin "" "The command to run with --version to verify the install works"
+valopt rel-manifest-dir "${CFG_PACKAGE_NAME}lib" "The directory under lib/ where the manifest lives"
+valopt success-message "Installed." "The string to print after successful installation"
+valopt legacy-manifest-dirs "" "Places to look for legacy manifests to uninstall"
+valopt input-tarballs "" "Installers to combine"
+valopt non-installed-overlay "" "Directory containing files that should not be installed"
+valopt work-dir "./workdir" "The directory to do temporary work and put the final image"
+valopt output-dir "./dist" "The location to put the final tarball"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+RUST_INSTALLER_VERSION=`cat "$CFG_SRC_DIR/rust-installer-version"`
+
+# Create the work directory for the new installer
+mkdir -p "$CFG_WORK_DIR"
+need_ok "couldn't create work dir"
+
+rm -Rf "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't delete work package dir"
+
+mkdir -p "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't create work package dir"
+
+INPUT_TARBALLS=`echo "$CFG_INPUT_TARBALLS" | sed 's/,/ /g'`
+
+# Merge each installer into the work directory of the new installer
+for input_tarball in $INPUT_TARBALLS; do
+
+    # Extract the input tarballs
+    tar xzf $input_tarball -C "$CFG_WORK_DIR"
+    need_ok "failed to extract tarball"
+
+    # Verify the version number
+    PKG_NAME=`echo "$input_tarball" | sed s/\.tar\.gz//g`
+    PKG_NAME=`basename $PKG_NAME`
+    VERSION=`cat "$CFG_WORK_DIR/$PKG_NAME/rust-installer-version"`
+    if [ "$RUST_INSTALLER_VERSION" != "$VERSION" ]; then
+       err "incorrect installer version in $input_tarball"
+    fi
+
+    # Interpret the manifest to copy the contents to the new installer
+    COMPONENTS=`cat "$CFG_WORK_DIR/$PKG_NAME/components"`
+    for component in $COMPONENTS; do
+       while read directive; do
+           COMMAND=`echo $directive | cut -f1 -d:`
+           FILE=`echo $directive | cut -f2 -d:`
+
+           NEW_FILE_PATH="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/$FILE"
+           mkdir -p "$(dirname $NEW_FILE_PATH)"
+
+           case "$COMMAND" in
+               file | dir)
+                   if [ -e "$NEW_FILE_PATH" ]; then
+                       err "file $NEW_FILE_PATH already exists"
+                   fi
+                   cp -R "$CFG_WORK_DIR/$PKG_NAME/$FILE" "$NEW_FILE_PATH"
+                   need_ok "failed to copy file $FILE"
+                   ;;
+
+               * )
+                   err "unknown command"
+                   ;;
+
+           esac
+       done < "$CFG_WORK_DIR/$PKG_NAME/manifest-$component.in"
+
+       # Copy the manifest
+       if [ -e "$CFG_WORK_DIR/$CFG_PACKAGE_NAME/manifest-$component.in" ]; then
+           err "manifest for $component already exists"
+       fi
+       cp "$CFG_WORK_DIR/$PKG_NAME/manifest-$component.in" "$CFG_WORK_DIR/$CFG_PACKAGE_NAME/manifest-$component.in"
+       need_ok "failed to copy manifest for $component"
+
+       # Merge the component name
+       echo "$component" >> "$CFG_WORK_DIR/$CFG_PACKAGE_NAME/components"
+       need_ok "failed to merge component $component"
+    done
+done
+
+# Write the version number
+echo "$RUST_INSTALLER_VERSION" > "$CFG_WORK_DIR/$CFG_PACKAGE_NAME/rust-installer-version"
+
+# Copy the overlay
+if [ -n "$CFG_NON_INSTALLED_OVERLAY" ]; then
+    OVERLAY_FILES=`(cd "$CFG_NON_INSTALLED_OVERLAY" && find . -type f)`
+    for f in $OVERLAY_FILES; do
+       if [ -e "$CFG_WORK_DIR/$CFG_PACKAGE_NAME/$f" ]; then err "overlay $f exists"; fi
+
+       cp "$CFG_NON_INSTALLED_OVERLAY/$f" "$CFG_WORK_DIR/$CFG_PACKAGE_NAME/$f"
+       need_ok "failed to copy overlay $f"
+    done
+fi
+
+# Generate the install script
+"$CFG_SRC_DIR/gen-install-script.sh" \
+    --product-name="$CFG_PRODUCT_NAME" \
+    --verify-bin="$CFG_VERIFY_BIN" \
+    --rel-manifest-dir="$CFG_REL_MANIFEST_DIR" \
+    --success-message="$CFG_SUCCESS_MESSAGE" \
+    --legacy-manifest-dirs="$CFG_LEGACY_MANIFEST_DIRS" \
+    --output-script="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/install.sh"
+
+need_ok "failed to generate install script"
+
+mkdir -p "$CFG_OUTPUT_DIR"
+need_ok "couldn't create output dir"
+
+rm -Rf "$CFG_OUTPUT_DIR/$CFG_PACKAGE_NAME.tar.gz"
+need_ok "couldn't delete old tarball"
+
+# Make a tarball
+tar -czf "$CFG_OUTPUT_DIR/$CFG_PACKAGE_NAME.tar.gz" -C "$CFG_WORK_DIR" "$CFG_PACKAGE_NAME"
+need_ok "failed to tar"
diff --git a/src/rust-installer/test/rust-installer-v2/gen-install-script.sh b/src/rust-installer/test/rust-installer-v2/gen-install-script.sh
new file mode 100755 (executable)
index 0000000..e00185e
--- /dev/null
@@ -0,0 +1,261 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "gen-install-script: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "gen-install-script: WARNING: $1"
+}
+
+err() {
+    echo "gen-install-script: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "gen-install-script: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "gen-install-script: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+msg "looking for install programs"
+msg
+
+need_cmd sed
+need_cmd chmod
+need_cmd cat
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+valopt product-name "Product" "The name of the product, for display"
+valopt verify-bin "" "The command to run with --version to verify the install works"
+valopt rel-manifest-dir "${CFG_VERIFY_BIN}lib" "The directory under lib/ where the manifest lives"
+valopt success-message "Installed." "The string to print after successful installation"
+valopt output-script "${CFG_SRC_DIR}/install.sh" "The name of the output script"
+valopt legacy-manifest-dirs "" "Places to look for legacy manifests to uninstall"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+RUST_INSTALLER_VERSION=`cat "$CFG_SRC_DIR/rust-installer-version"`
+
+# Replace dashes in the success message with spaces (our arg handling botches spaces)
+CFG_PRODUCT_NAME=`echo "$CFG_PRODUCT_NAME" | sed "s/-/ /g"`
+
+# Replace dashes in the success message with spaces (our arg handling botches spaces)
+CFG_SUCCESS_MESSAGE=`echo "$CFG_SUCCESS_MESSAGE" | sed "s/-/ /g"`
+
+SCRIPT_TEMPLATE=`cat "${CFG_SRC_DIR}/install-template.sh"`
+
+# Using /bin/echo because under sh emulation dash *seems* to escape \n, which screws up the template
+SCRIPT=`/bin/echo "${SCRIPT_TEMPLATE}"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_PRODUCT_NAME%%/\"${CFG_PRODUCT_NAME}\"/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_VERIFY_BIN%%/${CFG_VERIFY_BIN}/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_REL_MANIFEST_DIR%%/${CFG_REL_MANIFEST_DIR}/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_SUCCESS_MESSAGE%%/\"${CFG_SUCCESS_MESSAGE}\"/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_LEGACY_MANIFEST_DIRS%%/\"${CFG_LEGACY_MANIFEST_DIRS}\"/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_RUST_INSTALLER_VERSION%%/\"$RUST_INSTALLER_VERSION\"/"`
+
+/bin/echo "${SCRIPT}" > "${CFG_OUTPUT_SCRIPT}"
+need_ok "couldn't write script"
+chmod u+x "${CFG_OUTPUT_SCRIPT}"
+need_ok "couldn't chmod script"
diff --git a/src/rust-installer/test/rust-installer-v2/gen-installer.sh b/src/rust-installer/test/rust-installer-v2/gen-installer.sh
new file mode 100755 (executable)
index 0000000..67eaf39
--- /dev/null
@@ -0,0 +1,332 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "gen-installer: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "gen-installer: WARNING: $1"
+}
+
+err() {
+    echo "gen-installer: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "gen-installer: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "gen-installer: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+msg "looking for programs"
+msg
+
+need_cmd tar
+need_cmd cp
+need_cmd rm
+need_cmd mkdir
+need_cmd echo
+need_cmd tr
+need_cmd awk
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+valopt product-name "Product" "The name of the product, for display"
+valopt component-name "component" "The name of the component, distinct from other installed components"
+valopt package-name "package" "The name of the package, tarball"
+valopt verify-bin "" "The command to run with --version to verify the install works"
+valopt rel-manifest-dir "${CFG_PACKAGE_NAME}lib" "The directory under lib/ where the manifest lives"
+valopt success-message "Installed." "The string to print after successful installation"
+valopt legacy-manifest-dirs "" "Places to look for legacy manifests to uninstall"
+valopt non-installed-prefixes "" "Path prefixes that should be included but not installed"
+valopt bulk-dirs "" "Path prefixes of directories that should be installed/uninstalled in bulk"
+valopt image-dir "./install-image" "The directory containing the installation medium"
+valopt work-dir "./workdir" "The directory to do temporary work"
+valopt output-dir "./dist" "The location to put the final image and tarball"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+RUST_INSTALLER_VERSION=`cat "$CFG_SRC_DIR/rust-installer-version"`
+
+if [ ! -d "$CFG_IMAGE_DIR" ]
+then
+    err "image dir $CFG_IMAGE_DIR does not exist"
+fi
+
+mkdir -p "$CFG_WORK_DIR"
+need_ok "couldn't create work dir"
+
+rm -Rf "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't delete work package dir"
+
+mkdir -p "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't create work package dir"
+
+cp -r "$CFG_IMAGE_DIR/"* "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't copy source image"
+
+# Create the manifest
+MANIFEST=`(cd "$CFG_WORK_DIR/$CFG_PACKAGE_NAME" && find . -type f | sed 's/^\.\///') | sort`
+
+# Remove non-installed files from manifest
+NON_INSTALLED_PREFIXES=`echo "$CFG_NON_INSTALLED_PREFIXES" | tr "," " "`
+for prefix in $NON_INSTALLED_PREFIXES; do
+    # This adds the escapes to '/' in paths to make them '\/' so sed doesn't puke.
+    # I figured this out by adding backslashes until it worked. Holy shit.
+    prefix=`echo "$prefix" | sed s/\\\//\\\\\\\\\\\//g`
+    MANIFEST=`echo "$MANIFEST" | sed /^$prefix/d`
+done
+
+# Remove files in bulk dirs
+BULK_DIRS=`echo "$CFG_BULK_DIRS" | tr "," " "`
+for bulk_dir in $BULK_DIRS; do
+    bulk_dir=`echo "$bulk_dir" | sed s/\\\//\\\\\\\\\\\//g`
+    MANIFEST=`echo "$MANIFEST" | sed /^$bulk_dir/d`
+done
+
+# Add 'file:' installation directives.
+# The -n prevents adding a blank file: if the manifest is empty
+MANIFEST=`/bin/echo -n "$MANIFEST" | sed s/^/file:/`
+
+# Add 'dir:' directives
+for bulk_dir in $BULK_DIRS; do
+    MANIFEST=`echo "$MANIFEST" && echo "dir:$bulk_dir"`
+done
+
+# The above step may have left a leading empty line if there were only
+# bulk dirs. Remove it.
+MANIFEST=`echo "$MANIFEST" | sed /^$/d`
+
+MANIFEST_FILE="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/manifest-$CFG_COMPONENT_NAME.in"
+COMPONENT_FILE="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/components"
+VERSION_FILE="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/rust-installer-version"
+
+# Write the manifest
+echo "$MANIFEST" > "$MANIFEST_FILE"
+
+# Write the component name
+echo "$CFG_COMPONENT_NAME" > "$COMPONENT_FILE"
+
+# Write the installer version (only used by combine-installers.sh)
+echo "$RUST_INSTALLER_VERSION" > "$VERSION_FILE"
+
+# Generate the install script
+"$CFG_SRC_DIR/gen-install-script.sh" \
+    --product-name="$CFG_PRODUCT_NAME" \
+    --verify-bin="$CFG_VERIFY_BIN" \
+    --rel-manifest-dir="$CFG_REL_MANIFEST_DIR" \
+    --success-message="$CFG_SUCCESS_MESSAGE" \
+    --legacy-manifest-dirs="$CFG_LEGACY_MANIFEST_DIRS" \
+    --output-script="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/install.sh"
+
+need_ok "failed to generate install script"    
+
+mkdir -p "$CFG_OUTPUT_DIR"
+need_ok "couldn't create output dir"
+
+rm -Rf "$CFG_OUTPUT_DIR/$CFG_PACKAGE_NAME.tar.gz"
+need_ok "couldn't delete old tarball"
+
+# Make a tarball
+tar -czf "$CFG_OUTPUT_DIR/$CFG_PACKAGE_NAME.tar.gz" -C "$CFG_WORK_DIR" "$CFG_PACKAGE_NAME"
+need_ok "failed to tar"
diff --git a/src/rust-installer/test/rust-installer-v2/install-template.sh b/src/rust-installer/test/rust-installer-v2/install-template.sh
new file mode 100644 (file)
index 0000000..98d449a
--- /dev/null
@@ -0,0 +1,743 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "install: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "install: WARNING: $1"
+}
+
+err() {
+    echo "install: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "install: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "install: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+absolutify() {
+    FILE_PATH="${1}"
+    FILE_PATH_DIRNAME="$(dirname ${FILE_PATH})"
+    FILE_PATH_BASENAME="$(basename ${FILE_PATH})"
+    FILE_ABS_PATH="$(cd ${FILE_PATH_DIRNAME} && pwd)"
+    FILE_PATH="${FILE_ABS_PATH}/${FILE_PATH_BASENAME}"
+    # This is the return value
+    ABSOLUTIFIED="${FILE_PATH}"
+}
+
+msg "looking for install programs"
+msg
+
+need_cmd mkdir
+need_cmd printf
+need_cmd cut
+need_cmd grep
+need_cmd uname
+need_cmd tr
+need_cmd sed
+need_cmd chmod
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+# Check for mingw or cygwin in order to special case $CFG_LIBDIR_RELATIVE.
+# This logic is duplicated from configure in order to get the correct libdir
+# for Windows installs.
+CFG_OSTYPE=$(uname -s)
+
+case $CFG_OSTYPE in
+
+    Linux)
+        CFG_OSTYPE=unknown-linux-gnu
+        ;;
+
+    FreeBSD)
+        CFG_OSTYPE=unknown-freebsd
+        ;;
+
+    DragonFly)
+        CFG_OSTYPE=unknown-dragonfly
+        ;;
+
+    Darwin)
+        CFG_OSTYPE=apple-darwin
+        ;;
+
+    MINGW*)
+        # msys' `uname` does not print gcc configuration, but prints msys
+        # configuration. so we cannot believe `uname -m`:
+        # msys1 is always i686 and msys2 is always x86_64.
+        # instead, msys defines $MSYSTEM which is MINGW32 on i686 and
+        # MINGW64 on x86_64.
+        CFG_CPUTYPE=i686
+        CFG_OSTYPE=pc-windows-gnu
+        if [ "$MSYSTEM" = MINGW64 ]
+        then
+            CFG_CPUTYPE=x86_64
+        fi
+        ;;
+
+    MSYS*)
+        CFG_OSTYPE=pc-windows-gnu
+        ;;
+
+# Thad's Cygwin identifers below
+
+#   Vista 32 bit
+    CYGWIN_NT-6.0)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=i686
+        ;;
+
+#   Vista 64 bit
+    CYGWIN_NT-6.0-WOW64)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=x86_64
+        ;;
+
+#   Win 7 32 bit
+    CYGWIN_NT-6.1)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=i686
+        ;;
+
+#   Win 7 64 bit
+    CYGWIN_NT-6.1-WOW64)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=x86_64
+        ;;
+esac
+
+OPTIONS=""
+BOOL_OPTIONS=""
+VAL_OPTIONS=""
+
+if [ "$CFG_OSTYPE" = "pc-windows-gnu" ]
+then
+    CFG_LD_PATH_VAR=PATH
+    CFG_OLD_LD_PATH_VAR=$PATH
+elif [ "$CFG_OSTYPE" = "apple-darwin" ]
+then
+    CFG_LD_PATH_VAR=DYLD_LIBRARY_PATH
+    CFG_OLD_LD_PATH_VAR=$DYLD_LIBRARY_PATH
+else
+    CFG_LD_PATH_VAR=LD_LIBRARY_PATH
+    CFG_OLD_LD_PATH_VAR=$LD_LIBRARY_PATH
+fi
+
+flag uninstall "only uninstall from the installation prefix"
+valopt destdir "" "set installation root"
+opt verify 1 "verify that the installed binaries run correctly"
+valopt prefix "/usr/local" "set installation prefix"
+# NB This isn't quite the same definition as in `configure`.
+# just using 'lib' instead of configure's CFG_LIBDIR_RELATIVE
+valopt libdir "${CFG_DESTDIR}${CFG_PREFIX}/lib" "install libraries"
+valopt mandir "${CFG_DESTDIR}${CFG_PREFIX}/share/man" "install man pages in PATH"
+opt ldconfig 1 "run ldconfig after installation (Linux only)"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+# Template configuration.
+# These names surrounded by '%%` are replaced by sed when generating install.sh
+# FIXME: Might want to consider loading this from a file and not generating install.sh
+
+# Rust or Cargo
+TEMPLATE_PRODUCT_NAME=%%TEMPLATE_PRODUCT_NAME%%
+# rustc or cargo
+TEMPLATE_VERIFY_BIN=%%TEMPLATE_VERIFY_BIN%%
+# rustlib or cargo
+TEMPLATE_REL_MANIFEST_DIR=%%TEMPLATE_REL_MANIFEST_DIR%%
+# 'Rust is ready to roll.' or 'Cargo is cool to cruise.'
+TEMPLATE_SUCCESS_MESSAGE=%%TEMPLATE_SUCCESS_MESSAGE%%
+# Locations to look for directories containing legacy, pre-versioned manifests
+TEMPLATE_LEGACY_MANIFEST_DIRS=%%TEMPLATE_LEGACY_MANIFEST_DIRS%%
+# The installer version
+TEMPLATE_RUST_INSTALLER_VERSION=%%TEMPLATE_RUST_INSTALLER_VERSION%%
+
+# OK, let's get installing ...
+
+# If we don't have a verify bin then disable verify
+if [ -z "$TEMPLATE_VERIFY_BIN" ]; then
+    CFG_DISABLE_VERIFY=1
+fi
+
+# Sanity check: can we run the binaries?
+if [ -z "${CFG_DISABLE_VERIFY}" ]
+then
+    # Don't do this if uninstalling. Failure here won't help in any way.
+    if [ -z "${CFG_UNINSTALL}" ]
+    then
+        msg "verifying platform can run binaries"
+        export $CFG_LD_PATH_VAR="${CFG_SRC_DIR}/lib:$CFG_OLD_LD_PATH_VAR"
+        "${CFG_SRC_DIR}/bin/${TEMPLATE_VERIFY_BIN}" --version 2> /dev/null 1> /dev/null
+        if [ $? -ne 0 ]
+        then
+            err "can't execute binaries on this platform"
+        fi
+        export $CFG_LD_PATH_VAR="$CFG_OLD_LD_PATH_VAR"
+    fi
+fi
+
+# Sanity check: can we can write to the destination?
+msg "verifying destination is writable"
+umask 022 && mkdir -p "${CFG_LIBDIR}"
+need_ok "can't write to destination. consider \`sudo\`."
+touch "${CFG_LIBDIR}/rust-install-probe" > /dev/null
+if [ $? -ne 0 ]
+then
+    err "can't write to destination. consider \`sudo\`."
+fi
+rm -f "${CFG_LIBDIR}/rust-install-probe"
+need_ok "failed to remove install probe"
+
+# Sanity check: don't install to the directory containing the installer.
+# That would surely cause chaos.
+msg "verifying destination is not the same as source"
+INSTALLER_DIR="$(cd $(dirname $0) && pwd)"
+PREFIX_DIR="$(cd ${CFG_PREFIX} && pwd)"
+if [ "${INSTALLER_DIR}" = "${PREFIX_DIR}" ]
+then
+    err "can't install to same directory as installer"
+fi
+
+# Open the components file to get the list of components to install
+COMPONENTS=`cat "$CFG_SRC_DIR/components"`
+
+# Sanity check: do we have components?
+if [ ! -n "$COMPONENTS" ]; then
+    err "unable to find installation components"
+fi
+
+# Using an absolute path to libdir in a few places so that the status
+# messages are consistently using absolute paths.
+absolutify "${CFG_LIBDIR}"
+ABS_LIBDIR="${ABSOLUTIFIED}"
+
+# Replace commas in legacy manifest list with spaces
+LEGACY_MANIFEST_DIRS=`echo "$TEMPLATE_LEGACY_MANIFEST_DIRS" | sed "s/,/ /g"`
+
+# Uninstall from legacy manifests
+for md in $LEGACY_MANIFEST_DIRS; do
+    # First, uninstall from the installation prefix.
+    # Errors are warnings - try to rm everything in the manifest even if some fail.
+    if [ -f "$ABS_LIBDIR/$md/manifest" ]
+    then
+
+       # Iterate through installed manifest and remove files
+       while read p; do
+            # The installed manifest contains absolute paths
+            msg "removing legacy file $p"
+            if [ -f "$p" ]
+            then
+               rm -f "$p"
+               if [ $? -ne 0 ]
+               then
+                    warn "failed to remove $p"
+               fi
+            else
+               warn "supposedly installed file $p does not exist!"
+            fi
+       done < "$ABS_LIBDIR/$md/manifest"
+
+       # If we fail to remove $md below, then the
+       # installed manifest will still be full; the installed manifest
+       # needs to be empty before install.
+       msg "removing legacy manifest $ABS_LIBDIR/$md/manifest"
+       rm -f "$ABS_LIBDIR/$md/manifest"
+       # For the above reason, this is a hard error
+       need_ok "failed to remove installed manifest"
+
+       # Remove $TEMPLATE_REL_MANIFEST_DIR directory
+       msg "removing legacy manifest dir ${ABS_LIBDIR}/$md"
+       rm -Rf "${ABS_LIBDIR}/$md"
+       if [ $? -ne 0 ]
+       then
+            warn "failed to remove $md"
+       fi
+
+       UNINSTALLED_SOMETHING=1
+    fi
+done
+
+# Load the version of the installed installer
+if [ -f "$ABS_LIBDIR/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version" ]; then
+    INSTALLED_VERSION=`cat "$ABS_LIBDIR/$TEMPLATE_REL_MANIFEST_DIR/rust-installer-version"`
+
+    # Sanity check
+    if [ ! -n "$INSTALLED_VERSION" ]; then err "rust installer version is empty"; fi
+fi
+
+# If there's something installed, then uninstall
+if [ -n "$INSTALLED_VERSION" ]; then
+    # Check the version of the installed installer
+    case "$INSTALLED_VERSION" in
+
+       # TODO: If this is a previous version, then upgrade in place to the
+       # current version before uninstalling. No need to do this yet because
+       # there is no prior version (only the legacy 'unversioned' installer
+       # which we've already dealt with).
+
+       # This is the current version. Nothing need to be done except uninstall.
+       "$TEMPLATE_RUST_INSTALLER_VERSION")
+           ;;
+
+       # TODO: If this is an unknown (future) version then bail.
+       *)
+           echo "The copy of $TEMPLATE_PRODUCT_NAME at $CFG_PREFIX was installed using an"
+           echo "unknown version ($INSTALLED_VERSION) of rust-installer."
+           echo "Uninstall it first with the installer used for the original installation"
+           echo "before continuing."
+           exit 1
+           ;;
+    esac
+
+    MD="$ABS_LIBDIR/$TEMPLATE_REL_MANIFEST_DIR"
+    INSTALLED_COMPONENTS=`cat $MD/components`
+
+    # Uninstall (our components only) before reinstalling
+    for available_component in $COMPONENTS; do
+       for installed_component in $INSTALLED_COMPONENTS; do
+           if [ "$available_component" = "$installed_component" ]; then
+               COMPONENT_MANIFEST="$MD/manifest-$installed_component"
+
+               # Sanity check: there should be a component manifest
+               if [ ! -f "$COMPONENT_MANIFEST" ]; then
+                   err "installed component '$installed_component' has no manifest"
+               fi
+
+               # Iterate through installed component manifest and remove files
+               while read directive; do
+
+                   COMMAND=`echo $directive | cut -f1 -d:`
+                   FILE=`echo $directive | cut -f2 -d:`
+
+                   # Sanity checks
+                   if [ ! -n "$COMMAND" ]; then err "malformed installation directive"; fi
+                   if [ ! -n "$FILE" ]; then err "malformed installation directive"; fi
+
+                   case "$COMMAND" in
+                       file)
+                           msg "removing file $FILE"
+                           if [ -f "$FILE" ]; then
+                               rm -f "$FILE"
+                               if [ $? -ne 0 ]; then
+                                   warn "failed to remove $FILE"
+                               fi
+                           else
+                               warn "supposedly installed file $FILE does not exist!"
+                           fi
+                           ;;
+
+                       dir)
+                           msg "removing directory $FILE"
+                           rm -Rf "$FILE"
+                           if [ $? -ne 0 ]; then
+                               warn "unable to remove directory $FILE"
+                           fi
+                           ;;
+
+                       *)
+                           err "unknown installation directive"
+                           ;;
+                   esac
+
+               done < "$COMPONENT_MANIFEST"
+
+               # Remove the installed component manifest
+               msg "removing component manifest $COMPONENT_MANIFEST"
+               rm -f "$COMPONENT_MANIFEST"
+               # This is a hard error because the installation is unrecoverable
+               need_ok "failed to remove installed manifest for component '$installed_component'"
+
+               # Update the installed component list
+               MODIFIED_COMPONENTS=`sed /^$installed_component\$/d $MD/components`
+               echo "$MODIFIED_COMPONENTS" > "$MD/components"
+               need_ok "failed to update installed component list"
+           fi
+       done
+    done
+
+    # If there are no remaining components delete the manifest directory
+    REMAINING_COMPONENTS=`cat $MD/components`
+    if [ ! -n "$REMAINING_COMPONENTS" ]; then
+       msg "removing manifest directory $MD"
+       rm -Rf "$MD"
+       if [ $? -ne 0 ]; then
+           warn "failed to remove $MD"
+       fi
+    fi
+
+    UNINSTALLED_SOMETHING=1
+fi
+
+# There's no installed version. If we were asked to uninstall, then that's a problem.
+if [ -n "${CFG_UNINSTALL}" -a ! -n "$UNINSTALLED_SOMETHING" ]
+then
+    err "unable to find installation manifest at ${CFG_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+fi
+
+# If we're only uninstalling then exit
+if [ -n "${CFG_UNINSTALL}" ]
+then
+    echo
+    echo "    ${TEMPLATE_PRODUCT_NAME} is uninstalled."
+    echo
+    exit 0
+fi
+
+# Create the directory to contain the manifests
+mkdir -p "${CFG_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+need_ok "failed to create ${TEMPLATE_REL_MANIFEST_DIR}"
+
+# Install each component
+for component in $COMPONENTS; do
+
+    # The file name of the manifest we're installing from
+    INPUT_MANIFEST="${CFG_SRC_DIR}/manifest-$component.in"
+
+    # The installed manifest directory
+    MD="$ABS_LIBDIR/$TEMPLATE_REL_MANIFEST_DIR"
+
+    # The file name of the manifest we're going to create during install
+    INSTALLED_MANIFEST="$MD/manifest-$component"
+
+    # Create the installed manifest, which we will fill in with absolute file paths
+    touch "${INSTALLED_MANIFEST}"
+    need_ok "failed to create installed manifest"
+
+    # Sanity check: do we have our input manifests?
+    if [ ! -f "$INPUT_MANIFEST" ]; then
+       err "manifest for $component does not exist at $INPUT_MANIFEST"
+    fi
+
+    # Now install, iterate through the new manifest and copy files
+    while read directive; do
+
+       COMMAND=`echo $directive | cut -f1 -d:`
+       FILE=`echo $directive | cut -f2 -d:`
+
+       # Sanity checks
+       if [ ! -n "$COMMAND" ]; then err "malformed installation directive"; fi
+       if [ ! -n "$FILE" ]; then err "malformed installation directive"; fi
+
+       # Decide the destination of the file
+       FILE_INSTALL_PATH="${CFG_DESTDIR}${CFG_PREFIX}/$FILE"
+
+       if echo "$FILE" | grep "^lib/" > /dev/null
+       then
+            f=`echo $FILE | sed 's/^lib\///'`
+            FILE_INSTALL_PATH="${CFG_LIBDIR}/$f"
+       fi
+
+       if echo "$FILE" | grep "^share/man/" > /dev/null
+       then
+            f=`echo $FILE | sed 's/^share\/man\///'`
+            FILE_INSTALL_PATH="${CFG_MANDIR}/$f"
+       fi
+
+       # Make sure there's a directory for it
+       umask 022 && mkdir -p "$(dirname ${FILE_INSTALL_PATH})"
+       need_ok "directory creation failed"
+
+       # Make the path absolute so we can uninstall it later without
+       # starting from the installation cwd
+       absolutify "${FILE_INSTALL_PATH}"
+       FILE_INSTALL_PATH="${ABSOLUTIFIED}"
+
+       case "$COMMAND" in
+           file)
+
+               # Install the file
+               msg "copying file $FILE_INSTALL_PATH"
+               if echo "$FILE" | grep "^bin/" > /dev/null
+               then
+                   install -m755 "${CFG_SRC_DIR}/$FILE" "${FILE_INSTALL_PATH}"
+               else
+                   install -m644 "${CFG_SRC_DIR}/$FILE" "${FILE_INSTALL_PATH}"
+               fi
+               need_ok "file creation failed"
+
+               # Update the manifest
+               echo "file:${FILE_INSTALL_PATH}" >> "${INSTALLED_MANIFEST}"
+               need_ok "failed to update manifest"
+
+               ;;
+
+           dir)
+
+               # Copy the dir
+               msg "copying directory $FILE_INSTALL_PATH"
+
+               # Sanity check: bulk dirs are supposed to be uniquely ours and should not exist
+               if [ -e "$FILE_INSTALL_PATH" ]; then
+                   err "$FILE_INSTALL_PATH already exists"
+               fi
+
+               cp -R "$CFG_SRC_DIR/$FILE" "$FILE_INSTALL_PATH"
+               need_ok "failed to copy directory"
+
+                # Set permissions. 0755 for dirs, 644 for files
+                chmod -R u+rwX,go+rX,go-w "$FILE_INSTALL_PATH"
+                need_ok "failed to set permissions on directory"
+
+               # Update the manifest
+               echo "dir:$FILE_INSTALL_PATH" >> "$INSTALLED_MANIFEST"
+               need_ok "failed to update manifest"
+               ;;
+
+           *)
+               err "unknown installation directive"
+               ;;
+       esac
+    done < "$INPUT_MANIFEST"
+
+    # Update the components
+    echo "$component" >> "$MD/components"
+    need_ok "failed to update components list for $component"
+
+done
+
+# Drop the version number into the manifest dir
+echo "$TEMPLATE_RUST_INSTALLER_VERSION" > "${ABS_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}/rust-installer-version"
+
+# Run ldconfig to make dynamic libraries available to the linker
+if [ "$CFG_OSTYPE" = "unknown-linux-gnu" -a ! -n "$CFG_DISABLE_LDCONFIG" ]; then
+    msg "running ldconfig"
+    ldconfig
+    if [ $? -ne 0 ]
+    then
+        warn "failed to run ldconfig."
+        warn "this may happen when not installing as root and may be fine"
+    fi
+fi
+
+# Sanity check: can we run the installed binaries?
+#
+# As with the verification above, make sure the right LD_LIBRARY_PATH-equivalent
+# is in place. Try first without this variable, and if that fails try again with
+# the variable. If the second time tries, print a hopefully helpful message to
+# add something to the appropriate environment variable.
+if [ -z "${CFG_DISABLE_VERIFY}" ]
+then
+    export $CFG_LD_PATH_VAR="${CFG_PREFIX}/lib:$CFG_OLD_LD_PATH_VAR"
+    "${CFG_PREFIX}/bin/${TEMPLATE_VERIFY_BIN}" --version > /dev/null
+    if [ $? -ne 0 ]
+    then
+        ERR="can't execute installed binaries. "
+        ERR="${ERR}installation may be broken. "
+        ERR="${ERR}if this is expected then rerun install.sh with \`--disable-verify\` "
+        ERR="${ERR}or \`make install\` with \`--disable-verify-install\`"
+        err "${ERR}"
+    else
+        echo
+        echo "    Note: please ensure '${CFG_PREFIX}/lib' is added to ${CFG_LD_PATH_VAR}"
+    fi
+fi
+
+echo
+echo "    ${TEMPLATE_SUCCESS_MESSAGE}"
+echo
+
+
diff --git a/src/rust-installer/test/rust-installer-v2/rust-installer-version b/src/rust-installer/test/rust-installer-v2/rust-installer-version
new file mode 100644 (file)
index 0000000..d8263ee
--- /dev/null
@@ -0,0 +1 @@
+2
\ No newline at end of file
diff --git a/src/rust-installer/test/rust-installer-v2/test.sh b/src/rust-installer/test/rust-installer-v2/test.sh
new file mode 100755 (executable)
index 0000000..87dcdcb
--- /dev/null
@@ -0,0 +1,588 @@
+#!/bin/sh
+
+S="$(cd $(dirname $0) && pwd)"
+
+TEST_DIR="$S/test"
+TMP_DIR="$S/tmp"
+WORK_DIR="$TMP_DIR/workdir"
+OUT_DIR="$TMP_DIR/outdir"
+PREFIX_DIR="$TMP_DIR/prefix"
+
+case $(uname -s) in
+
+    MINGW* | MSYS*)
+       WINDOWS=1
+        ;;
+esac
+
+pre() {
+    echo
+    echo "test: $1"
+    echo
+    rm -Rf "$WORK_DIR"
+    rm -Rf "$OUT_DIR"
+    rm -Rf "$PREFIX_DIR"
+    mkdir -p "$WORK_DIR"
+    mkdir -p "$OUT_DIR"
+    mkdir -p "$PREFIX_DIR"
+}
+
+post() {
+    rm -Rf "$WORK_DIR"
+    rm -Rf "$OUT_DIR"
+    rm -Rf "$PREFIX_DIR"
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+       echo
+       echo "TEST FAILED!"
+       echo
+       exit 1
+    fi
+}
+
+try() {
+    cmd="$@"
+    echo \$ "$cmd"
+    OUTPUT=`$@`
+    if [ $? -ne 0 ]; then
+       echo
+       # Using /bin/echo to avoid escaping
+       /bin/echo "$OUTPUT"
+       echo
+       echo "TEST FAILED!"
+       echo
+       exit 1
+    fi
+}
+
+expect_fail() {
+    cmd="$@"
+    echo \$ "$cmd"
+    OUTPUT=`$@`
+    if [ $? -eq 0 ]; then
+       echo
+       # Using /bin/echo to avoid escaping
+       /bin/echo "$OUTPUT"
+       echo
+       echo "TEST FAILED!"
+       echo
+       exit 1
+    fi
+}
+
+# Installation tests
+
+pre "basic install"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR"
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try test -e "$PREFIX_DIR/bin/program"
+try test -e "$PREFIX_DIR/bin/program2"
+try test -e "$PREFIX_DIR/bin/bad-bin"
+post
+
+pre "basic uninstall"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR"
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+try "$WORK_DIR/package/install.sh --uninstall" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/something-to-install"
+try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+try test ! -e "$PREFIX_DIR/bin/program"
+try test ! -e "$PREFIX_DIR/bin/program2"
+try test ! -e "$PREFIX_DIR/bin/bad-bin"
+try test ! -e "$PREFIX_DIR/lib/packagelib"
+post
+
+pre "not installed files"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --non-installed-prefixes=something-to-not-install,dir-to-not-install
+try test -e "$WORK_DIR/package/something-to-not-install"
+try test -e "$WORK_DIR/package/dir-to-not-install"
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/something-to-not-install"
+try test ! -e "$PREFIX_DIR/dir-to-not-install"
+post
+
+pre "verify override"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --verify-bin=program2
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+post
+
+pre "tarball with package name"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rustc-nightly
+try "$WORK_DIR/rustc-nightly/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$OUT_DIR/rustc-nightly.tar.gz"
+post
+
+pre "bulk directory"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --bulk-dirs=dir-to-install
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try test -e "$PREFIX_DIR/bin/program"
+try test -e "$PREFIX_DIR/bin/program2"
+try test -e "$PREFIX_DIR/bin/bad-bin"
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+try test ! -e "$PREFIX_DIR/dir-to-install"
+post
+
+pre "nested bulk directory"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image4" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --bulk-dirs=dir-to-install/qux
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/dir-to-install/qux/bar"
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+try test ! -e "$PREFIX_DIR/dir-to-install/qux"
+post
+
+pre "only bulk directory, no files"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image5" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --bulk-dirs=dir-to-install
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+post
+
+pre "nested not installed files"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image4" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --non-installed-prefixes=dir-to-install/qux/bar
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/dir-to-install/qux/bar"
+post
+
+# Upgrade tests
+
+pre "upgrade v1 -> v2"
+mkdir "$WORK_DIR/v1"
+try sh "$S/test/rust-installer-v1/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image2" \
+    --work-dir="$WORK_DIR/v1" \
+    --output-dir="$OUT_DIR/v1" \
+    --verify-bin=oldprogram \
+    --rel-manifest-dir=packagelib
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --rel-manifest-dir=packagelib \
+    --legacy-manifest-dirs=packagelib
+try "$WORK_DIR/v1/package/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/bar"
+try test -e "$PREFIX_DIR/bin/oldprogram"
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/dir-to-install/bar"
+try test ! -e "$PREFIX_DIR/bin/oldprogram"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try test -e "$PREFIX_DIR/bin/program"
+try test -e "$PREFIX_DIR/bin/program2"
+try test -e "$PREFIX_DIR/bin/bad-bin"
+try "$WORK_DIR/package/install.sh --uninstall" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/something-to-install"
+try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+try test ! -e "$PREFIX_DIR/bin/program"
+try test ! -e "$PREFIX_DIR/bin/program2"
+try test ! -e "$PREFIX_DIR/bin/bad-bin"
+try test ! -e "$PREFIX_DIR/lib/packagelib"
+post
+
+pre "upgrade v1 -> v2 with multiple legacy manifests"
+mkdir "$WORK_DIR/v1"
+try sh "$S/test/rust-installer-v1/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image2" \
+    --work-dir="$WORK_DIR/v1" \
+    --output-dir="$OUT_DIR/v1" \
+    --verify-bin=oldprogram \
+    --rel-manifest-dir=rustlib
+try sh "$S/test/rust-installer-v1/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image3" \
+    --work-dir="$WORK_DIR/v1b" \
+    --output-dir="$OUT_DIR/v1b" \
+    --verify-bin=cargo \
+    --rel-manifest-dir=cargo
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --rel-manifest-dir=packagelib \
+    --legacy-manifest-dirs=rustlib,cargo
+try "$WORK_DIR/v1/package/install.sh" --prefix="$PREFIX_DIR"
+try "$WORK_DIR/v1b/package/install.sh" --prefix="$PREFIX_DIR"
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/dir-to-install/bar"
+try test ! -e "$PREFIX_DIR/bin/oldprogram"
+try test ! -e "$PREFIX_DIR/bin/cargo"
+try test ! -e "$PREFIX_DIR/lib/cargo"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try test -e "$PREFIX_DIR/bin/program"
+try test -e "$PREFIX_DIR/bin/program2"
+try test -e "$PREFIX_DIR/bin/bad-bin"
+try "$WORK_DIR/package/install.sh --uninstall" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/something-to-install"
+try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+try test ! -e "$PREFIX_DIR/bin/program"
+try test ! -e "$PREFIX_DIR/bin/program2"
+try test ! -e "$PREFIX_DIR/bin/bad-bin"
+try test ! -e "$PREFIX_DIR/lib/packagelib"
+post
+
+pre "multiple components"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR/c1" \
+    --output-dir="$OUT_DIR/c1" \
+    --component-name=rustc
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image3" \
+    --work-dir="$WORK_DIR/c2" \
+    --output-dir="$OUT_DIR/c2" \
+    --verify-bin=cargo \
+    --component-name=cargo
+try "$WORK_DIR/c1/package/install.sh" --prefix="$PREFIX_DIR"
+try "$WORK_DIR/c2/package/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try test -e "$PREFIX_DIR/bin/program"
+try test -e "$PREFIX_DIR/bin/program2"
+try test -e "$PREFIX_DIR/bin/bad-bin"
+try test -e "$PREFIX_DIR/bin/cargo"
+try "$WORK_DIR/c1/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+try test ! -e "$PREFIX_DIR/something-to-install"
+try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+try test ! -e "$PREFIX_DIR/bin/program"
+try test ! -e "$PREFIX_DIR/bin/program2"
+try test ! -e "$PREFIX_DIR/bin/bad-bin"
+try "$WORK_DIR/c2/package/install.sh" --prefix="$PREFIX_DIR" --uninstall
+try test ! -e "$PREFIX_DIR/bin/cargo"
+try test ! -e "$PREFIX_DIR/lib/packagelib"
+post
+
+# Combined installer tests
+
+pre "combine installers"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rustc \
+    --component-name=rustc
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image3" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --verify-bin=cargo \
+    --package-name=cargo \
+    --component-name=cargo
+try sh "$S/combine-installers.sh" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rust \
+    --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
+try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try test -e "$PREFIX_DIR/bin/program"
+try test -e "$PREFIX_DIR/bin/program2"
+try test -e "$PREFIX_DIR/bin/bad-bin"
+try test -e "$PREFIX_DIR/bin/cargo"
+try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/something-to-install"
+try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+try test ! -e "$PREFIX_DIR/bin/program"
+try test ! -e "$PREFIX_DIR/bin/program2"
+try test ! -e "$PREFIX_DIR/bin/bad-bin"
+try test ! -e "$PREFIX_DIR/bin/cargo"
+try test ! -e "$PREFIX_DIR/lib/packagelib"
+post
+
+pre "combine three installers"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rustc \
+    --component-name=rustc
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image3" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --verify-bin=cargo \
+    --package-name=cargo \
+    --component-name=cargo
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image4" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rust-docs \
+    --component-name=rust-docs
+try sh "$S/combine-installers.sh" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rust \
+    --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
+try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try test -e "$PREFIX_DIR/bin/program"
+try test -e "$PREFIX_DIR/bin/program2"
+try test -e "$PREFIX_DIR/bin/bad-bin"
+try test -e "$PREFIX_DIR/bin/cargo"
+try test -e "$PREFIX_DIR/dir-to-install/qux/bar"
+try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/something-to-install"
+try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+try test ! -e "$PREFIX_DIR/bin/program"
+try test ! -e "$PREFIX_DIR/bin/program2"
+try test ! -e "$PREFIX_DIR/bin/bad-bin"
+try test ! -e "$PREFIX_DIR/bin/cargo"
+try test ! -e "$PREFIX_DIR/lib/packagelib"
+try test ! -e "$PREFIX_DIR/dir-to-install/qux/bar"
+post
+
+pre "combine installers with overlay"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rustc \
+    --component-name=rustc
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image3" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --verify-bin=cargo \
+    --package-name=cargo \
+    --component-name=cargo
+mkdir -p "$WORK_DIR/overlay"
+touch "$WORK_DIR/overlay/README"
+try sh "$S/combine-installers.sh" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rust \
+    --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+    --non-installed-overlay="$WORK_DIR/overlay"
+try test -e "$WORK_DIR/rust/README"
+try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/README"
+post
+
+pre "combined with bulk dirs"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rustc \
+    --component-name=rustc \
+    --bulk-dirs=dir-to-install
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image3" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --verify-bin=cargo \
+    --package-name=cargo \
+    --component-name=cargo
+try sh "$S/combine-installers.sh" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rust \
+    --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz"
+try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/dir-to-install"
+post
+
+pre "combine install with separate uninstall"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rustc \
+    --component-name=rustc \
+    --rel-manifest-dir=rustlib
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image3" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --verify-bin=cargo \
+    --package-name=cargo \
+    --component-name=cargo \
+    --rel-manifest-dir=rustlib
+try sh "$S/combine-installers.sh" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rust \
+    --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+    --rel-manifest-dir=rustlib
+try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try test -e "$PREFIX_DIR/bin/program"
+try test -e "$PREFIX_DIR/bin/program2"
+try test -e "$PREFIX_DIR/bin/bad-bin"
+try test -e "$PREFIX_DIR/bin/cargo"
+try "$WORK_DIR/rustc/install.sh --uninstall" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/something-to-install"
+try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+try test ! -e "$PREFIX_DIR/bin/program"
+try test ! -e "$PREFIX_DIR/bin/program2"
+try test ! -e "$PREFIX_DIR/bin/bad-bin"
+try "$WORK_DIR/cargo/install.sh --uninstall" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/bin/cargo"
+try test ! -e "$PREFIX_DIR/lib/packagelib"
+post
+
+pre "combined v1 -> v2 upgrade"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rustc \
+    --component-name=rustc \
+    --rel-manifest-dir=rustlib
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image3" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --verify-bin=cargo \
+    --package-name=cargo \
+    --component-name=cargo \
+    --rel-manifest-dir=rustlib
+try sh "$S/combine-installers.sh" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=rust \
+    --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz" \
+    --rel-manifest-dir=rustlib \
+    --legacy-manifest-dirs=cargo,rustlib
+try sh "$S/test/rust-installer-v1/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image2" \
+    --work-dir="$WORK_DIR/v1" \
+    --output-dir="$OUT_DIR/v1" \
+    --verify-bin=oldprogram \
+    --rel-manifest-dir=rustlib
+try sh "$S/test/rust-installer-v1/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image3" \
+    --work-dir="$WORK_DIR/v1b" \
+    --output-dir="$OUT_DIR/v1b" \
+    --verify-bin=cargo \
+    --rel-manifest-dir=cargo
+try "$WORK_DIR/v1/package//install.sh" --prefix="$PREFIX_DIR"
+try "$WORK_DIR/v1b/package//install.sh" --prefix="$PREFIX_DIR"
+try "$WORK_DIR/rust/install.sh" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/dir-to-install/bar"
+try test ! -e "$PREFIX_DIR/bin/oldprogram"
+try test ! -e "$PREFIX_DIR/lib/cargo"
+try test -e "$PREFIX_DIR/something-to-install"
+try test -e "$PREFIX_DIR/dir-to-install/foo"
+try test -e "$PREFIX_DIR/bin/program"
+try test -e "$PREFIX_DIR/bin/program2"
+try test -e "$PREFIX_DIR/bin/bad-bin"
+try test -e "$PREFIX_DIR/bin/cargo"
+try test -e "$PREFIX_DIR/lib/rustlib"
+try "$WORK_DIR/rust/install.sh --uninstall" --prefix="$PREFIX_DIR"
+try test ! -e "$PREFIX_DIR/something-to-install"
+try test ! -e "$PREFIX_DIR/dir-to-install/foo"
+try test ! -e "$PREFIX_DIR/bin/program"
+try test ! -e "$PREFIX_DIR/bin/program2"
+try test ! -e "$PREFIX_DIR/bin/bad-bin"
+try test ! -e "$PREFIX_DIR/bin/cargo"
+try test ! -e "$PREFIX_DIR/lib/rustlib"
+post
+
+# Smoke tests
+
+pre "can't run bins error"
+try sh "$S/gen-installer.sh" \
+    --verify-bin=bad-bin \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR"
+expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+post
+
+if [ ! -n "$WINDOWS" ]; then
+    # chmod doesn't work on windows
+    pre "can't write error"
+    try sh "$S/gen-installer.sh" \
+       --image-dir="$TEST_DIR/image1" \
+       --work-dir="$WORK_DIR" \
+       --output-dir="$OUT_DIR"
+    chmod u-w "$PREFIX_DIR"
+    expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+    chmod u+w "$PREFIX_DIR"
+    post
+fi
+
+pre "can't install to installer"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --package-name=my-package
+expect_fail "$WORK_DIR/my-package/install.sh" --prefix="$WORK_DIR/my-package"
+post
+
+pre "upgrade from future installer error"
+try sh "$S/gen-installer.sh" \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR" \
+    --rel-manifest-dir=rustlib
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+echo 100 > "$PREFIX_DIR/lib/rustlib/rust-installer-version"
+expect_fail "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR"
+post
+
+pre "disable-verify"
+try sh "$S/gen-installer.sh" \
+    --verify-bin=bad-bin \
+    --image-dir="$TEST_DIR/image1" \
+    --work-dir="$WORK_DIR" \
+    --output-dir="$OUT_DIR"
+try "$WORK_DIR/package/install.sh" --prefix="$PREFIX_DIR" --disable-verify
+post
+
+# TODO: DESTDIR
+# TODO: mandir/libdir/bindir, etc.
+
+echo
+echo "TOTAL SUCCESS!"
+echo
diff --git a/src/rust-installer/test/rust-installer-v2/test/image1/bin/bad-bin b/src/rust-installer/test/rust-installer-v2/test/image1/bin/bad-bin
new file mode 100644 (file)
index 0000000..b5b0e32
--- /dev/null
@@ -0,0 +1 @@
+#!/bin/bogus
\ No newline at end of file
diff --git a/src/rust-installer/test/rust-installer-v2/test/image1/bin/program b/src/rust-installer/test/rust-installer-v2/test/image1/bin/program
new file mode 100755 (executable)
index 0000000..96b4b06
--- /dev/null
@@ -0,0 +1 @@
+#!/bin/sh
\ No newline at end of file
diff --git a/src/rust-installer/test/rust-installer-v2/test/image1/bin/program2 b/src/rust-installer/test/rust-installer-v2/test/image1/bin/program2
new file mode 100755 (executable)
index 0000000..96b4b06
--- /dev/null
@@ -0,0 +1 @@
+#!/bin/sh
\ No newline at end of file
diff --git a/src/rust-installer/test/rust-installer-v2/test/image1/dir-to-install/foo b/src/rust-installer/test/rust-installer-v2/test/image1/dir-to-install/foo
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/rust-installer/test/rust-installer-v2/test/image1/dir-to-not-install/foo b/src/rust-installer/test/rust-installer-v2/test/image1/dir-to-not-install/foo
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/rust-installer/test/rust-installer-v2/test/image1/something-to-install b/src/rust-installer/test/rust-installer-v2/test/image1/something-to-install
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/rust-installer/test/rust-installer-v2/test/image1/something-to-not-install b/src/rust-installer/test/rust-installer-v2/test/image1/something-to-not-install
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/rust-installer/test/rust-installer-v2/test/image2/bin/oldprogram b/src/rust-installer/test/rust-installer-v2/test/image2/bin/oldprogram
new file mode 100755 (executable)
index 0000000..96b4b06
--- /dev/null
@@ -0,0 +1 @@
+#!/bin/sh
\ No newline at end of file
diff --git a/src/rust-installer/test/rust-installer-v2/test/image2/dir-to-install/bar b/src/rust-installer/test/rust-installer-v2/test/image2/dir-to-install/bar
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/rust-installer/test/rust-installer-v2/test/image2/something-to-install b/src/rust-installer/test/rust-installer-v2/test/image2/something-to-install
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/rust-installer/test/rust-installer-v2/test/image3/bin/cargo b/src/rust-installer/test/rust-installer-v2/test/image3/bin/cargo
new file mode 100755 (executable)
index 0000000..96b4b06
--- /dev/null
@@ -0,0 +1 @@
+#!/bin/sh
\ No newline at end of file
diff --git a/src/rust-installer/test/rust-installer-v2/test/image4/baz b/src/rust-installer/test/rust-installer-v2/test/image4/baz
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/rust-installer/test/rust-installer-v2/test/image4/dir-to-install/qux/bar b/src/rust-installer/test/rust-installer-v2/test/image4/dir-to-install/qux/bar
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/rust-installer/test/rust-installer-v2/test/image5/dir-to-install/foo b/src/rust-installer/test/rust-installer-v2/test/image5/dir-to-install/foo
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/README.md b/src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/README.md
new file mode 100644 (file)
index 0000000..f541504
--- /dev/null
@@ -0,0 +1,30 @@
+A generator for the install.sh script commonly used to install Rust in
+Unix environments. It is used By Rust, Cargo, and is intended to be
+used by a future combined installer of Rust + Cargo.
+
+# Usage
+
+```
+./gen-installer.sh --product-name=Rust \
+                   --verify-bin=rustc \
+                   --rel-manifest-dir=rustlib \
+                   --success-message=Rust-is-ready-to-roll. \
+                   --image-dir=./install-image \
+                   --work-dir=./temp \
+                   --output-dir=./dist \
+                   --non-installed-prefixes="foo,bin/bar,lib/baz" \
+                   --package-name=rustc-nightly-i686-apple-darwin
+```
+
+Or, to just generate the script.
+
+```
+./gen-install-script.sh --product-name=Rust \
+                        --verify-bin=rustc \
+                        --rel-manifest-dir=rustlib \
+                        --success-message=Rust-is-ready-to-roll. \
+                        --output-script=install.sh
+```
+
+*Note: the dashes in `success-message` are converted to spaces. The
+script's argument handling is broken with spaces.*
\ No newline at end of file
diff --git a/src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/gen-install-script.sh b/src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/gen-install-script.sh
new file mode 100755 (executable)
index 0000000..4c7e327
--- /dev/null
@@ -0,0 +1,253 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "gen-install-script: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "gen-install-script: WARNING: $1"
+}
+
+err() {
+    echo "gen-install-script: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "gen-install-script: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "gen-install-script: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+msg "looking for install programs"
+msg
+
+need_cmd sed
+need_cmd chmod
+need_cmd cat
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+valopt product-name "Product" "The name of the product, for display"
+valopt verify-bin "program" "The command to run with --version to verify the install works"
+valopt rel-manifest-dir "${CFG_VERIFY_BIN}lib" "The directory under lib/ where the manifest lives"
+valopt success-message "Installed." "The string to print after successful installation"
+valopt output-script "${CFG_SRC_DIR}/install.sh" "The name of the output script"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+# Replace dashes in the success message with spaces (our arg handling botches spaces)
+CFG_SUCCESS_MESSAGE=`echo "$CFG_SUCCESS_MESSAGE" | sed "s/-/ /g"`
+
+SCRIPT_TEMPLATE=`cat "${CFG_SRC_DIR}/install-template.sh"`
+
+# Using /bin/echo because under sh emulation dash *seems* to escape \n, which screws up the template
+SCRIPT=`/bin/echo "${SCRIPT_TEMPLATE}"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_PRODUCT_NAME%%/${CFG_PRODUCT_NAME}/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_VERIFY_BIN%%/${CFG_VERIFY_BIN}/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_REL_MANIFEST_DIR%%/${CFG_REL_MANIFEST_DIR}/"`
+SCRIPT=`/bin/echo "${SCRIPT}" | sed "s/%%TEMPLATE_SUCCESS_MESSAGE%%/\"${CFG_SUCCESS_MESSAGE}\"/"`
+
+/bin/echo "${SCRIPT}" > "${CFG_OUTPUT_SCRIPT}"
+need_ok "couldn't write script"
+chmod u+x "${CFG_OUTPUT_SCRIPT}"
+need_ok "couldn't chmod script"
diff --git a/src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/gen-installer.sh b/src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/gen-installer.sh
new file mode 100755 (executable)
index 0000000..c88eebe
--- /dev/null
@@ -0,0 +1,303 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "gen-installer: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "gen-installer: WARNING: $1"
+}
+
+err() {
+    echo "gen-installer: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "gen-installer: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "gen-installer: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+msg "looking for programs"
+msg
+
+need_cmd tar
+need_cmd cp
+need_cmd rm
+need_cmd mkdir
+need_cmd echo
+need_cmd tr
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+valopt product-name "Product" "The name of the product, for display"
+valopt package-name "package" "The name of the package, tarball"
+valopt verify-bin "program" "The command to run with --version to verify the install works"
+valopt rel-manifest-dir "${CFG_VERIFY_BIN}lib" "The directory under lib/ where the manifest lives"
+valopt success-message "Installed." "The string to print after successful installation"
+valopt non-installed-prefixes "" "Path prefixes that should be included but not installed"
+valopt image-dir "./install-image" "The directory containing the installation medium"
+valopt work-dir "./workdir" "The directory to do temporary work"
+valopt output-dir "./dist" "The location to put the final image and tarball"
+opt tarball 1 "Disable tarball generation, leaving output in the temp dir"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+GEN_INSTALL_SCRIPT="$CFG_SRC_DIR/gen-install-script.sh"
+
+if [ ! -d "$CFG_IMAGE_DIR" ]
+then
+    err "image dir $CFG_IMAGE_DIR does not exist"
+fi
+
+mkdir -p "$CFG_WORK_DIR"
+need_ok "couldn't create work dir"
+
+rm -Rf "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't delete work package dir"
+
+mkdir -p "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't create work package dir"
+
+cp -r "$CFG_IMAGE_DIR/"* "$CFG_WORK_DIR/$CFG_PACKAGE_NAME"
+need_ok "couldn't copy source image"
+
+# Split non_installed_files into lines for later iteration
+NON_INSTALLED_PREFIXES=`echo "$CFG_NON_INSTALLED_PREFIXES" | tr "," "\n"`
+
+# Create the manifest
+MANIFEST=`(cd "$CFG_WORK_DIR/$CFG_PACKAGE_NAME" && find . -type f | sed 's/^\.\///') | sort`
+
+# Remove non-installed files from manifest
+NON_INSTALLED_PREFIXES=`echo "$CFG_NON_INSTALLED_PREFIXES" | tr "," " "`
+for prefix in $NON_INSTALLED_PREFIXES; do
+    MANIFEST=`echo "$MANIFEST" | sed /^$prefix/d`
+done
+
+MANIFEST_NAME="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/lib/$CFG_REL_MANIFEST_DIR/manifest.in"
+
+mkdir -p "$CFG_WORK_DIR/$CFG_PACKAGE_NAME/lib/$CFG_REL_MANIFEST_DIR"
+need_ok "couldn't create manifest dir"
+
+# Write the manifest
+echo "$MANIFEST" > "$MANIFEST_NAME"
+
+# Generate the install script
+"$CFG_SRC_DIR/gen-install-script.sh" \
+    --product-name="$CFG_PRODUCT_NAME" \
+    --verify-bin="$CFG_VERIFY_BIN" \
+    --rel-manifest-dir="$CFG_REL_MANIFEST_DIR" \
+    --success-message="$CFG_SUCCESS_MESSAGE" \
+    --output-script="$CFG_WORK_DIR/$CFG_PACKAGE_NAME/install.sh"
+
+need_ok "failed to generate install script"    
+
+mkdir -p "$CFG_OUTPUT_DIR"
+need_ok "couldn't create output dir"
+
+rm -Rf "$CFG_OUTPUT_DIR/$CFG_PACKAGE_NAME.tar.gz"
+need_ok "couldn't delete old tarball"
+
+# Make a tarball
+tar -czf "$CFG_OUTPUT_DIR/$CFG_PACKAGE_NAME.tar.gz" -C "$CFG_WORK_DIR" "$CFG_PACKAGE_NAME"
+need_ok "failed to tar"
diff --git a/src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/install-template.sh b/src/rust-installer/test/rust-installer-v2/test/rust-installer-v1/install-template.sh
new file mode 100644 (file)
index 0000000..545de67
--- /dev/null
@@ -0,0 +1,554 @@
+#!/bin/sh
+# Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution and at
+# http://rust-lang.org/COPYRIGHT.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+msg() {
+    echo "install: $1"
+}
+
+step_msg() {
+    msg
+    msg "$1"
+    msg
+}
+
+warn() {
+    echo "install: WARNING: $1"
+}
+
+err() {
+    echo "install: error: $1"
+    exit 1
+}
+
+need_ok() {
+    if [ $? -ne 0 ]
+    then
+        err "$1"
+    fi
+}
+
+need_cmd() {
+    if command -v $1 >/dev/null 2>&1
+    then msg "found $1"
+    else err "need $1"
+    fi
+}
+
+putvar() {
+    local T
+    eval T=\$$1
+    eval TLEN=\${#$1}
+    if [ $TLEN -gt 35 ]
+    then
+        printf "install: %-20s := %.35s ...\n" $1 "$T"
+    else
+        printf "install: %-20s := %s %s\n" $1 "$T" "$2"
+    fi
+}
+
+valopt() {
+    VAL_OPTIONS="$VAL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    if [ $HELP -eq 0 ]
+    then
+        local UOP=$(echo $OP | tr '[:lower:]' '[:upper:]' | tr '\-' '\_')
+        local V="CFG_${UOP}"
+        eval $V="$DEFAULT"
+        for arg in $CFG_ARGS
+        do
+            if echo "$arg" | grep -q -- "--$OP="
+            then
+                val=$(echo "$arg" | cut -f2 -d=)
+                eval $V=$val
+            fi
+        done
+        putvar $V
+    else
+        if [ -z "$DEFAULT" ]
+        then
+            DEFAULT="<none>"
+        fi
+        OP="${OP}=[${DEFAULT}]"
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+    fi
+}
+
+opt() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    local DEFAULT=$2
+    shift
+    shift
+    local DOC="$*"
+    local FLAG=""
+
+    if [ $DEFAULT -eq 0 ]
+    then
+        FLAG="enable"
+    else
+        FLAG="disable"
+        DOC="don't $DOC"
+    fi
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${FLAG}-${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                FLAG=$(echo $FLAG | tr 'a-z' 'A-Z')
+                local V="CFG_${FLAG}_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$FLAG-$OP" "$DOC"
+     fi
+}
+
+flag() {
+    BOOL_OPTIONS="$BOOL_OPTIONS $1"
+
+    local OP=$1
+    shift
+    local DOC="$*"
+
+    if [ $HELP -eq 0 ]
+    then
+        for arg in $CFG_ARGS
+        do
+            if [ "$arg" = "--${OP}" ]
+            then
+                OP=$(echo $OP | tr 'a-z-' 'A-Z_')
+                local V="CFG_${OP}"
+                eval $V=1
+                putvar $V
+            fi
+        done
+    else
+        if [ ! -z "$META" ]
+        then
+            OP="$OP=<$META>"
+        fi
+        printf "    --%-30s %s\n" "$OP" "$DOC"
+     fi
+}
+
+validate_opt () {
+    for arg in $CFG_ARGS
+    do
+        isArgValid=0
+        for option in $BOOL_OPTIONS
+        do
+            if test --disable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --enable-$option = $arg
+            then
+                isArgValid=1
+            fi
+            if test --$option = $arg
+            then
+                isArgValid=1
+            fi
+        done
+        for option in $VAL_OPTIONS
+        do
+            if echo "$arg" | grep -q -- "--$option="
+            then
+                isArgValid=1
+            fi
+        done
+        if [ "$arg" = "--help" ]
+        then
+            echo
+            echo "No more help available for Configure options,"
+            echo "check the Wiki or join our IRC channel"
+            break
+        else
+            if test $isArgValid -eq 0
+            then
+                err "Option '$arg' is not recognized"
+            fi
+        fi
+    done
+}
+
+absolutify() {
+    FILE_PATH="${1}"
+    FILE_PATH_DIRNAME="$(dirname ${FILE_PATH})"
+    FILE_PATH_BASENAME="$(basename ${FILE_PATH})"
+    FILE_ABS_PATH="$(cd ${FILE_PATH_DIRNAME} && pwd)"
+    FILE_PATH="${FILE_ABS_PATH}/${FILE_PATH_BASENAME}"
+    # This is the return value
+    ABSOLUTIFIED="${FILE_PATH}"
+}
+
+msg "looking for install programs"
+msg
+
+need_cmd mkdir
+need_cmd printf
+need_cmd cut
+need_cmd grep
+need_cmd uname
+need_cmd tr
+need_cmd sed
+
+CFG_SRC_DIR="$(cd $(dirname $0) && pwd)"
+CFG_SELF="$0"
+CFG_ARGS="$@"
+
+HELP=0
+if [ "$1" = "--help" ]
+then
+    HELP=1
+    shift
+    echo
+    echo "Usage: $CFG_SELF [options]"
+    echo
+    echo "Options:"
+    echo
+else
+    step_msg "processing $CFG_SELF args"
+fi
+
+# Check for mingw or cygwin in order to special case $CFG_LIBDIR_RELATIVE.
+# This logic is duplicated from configure in order to get the correct libdir
+# for Windows installs.
+CFG_OSTYPE=$(uname -s)
+
+case $CFG_OSTYPE in
+
+    Linux)
+        CFG_OSTYPE=unknown-linux-gnu
+        ;;
+
+    FreeBSD)
+        CFG_OSTYPE=unknown-freebsd
+        ;;
+
+    DragonFly)
+        CFG_OSTYPE=unknown-dragonfly
+        ;;
+
+    Darwin)
+        CFG_OSTYPE=apple-darwin
+        ;;
+
+    MINGW*)
+        # msys' `uname` does not print gcc configuration, but prints msys
+        # configuration. so we cannot believe `uname -m`:
+        # msys1 is always i686 and msys2 is always x86_64.
+        # instead, msys defines $MSYSTEM which is MINGW32 on i686 and
+        # MINGW64 on x86_64.
+        CFG_CPUTYPE=i686
+        CFG_OSTYPE=pc-windows-gnu
+        if [ "$MSYSTEM" = MINGW64 ]
+        then
+            CFG_CPUTYPE=x86_64
+        fi
+        ;;
+
+    MSYS*)
+        CFG_OSTYPE=pc-windows-gnu
+        ;;
+
+# Thad's Cygwin identifers below
+
+#   Vista 32 bit
+    CYGWIN_NT-6.0)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=i686
+        ;;
+
+#   Vista 64 bit
+    CYGWIN_NT-6.0-WOW64)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=x86_64
+        ;;
+
+#   Win 7 32 bit
+    CYGWIN_NT-6.1)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=i686
+        ;;
+
+#   Win 7 64 bit
+    CYGWIN_NT-6.1-WOW64)
+        CFG_OSTYPE=pc-windows-gnu
+        CFG_CPUTYPE=x86_64
+        ;;
+esac
+
+OPTIONS=""
+BOOL_OPTIONS=""
+VAL_OPTIONS=""
+
+if [ "$CFG_OSTYPE" = "pc-windows-gnu" ]
+then
+    CFG_LD_PATH_VAR=PATH
+    CFG_OLD_LD_PATH_VAR=$PATH
+elif [ "$CFG_OSTYPE" = "apple-darwin" ]
+then
+    CFG_LD_PATH_VAR=DYLD_LIBRARY_PATH
+    CFG_OLD_LD_PATH_VAR=$DYLD_LIBRARY_PATH
+else
+    CFG_LD_PATH_VAR=LD_LIBRARY_PATH
+    CFG_OLD_LD_PATH_VAR=$LD_LIBRARY_PATH
+fi
+
+flag uninstall "only uninstall from the installation prefix"
+valopt destdir "" "set installation root"
+opt verify 1 "verify that the installed binaries run correctly"
+valopt prefix "/usr/local" "set installation prefix"
+# NB This isn't quite the same definition as in `configure`.
+# just using 'lib' instead of configure's CFG_LIBDIR_RELATIVE
+valopt libdir "${CFG_DESTDIR}${CFG_PREFIX}/lib" "install libraries"
+valopt mandir "${CFG_DESTDIR}${CFG_PREFIX}/share/man" "install man pages in PATH"
+
+if [ $HELP -eq 1 ]
+then
+    echo
+    exit 0
+fi
+
+step_msg "validating $CFG_SELF args"
+validate_opt
+
+
+
+# Template configuration.
+# These names surrounded by '%%` are replaced by sed when generating install.sh
+
+# Rust or Cargo
+TEMPLATE_PRODUCT_NAME=%%TEMPLATE_PRODUCT_NAME%%
+# rustc or cargo
+TEMPLATE_VERIFY_BIN=%%TEMPLATE_VERIFY_BIN%%
+# rustlib or cargo
+TEMPLATE_REL_MANIFEST_DIR=%%TEMPLATE_REL_MANIFEST_DIR%%
+# 'Rust is ready to roll.' or 'Cargo is cool to cruise.'
+TEMPLATE_SUCCESS_MESSAGE=%%TEMPLATE_SUCCESS_MESSAGE%%
+
+# OK, let's get installing ...
+
+# Sanity check: can we run the binaries?
+if [ -z "${CFG_DISABLE_VERIFY}" ]
+then
+    # Don't do this if uninstalling. Failure here won't help in any way.
+    if [ -z "${CFG_UNINSTALL}" ]
+    then
+        msg "verifying platform can run binaries"
+        export $CFG_LD_PATH_VAR="${CFG_SRC_DIR}/lib:$CFG_OLD_LD_PATH_VAR"
+        "${CFG_SRC_DIR}/bin/${TEMPLATE_VERIFY_BIN}" --version 2> /dev/null 1> /dev/null
+        if [ $? -ne 0 ]
+        then
+            err "can't execute rustc binary on this platform"
+        fi
+        export $CFG_LD_PATH_VAR="$CFG_OLD_LD_PATH_VAR"
+    fi
+fi
+
+# Sanity check: can we can write to the destination?
+msg "verifying destination is writable"
+umask 022 && mkdir -p "${CFG_LIBDIR}"
+need_ok "can't write to destination. consider \`sudo\`."
+touch "${CFG_LIBDIR}/rust-install-probe" > /dev/null
+if [ $? -ne 0 ]
+then
+    err "can't write to destination. consider \`sudo\`."
+fi
+rm -f "${CFG_LIBDIR}/rust-install-probe"
+need_ok "failed to remove install probe"
+
+# Sanity check: don't install to the directory containing the installer.
+# That would surely cause chaos.
+msg "verifying destination is not the same as source"
+INSTALLER_DIR="$(cd $(dirname $0) && pwd)"
+PREFIX_DIR="$(cd ${CFG_PREFIX} && pwd)"
+if [ "${INSTALLER_DIR}" = "${PREFIX_DIR}" ]
+then
+    err "can't install to same directory as installer"
+fi
+
+# Using an absolute path to libdir in a few places so that the status
+# messages are consistently using absolute paths.
+absolutify "${CFG_LIBDIR}"
+ABS_LIBDIR="${ABSOLUTIFIED}"
+
+# The file name of the manifest we're going to create during install
+INSTALLED_MANIFEST="${ABS_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}/manifest"
+
+# First, uninstall from the installation prefix.
+# Errors are warnings - try to rm everything in the manifest even if some fail.
+if [ -f "${INSTALLED_MANIFEST}" ]
+then
+    msg
+
+    # Iterate through installed manifest and remove files
+    while read p; do
+        # The installed manifest contains absolute paths
+        msg "removing $p"
+        if [ -f "$p" ]
+        then
+            rm -f "$p"
+            if [ $? -ne 0 ]
+            then
+                warn "failed to remove $p"
+            fi
+        else
+            warn "supposedly installed file $p does not exist!"
+        fi
+    done < "${INSTALLED_MANIFEST}"
+
+    # If we fail to remove $TEMPLATE_REL_MANIFEST_DIR below, then the
+    # installed manifest will still be full; the installed manifest
+    # needs to be empty before install.
+    msg "removing ${INSTALLED_MANIFEST}"
+    rm -f "${INSTALLED_MANIFEST}"
+    # For the above reason, this is a hard error
+    need_ok "failed to remove installed manifest"
+
+    # Remove $TEMPLATE_REL_MANIFEST_DIR directory
+    msg "removing ${ABS_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+    rm -Rf "${ABS_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+    if [ $? -ne 0 ]
+    then
+        warn "failed to remove ${TEMPLATE_REL_MANIFEST_DIR}"
+    fi
+else
+    # There's no manifest. If we were asked to uninstall, then that's a problem.
+    if [ -n "${CFG_UNINSTALL}" ]
+    then
+        err "unable to find installation manifest at ${CFG_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+    fi
+fi
+
+# If we're only uninstalling then exit
+if [ -n "${CFG_UNINSTALL}" ]
+then
+    echo
+    echo "    ${TEMPLATE_PRODUCT_NAME} is uninstalled."
+    echo
+    exit 0
+fi
+
+# Create the installed manifest, which we will fill in with absolute file paths
+mkdir -p "${CFG_LIBDIR}/${TEMPLATE_REL_MANIFEST_DIR}"
+need_ok "failed to create ${TEMPLATE_REL_MANIFEST_DIR}"
+touch "${INSTALLED_MANIFEST}"
+need_ok "failed to create installed manifest"
+
+msg
+
+# Now install, iterate through the new manifest and copy files
+while read p; do
+
+    # Decide the destination of the file
+    FILE_INSTALL_PATH="${CFG_DESTDIR}${CFG_PREFIX}/$p"
+
+    if echo "$p" | grep "^lib/" > /dev/null
+    then
+        pp=`echo $p | sed 's/^lib\///'`
+        FILE_INSTALL_PATH="${CFG_LIBDIR}/$pp"
+    fi
+
+    if echo "$p" | grep "^share/man/" > /dev/null
+    then
+        pp=`echo $p | sed 's/^share\/man\///'`
+        FILE_INSTALL_PATH="${CFG_MANDIR}/$pp"
+    fi
+
+    # Make sure there's a directory for it
+    umask 022 && mkdir -p "$(dirname ${FILE_INSTALL_PATH})"
+    need_ok "directory creation failed"
+
+    # Make the path absolute so we can uninstall it later without
+    # starting from the installation cwd
+    absolutify "${FILE_INSTALL_PATH}"
+    FILE_INSTALL_PATH="${ABSOLUTIFIED}"
+
+    # Install the file
+    msg "installing ${FILE_INSTALL_PATH}"
+    if echo "$p" | grep "^bin/" > /dev/null
+    then
+        install -m755 "${CFG_SRC_DIR}/$p" "${FILE_INSTALL_PATH}"
+    else
+        install -m644 "${CFG_SRC_DIR}/$p" "${FILE_INSTALL_PATH}"
+    fi
+    need_ok "file creation failed"
+
+    # Update the manifest
+    echo "${FILE_INSTALL_PATH}" >> "${INSTALLED_MANIFEST}"
+    need_ok "failed to update manifest"
+
+# The manifest lists all files to install
+done < "${CFG_SRC_DIR}/lib/${TEMPLATE_REL_MANIFEST_DIR}/manifest.in"
+
+msg
+
+# Run ldconfig to make dynamic libraries available to the linker
+if [ "$CFG_OSTYPE" = "unknown-linux-gnu" ]
+    then
+    ldconfig
+    if [ $? -ne 0 ]
+    then
+        warn "failed to run ldconfig."
+        warn "this may happen when not installing as root and may be fine"
+    fi
+fi
+
+# Sanity check: can we run the installed binaries?
+#
+# As with the verification above, make sure the right LD_LIBRARY_PATH-equivalent
+# is in place. Try first without this variable, and if that fails try again with
+# the variable. If the second time tries, print a hopefully helpful message to
+# add something to the appropriate environment variable.
+if [ -z "${CFG_DISABLE_VERIFY}" ]
+then
+    msg "verifying installed binaries are executable"
+    "${CFG_PREFIX}/bin/${TEMPLATE_VERIFY_BIN}" --version 2> /dev/null 1> /dev/null
+    if [ $? -ne 0 ]
+    then
+        export $CFG_LD_PATH_VAR="${CFG_PREFIX}/lib:$CFG_OLD_LD_PATH_VAR"
+        "${CFG_PREFIX}/bin/${TEMPLATE_VERIFY_BIN}" --version > /dev/null
+        if [ $? -ne 0 ]
+        then
+            ERR="can't execute installed binaries. "
+            ERR="${ERR}installation may be broken. "
+            ERR="${ERR}if this is expected then rerun install.sh with \`--disable-verify\` "
+            ERR="${ERR}or \`make install\` with \`--disable-verify-install\`"
+            err "${ERR}"
+        else
+            echo
+            echo "    Note: please ensure '${CFG_PREFIX}/lib' is added to ${CFG_LD_PATH_VAR}"
+        fi
+    fi
+fi
+
+echo
+echo "    ${TEMPLATE_SUCCESS_MESSAGE}"
+echo
+
+
index 7a63742fba3427c9bff6b21fb306502da7cde221..c9d1eb39f0a3de464f6028d192e6ad4737ceb1e0 100644 (file)
@@ -62,9 +62,11 @@ dependencies = [
  "rustc_bitflags 0.0.0",
  "rustc_const_math 0.0.0",
  "rustc_data_structures 0.0.0",
+ "rustc_errors 0.0.0",
  "rustc_llvm 0.0.0",
  "serialize 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -87,8 +89,11 @@ dependencies = [
  "graphviz 0.0.0",
  "log 0.0.0",
  "rustc 0.0.0",
+ "rustc_data_structures 0.0.0",
+ "rustc_errors 0.0.0",
  "rustc_mir 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -102,6 +107,7 @@ dependencies = [
  "rustc_const_math 0.0.0",
  "serialize 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -133,6 +139,7 @@ dependencies = [
  "rustc_back 0.0.0",
  "rustc_borrowck 0.0.0",
  "rustc_const_eval 0.0.0",
+ "rustc_errors 0.0.0",
  "rustc_incremental 0.0.0",
  "rustc_lint 0.0.0",
  "rustc_llvm 0.0.0",
@@ -148,6 +155,16 @@ dependencies = [
  "serialize 0.0.0",
  "syntax 0.0.0",
  "syntax_ext 0.0.0",
+ "syntax_pos 0.0.0",
+]
+
+[[package]]
+name = "rustc_errors"
+version = "0.0.0"
+dependencies = [
+ "log 0.0.0",
+ "serialize 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -161,6 +178,7 @@ dependencies = [
  "rustc_data_structures 0.0.0",
  "serialize 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -172,6 +190,7 @@ dependencies = [
  "rustc_back 0.0.0",
  "rustc_const_eval 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -194,9 +213,12 @@ dependencies = [
  "rustc_back 0.0.0",
  "rustc_bitflags 0.0.0",
  "rustc_const_math 0.0.0",
+ "rustc_data_structures 0.0.0",
+ "rustc_errors 0.0.0",
  "rustc_llvm 0.0.0",
  "serialize 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -212,6 +234,7 @@ dependencies = [
  "rustc_const_math 0.0.0",
  "rustc_data_structures 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -222,7 +245,9 @@ dependencies = [
  "rustc 0.0.0",
  "rustc_const_eval 0.0.0",
  "rustc_const_math 0.0.0",
+ "rustc_errors 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -237,18 +262,19 @@ dependencies = [
  "rustc 0.0.0",
  "rustc_back 0.0.0",
  "rustc_bitflags 0.0.0",
+ "rustc_errors 0.0.0",
  "rustc_metadata 0.0.0",
- "rustc_mir 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
 name = "rustc_privacy"
 version = "0.0.0"
 dependencies = [
- "log 0.0.0",
  "rustc 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -258,7 +284,9 @@ dependencies = [
  "arena 0.0.0",
  "log 0.0.0",
  "rustc 0.0.0",
+ "rustc_errors 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -269,6 +297,7 @@ dependencies = [
  "rustc 0.0.0",
  "serialize 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -284,12 +313,13 @@ dependencies = [
  "rustc_const_eval 0.0.0",
  "rustc_const_math 0.0.0",
  "rustc_data_structures 0.0.0",
+ "rustc_errors 0.0.0",
  "rustc_incremental 0.0.0",
  "rustc_llvm 0.0.0",
- "rustc_mir 0.0.0",
  "rustc_platform_intrinsics 0.0.0",
  "serialize 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -303,8 +333,10 @@ dependencies = [
  "rustc_back 0.0.0",
  "rustc_const_eval 0.0.0",
  "rustc_const_math 0.0.0",
+ "rustc_errors 0.0.0",
  "rustc_platform_intrinsics 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -319,12 +351,14 @@ dependencies = [
  "rustc_back 0.0.0",
  "rustc_const_eval 0.0.0",
  "rustc_driver 0.0.0",
+ "rustc_errors 0.0.0",
  "rustc_lint 0.0.0",
  "rustc_metadata 0.0.0",
  "rustc_resolve 0.0.0",
  "rustc_trans 0.0.0",
  "serialize 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -340,7 +374,9 @@ version = "0.0.0"
 dependencies = [
  "log 0.0.0",
  "rustc_bitflags 0.0.0",
+ "rustc_errors 0.0.0",
  "serialize 0.0.0",
+ "syntax_pos 0.0.0",
 ]
 
 [[package]]
@@ -349,6 +385,15 @@ version = "0.0.0"
 dependencies = [
  "fmt_macros 0.0.0",
  "log 0.0.0",
+ "rustc_errors 0.0.0",
  "syntax 0.0.0",
+ "syntax_pos 0.0.0",
+]
+
+[[package]]
+name = "syntax_pos"
+version = "0.0.0"
+dependencies = [
+ "serialize 0.0.0",
 ]
 
index d3f2907bfc31cbb1e7c076f649794d5859663ff3..1e7b04c814cee2ae3f85d19d6a88f93dbcaf933b 100644 (file)
 #include "rustllvm.h"
 
 #include "llvm/Object/Archive.h"
-
-#if LLVM_VERSION_MINOR >= 7
 #include "llvm/Object/ArchiveWriter.h"
-#endif
 
 using namespace llvm;
 using namespace llvm::object;
@@ -34,13 +31,7 @@ struct LLVMRustArchiveMember {
   ~LLVMRustArchiveMember() {}
 };
 
-#if LLVM_VERSION_MINOR >= 6
 typedef OwningBinary<Archive> RustArchive;
-#define GET_ARCHIVE(a) ((a)->getBinary())
-#else
-typedef Archive RustArchive;
-#define GET_ARCHIVE(a) (a)
-#endif
 
 extern "C" void*
 LLVMRustOpenArchive(char *path) {
@@ -52,7 +43,6 @@ LLVMRustOpenArchive(char *path) {
         return nullptr;
     }
 
-#if LLVM_VERSION_MINOR >= 6
     ErrorOr<std::unique_ptr<Archive>> archive_or =
         Archive::create(buf_or.get()->getMemBufferRef());
 
@@ -63,14 +53,6 @@ LLVMRustOpenArchive(char *path) {
 
     OwningBinary<Archive> *ret = new OwningBinary<Archive>(
             std::move(archive_or.get()), std::move(buf_or.get()));
-#else
-    std::error_code err;
-    Archive *ret = new Archive(std::move(buf_or.get()), err);
-    if (err) {
-        LLVMRustSetLastError(err.message().c_str());
-        return nullptr;
-    }
-#endif
 
     return ret;
 }
@@ -87,7 +69,7 @@ struct RustArchiveIterator {
 
 extern "C" RustArchiveIterator*
 LLVMRustArchiveIteratorNew(RustArchive *ra) {
-    Archive *ar = GET_ARCHIVE(ra);
+    Archive *ar = ra->getBinary();
     RustArchiveIterator *rai = new RustArchiveIterator();
     rai->cur = ar->child_begin();
     rai->end = ar->child_end();
@@ -137,16 +119,12 @@ LLVMRustArchiveChildName(const Archive::Child *child, size_t *size) {
 extern "C" const char*
 LLVMRustArchiveChildData(Archive::Child *child, size_t *size) {
     StringRef buf;
-#if LLVM_VERSION_MINOR >= 7
     ErrorOr<StringRef> buf_or_err = child->getBuffer();
     if (buf_or_err.getError()) {
       LLVMRustSetLastError(buf_or_err.getError().message().c_str());
       return NULL;
     }
     buf = buf_or_err.get();
-#else
-    buf = child->getBuffer();
-#endif
     *size = buf.size();
     return buf.data();
 }
@@ -172,7 +150,6 @@ LLVMRustWriteArchive(char *Dst,
                      const LLVMRustArchiveMember **NewMembers,
                      bool WriteSymbtab,
                      Archive::Kind Kind) {
-#if LLVM_VERSION_MINOR >= 7
   std::vector<NewArchiveIterator> Members;
 
   for (size_t i = 0; i < NumMembers; i++) {
@@ -196,8 +173,5 @@ LLVMRustWriteArchive(char *Dst,
   if (!pair.second)
     return 0;
   LLVMRustSetLastError(pair.second.message().c_str());
-#else
-  LLVMRustSetLastError("writing archives not supported with this LLVM version");
-#endif
   return -1;
 }
index 8b01cac820ed08e07b846f5aafeb37360fb706e7..b26ab44601998d9dd89d81d92eae1ac0b7d70760 100644 (file)
@@ -90,13 +90,8 @@ extern "C" LLVMExecutionEngineRef LLVMBuildExecutionEngine(LLVMModuleRef mod)
     RustJITMemoryManager *mm = new RustJITMemoryManager;
 
     ExecutionEngine *ee =
-    #if LLVM_VERSION_MINOR >= 6
         EngineBuilder(std::unique_ptr<Module>(unwrap(mod)))
             .setMCJITMemoryManager(std::unique_ptr<RustJITMemoryManager>(mm))
-    #else
-        EngineBuilder(unwrap(mod))
-            .setMCJITMemoryManager(mm)
-    #endif
             .setEngineKind(EngineKind::JIT)
             .setErrorStr(&error_str)
             .setTargetOptions(options)
index b3d4e35d7b09c75560a9bba4f2c069dda4be76c7..3564f338a029f6bb9e25c90894a785f27afa4da9 100644 (file)
 #include "llvm/Support/CBindingWrapping.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Host.h"
-#if LLVM_VERSION_MINOR >= 7
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
-#else
-#include "llvm/Target/TargetLibraryInfo.h"
-#endif
 #include "llvm/Target/TargetMachine.h"
 #include "llvm/Target/TargetSubtargetInfo.h"
 #include "llvm/Transforms/IPO/PassManagerBuilder.h"
@@ -49,7 +45,7 @@ LLVMInitializePasses() {
   initializeVectorization(Registry);
   initializeIPO(Registry);
   initializeAnalysis(Registry);
-#if LLVM_VERSION_MINOR <= 7
+#if LLVM_VERSION_MINOR == 7
   initializeIPA(Registry);
 #endif
   initializeTransformUtils(Registry);
@@ -223,17 +219,8 @@ LLVMRustAddAnalysisPasses(LLVMTargetMachineRef TM,
                           LLVMPassManagerRef PMR,
                           LLVMModuleRef M) {
     PassManagerBase *PM = unwrap(PMR);
-#if LLVM_VERSION_MINOR >= 7
     PM->add(createTargetTransformInfoWrapperPass(
           unwrap(TM)->getTargetIRAnalysis()));
-#else
-#if LLVM_VERSION_MINOR == 6
-    PM->add(new DataLayoutPass());
-#else
-    PM->add(new DataLayoutPass(unwrap(M)));
-#endif
-    unwrap(TM)->addAnalysisPasses(*PM);
-#endif
 }
 
 extern "C" void
@@ -242,10 +229,8 @@ LLVMRustConfigurePassManagerBuilder(LLVMPassManagerBuilderRef PMB,
                                     bool MergeFunctions,
                                     bool SLPVectorize,
                                     bool LoopVectorize) {
-#if LLVM_VERSION_MINOR >= 6
     // Ignore mergefunc for now as enabling it causes crashes.
     //unwrap(PMB)->MergeFunctions = MergeFunctions;
-#endif
     unwrap(PMB)->SLPVectorize = SLPVectorize;
     unwrap(PMB)->OptLevel = OptLevel;
     unwrap(PMB)->LoopVectorize = LoopVectorize;
@@ -258,11 +243,7 @@ LLVMRustAddBuilderLibraryInfo(LLVMPassManagerBuilderRef PMB,
                               LLVMModuleRef M,
                               bool DisableSimplifyLibCalls) {
     Triple TargetTriple(unwrap(M)->getTargetTriple());
-#if LLVM_VERSION_MINOR >= 7
     TargetLibraryInfoImpl *TLI = new TargetLibraryInfoImpl(TargetTriple);
-#else
-    TargetLibraryInfo *TLI = new TargetLibraryInfo(TargetTriple);
-#endif
     if (DisableSimplifyLibCalls)
       TLI->disableAllFunctions();
     unwrap(PMB)->LibraryInfo = TLI;
@@ -275,17 +256,10 @@ LLVMRustAddLibraryInfo(LLVMPassManagerRef PMB,
                        LLVMModuleRef M,
                        bool DisableSimplifyLibCalls) {
     Triple TargetTriple(unwrap(M)->getTargetTriple());
-#if LLVM_VERSION_MINOR >= 7
     TargetLibraryInfoImpl TLII(TargetTriple);
     if (DisableSimplifyLibCalls)
       TLII.disableAllFunctions();
     unwrap(PMB)->add(new TargetLibraryInfoWrapperPass(TLII));
-#else
-    TargetLibraryInfo *TLI = new TargetLibraryInfo(TargetTriple);
-    if (DisableSimplifyLibCalls)
-      TLI->disableAllFunctions();
-    unwrap(PMB)->add(TLI);
-#endif
 }
 
 // Unfortunately, the LLVM C API doesn't provide an easy way of iterating over
@@ -323,25 +297,16 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target,
   PassManager *PM = unwrap<PassManager>(PMR);
 
   std::string ErrorInfo;
-#if LLVM_VERSION_MINOR >= 6
   std::error_code EC;
   raw_fd_ostream OS(path, EC, sys::fs::F_None);
   if (EC)
     ErrorInfo = EC.message();
-#else
-  raw_fd_ostream OS(path, ErrorInfo, sys::fs::F_None);
-#endif
   if (ErrorInfo != "") {
     LLVMRustSetLastError(ErrorInfo.c_str());
     return false;
   }
 
-#if LLVM_VERSION_MINOR >= 7
   unwrap(Target)->addPassesToEmitFile(*PM, OS, FileType, false);
-#else
-  formatted_raw_ostream FOS(OS);
-  unwrap(Target)->addPassesToEmitFile(*PM, FOS, FileType, false);
-#endif
   PM->run(*unwrap(M));
 
   // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output
@@ -358,14 +323,10 @@ LLVMRustPrintModule(LLVMPassManagerRef PMR,
   PassManager *PM = unwrap<PassManager>(PMR);
   std::string ErrorInfo;
 
-#if LLVM_VERSION_MINOR >= 6
   std::error_code EC;
   raw_fd_ostream OS(path, EC, sys::fs::F_None);
   if (EC)
     ErrorInfo = EC.message();
-#else
-  raw_fd_ostream OS(path, ErrorInfo, sys::fs::F_None);
-#endif
 
   formatted_raw_ostream FOS(OS);
 
@@ -428,22 +389,10 @@ extern "C" void
 LLVMRustSetDataLayoutFromTargetMachine(LLVMModuleRef Module,
                                        LLVMTargetMachineRef TMR) {
     TargetMachine *Target = unwrap(TMR);
-#if LLVM_VERSION_MINOR >= 7
     unwrap(Module)->setDataLayout(Target->createDataLayout());
-#elif LLVM_VERSION_MINOR >= 6
-    if (const DataLayout *DL = Target->getSubtargetImpl()->getDataLayout())
-        unwrap(Module)->setDataLayout(DL);
-#else
-    if (const DataLayout *DL = Target->getDataLayout())
-        unwrap(Module)->setDataLayout(DL);
-#endif
 }
 
 extern "C" LLVMTargetDataRef
 LLVMRustGetModuleDataLayout(LLVMModuleRef M) {
-#if LLVM_VERSION_MINOR >= 7
     return wrap(&unwrap(M)->getDataLayout());
-#else
-    return wrap(unwrap(M)->getDataLayout());
-#endif
 }
index 697b2d3f539651533a613685e4f0b321a28d3d60..fadd95c9a72459fce25a9810baf5213eb090d4ec 100644 (file)
@@ -243,7 +243,6 @@ extern "C" LLVMValueRef LLVMInlineAsm(LLVMTypeRef Ty,
 
 typedef DIBuilder* DIBuilderRef;
 
-#if LLVM_VERSION_MINOR >= 6
 typedef struct LLVMOpaqueMetadata *LLVMMetadataRef;
 
 namespace llvm {
@@ -253,29 +252,15 @@ inline Metadata **unwrap(LLVMMetadataRef *Vals) {
   return reinterpret_cast<Metadata**>(Vals);
 }
 }
-#else
-typedef LLVMValueRef LLVMMetadataRef;
-#endif
 
 template<typename DIT>
 DIT* unwrapDIptr(LLVMMetadataRef ref) {
     return (DIT*) (ref ? unwrap<MDNode>(ref) : NULL);
 }
 
-#if LLVM_VERSION_MINOR <= 6
-template<typename DIT>
-DIT unwrapDI(LLVMMetadataRef ref) {
-    return DIT(ref ? unwrap<MDNode>(ref) : NULL);
-}
-#else
 #define DIDescriptor DIScope
 #define DIArray DINodeArray
 #define unwrapDI unwrapDIptr
-#endif
-
-#if LLVM_VERSION_MINOR <= 5
-#define DISubroutineType DICompositeType
-#endif
 
 extern "C" uint32_t LLVMRustDebugMetadataVersion() {
     return DEBUG_METADATA_VERSION;
@@ -339,16 +324,10 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateSubroutineType(
     LLVMMetadataRef File,
     LLVMMetadataRef ParameterTypes) {
     return wrap(Builder->createSubroutineType(
-#if LLVM_VERSION_MINOR <= 7
+#if LLVM_VERSION_MINOR == 7
         unwrapDI<DIFile>(File),
 #endif
-#if LLVM_VERSION_MINOR >= 7
         DITypeRefArray(unwrap<MDTuple>(ParameterTypes))));
-#elif LLVM_VERSION_MINOR >= 6
-        unwrapDI<DITypeArray>(ParameterTypes)));
-#else
-        unwrapDI<DIArray>(ParameterTypes)));
-#endif
 }
 
 extern "C" LLVMMetadataRef LLVMDIBuilderCreateFunction(
@@ -435,11 +414,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateStructType(
         AlignInBits,
         Flags,
         unwrapDI<DIType>(DerivedFrom),
-#if LLVM_VERSION_MINOR >= 7
         DINodeArray(unwrapDI<MDTuple>(Elements)),
-#else
-        unwrapDI<DIArray>(Elements),
-#endif
         RunTimeLang,
         unwrapDI<DIType>(VTableHolder),
         UniqueId
@@ -473,9 +448,6 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateLexicalBlock(
     return wrap(Builder->createLexicalBlock(
         unwrapDI<DIDescriptor>(Scope),
         unwrapDI<DIFile>(File), Line, Col
-#if LLVM_VERSION_MINOR == 5
-        , 0
-#endif
         ));
 }
 
@@ -490,11 +462,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateStaticVariable(
     bool isLocalToUnit,
     LLVMValueRef Val,
     LLVMMetadataRef Decl = NULL) {
-#if LLVM_VERSION_MINOR >= 6
     return wrap(Builder->createGlobalVariable(unwrapDI<DIDescriptor>(Context),
-#else
-    return wrap(Builder->createStaticVariable(unwrapDI<DIDescriptor>(Context),
-#endif
         Name,
         LinkageName,
         unwrapDI<DIFile>(File),
@@ -518,25 +486,6 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateVariable(
     int64_t* AddrOps,
     unsigned AddrOpsCount,
     unsigned ArgNo) {
-#if LLVM_VERSION_MINOR == 5
-    if (AddrOpsCount > 0) {
-        SmallVector<llvm::Value *, 16> addr_ops;
-        llvm::Type *Int64Ty = Type::getInt64Ty(unwrap<MDNode>(Scope)->getContext());
-        for (unsigned i = 0; i < AddrOpsCount; ++i)
-            addr_ops.push_back(ConstantInt::get(Int64Ty, AddrOps[i]));
-
-        return wrap(Builder->createComplexVariable(
-            Tag,
-            unwrapDI<DIDescriptor>(Scope),
-            Name,
-            unwrapDI<DIFile>(File),
-            LineNo,
-            unwrapDI<DIType>(Ty),
-            addr_ops,
-            ArgNo
-        ));
-    }
-#endif
 #if LLVM_VERSION_MINOR >= 8
     if (Tag == 0x100) { // DW_TAG_auto_variable
         return wrap(Builder->createAutoVariable(
@@ -568,11 +517,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateArrayType(
     LLVMMetadataRef Subscripts) {
     return wrap(Builder->createArrayType(Size, AlignInBits,
         unwrapDI<DIType>(Ty),
-#if LLVM_VERSION_MINOR >= 7
         DINodeArray(unwrapDI<MDTuple>(Subscripts))
-#else
-        unwrapDI<DIArray>(Subscripts)
-#endif
     ));
 }
 
@@ -584,11 +529,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateVectorType(
     LLVMMetadataRef Subscripts) {
     return wrap(Builder->createVectorType(Size, AlignInBits,
         unwrapDI<DIType>(Ty),
-#if LLVM_VERSION_MINOR >= 7
         DINodeArray(unwrapDI<MDTuple>(Subscripts))
-#else
-        unwrapDI<DIArray>(Subscripts)
-#endif
     ));
 }
 
@@ -603,18 +544,9 @@ extern "C" LLVMMetadataRef LLVMDIBuilderGetOrCreateArray(
     DIBuilderRef Builder,
     LLVMMetadataRef* Ptr,
     unsigned Count) {
-#if LLVM_VERSION_MINOR >= 7
     Metadata **DataValue = unwrap(Ptr);
     return wrap(Builder->getOrCreateArray(
         ArrayRef<Metadata*>(DataValue, Count)).get());
-#else
-    return wrap(Builder->getOrCreateArray(
-#if LLVM_VERSION_MINOR >= 6
-        ArrayRef<Metadata*>(unwrap(Ptr), Count)));
-#else
-        ArrayRef<Value*>(reinterpret_cast<Value**>(Ptr), Count)));
-#endif
-#endif
 }
 
 extern "C" LLVMValueRef LLVMDIBuilderInsertDeclareAtEnd(
@@ -627,18 +559,10 @@ extern "C" LLVMValueRef LLVMDIBuilderInsertDeclareAtEnd(
     LLVMBasicBlockRef InsertAtEnd) {
     return wrap(Builder->insertDeclare(
         unwrap(Val),
-#if LLVM_VERSION_MINOR >= 7
         unwrap<DILocalVariable>(VarInfo),
-#else
-        unwrapDI<DIVariable>(VarInfo),
-#endif
-#if LLVM_VERSION_MINOR >= 6
         Builder->createExpression(
           llvm::ArrayRef<int64_t>(AddrOps, AddrOpsCount)),
-#endif
-#if LLVM_VERSION_MINOR >= 7
         DebugLoc(cast<MDNode>(unwrap<MetadataAsValue>(DL)->getMetadata())),
-#endif
         unwrap(InsertAtEnd)));
 }
 
@@ -650,22 +574,12 @@ extern "C" LLVMValueRef LLVMDIBuilderInsertDeclareBefore(
     unsigned AddrOpsCount,
     LLVMValueRef DL,
     LLVMValueRef InsertBefore) {
-#if LLVM_VERSION_MINOR >= 6
-#endif
     return wrap(Builder->insertDeclare(
         unwrap(Val),
-#if LLVM_VERSION_MINOR >= 7
         unwrap<DILocalVariable>(VarInfo),
-#else
-        unwrapDI<DIVariable>(VarInfo),
-#endif
-#if LLVM_VERSION_MINOR >= 6
         Builder->createExpression(
           llvm::ArrayRef<int64_t>(AddrOps, AddrOpsCount)),
-#endif
-#if LLVM_VERSION_MINOR >= 7
         DebugLoc(cast<MDNode>(unwrap<MetadataAsValue>(DL)->getMetadata())),
-#endif
         unwrap<Instruction>(InsertBefore)));
 }
 
@@ -695,11 +609,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateEnumerationType(
         LineNumber,
         SizeInBits,
         AlignInBits,
-#if LLVM_VERSION_MINOR >= 7
         DINodeArray(unwrapDI<MDTuple>(Elements)),
-#else
-        unwrapDI<DIArray>(Elements),
-#endif
         unwrapDI<DIType>(ClassType)));
 }
 
@@ -724,11 +634,7 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateUnionType(
         SizeInBits,
         AlignInBits,
         Flags,
-#if LLVM_VERSION_MINOR >= 7
         DINodeArray(unwrapDI<MDTuple>(Elements)),
-#else
-        unwrapDI<DIArray>(Elements),
-#endif
         RunTimeLang,
         UniqueId
         ));
@@ -747,12 +653,6 @@ extern "C" LLVMMetadataRef LLVMDIBuilderCreateTemplateTypeParameter(
       unwrapDI<DIDescriptor>(Scope),
       Name,
       unwrapDI<DIType>(Ty)
-#if LLVM_VERSION_MINOR <= 6
-      ,
-      unwrapDI<MDNode*>(File),
-      LineNo,
-      ColumnNo
-#endif
       ));
 }
 
@@ -785,15 +685,8 @@ extern "C" void LLVMDICompositeTypeSetTypeArray(
     LLVMMetadataRef CompositeType,
     LLVMMetadataRef TypeArray)
 {
-#if LLVM_VERSION_MINOR >= 7
     DICompositeType *tmp = unwrapDI<DICompositeType>(CompositeType);
     Builder->replaceArrays(tmp, DINodeArray(unwrap<MDTuple>(TypeArray)));
-#elif LLVM_VERSION_MINOR >= 6
-    DICompositeType tmp = unwrapDI<DICompositeType>(CompositeType);
-    Builder->replaceArrays(tmp, unwrapDI<DIArray>(TypeArray));
-#else
-    unwrapDI<DICompositeType>(CompositeType).setTypeArray(unwrapDI<DIArray>(TypeArray));
-#endif
 }
 
 extern "C" LLVMValueRef LLVMDIBuilderCreateDebugLocation(
@@ -810,15 +703,7 @@ extern "C" LLVMValueRef LLVMDIBuilderCreateDebugLocation(
                                        unwrapDIptr<MDNode>(Scope),
                                        unwrapDIptr<MDNode>(InlinedAt));
 
-#if LLVM_VERSION_MINOR >= 6
-    return wrap(MetadataAsValue::get(context, debug_loc.getAsMDNode(
-#if LLVM_VERSION_MINOR <= 6
-            context
-#endif
-        )));
-#else
-    return wrap(debug_loc.getAsMDNode(context));
-#endif
+    return wrap(MetadataAsValue::get(context, debug_loc.getAsMDNode()));
 }
 
 extern "C" void LLVMWriteTypeToString(LLVMTypeRef Type, RustStringRef str) {
@@ -838,40 +723,22 @@ extern "C" void LLVMWriteValueToString(LLVMValueRef Value, RustStringRef str) {
 extern "C" bool
 LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) {
     Module *Dst = unwrap(dst);
-#if LLVM_VERSION_MINOR >= 6
     std::unique_ptr<MemoryBuffer> buf = MemoryBuffer::getMemBufferCopy(StringRef(bc, len));
-#if LLVM_VERSION_MINOR >= 7
     ErrorOr<std::unique_ptr<Module>> Src =
         llvm::getLazyBitcodeModule(std::move(buf), Dst->getContext());
-#else
-    ErrorOr<Module *> Src = llvm::getLazyBitcodeModule(std::move(buf), Dst->getContext());
-#endif
-#else
-    MemoryBuffer* buf = MemoryBuffer::getMemBufferCopy(StringRef(bc, len));
-    ErrorOr<Module *> Src = llvm::getLazyBitcodeModule(buf, Dst->getContext());
-#endif
     if (!Src) {
         LLVMRustSetLastError(Src.getError().message().c_str());
-#if LLVM_VERSION_MINOR == 5
-        delete buf;
-#endif
         return false;
     }
 
     std::string Err;
 
-#if LLVM_VERSION_MINOR >= 6
     raw_string_ostream Stream(Err);
     DiagnosticPrinterRawOStream DP(Stream);
 #if LLVM_VERSION_MINOR >= 8
     if (Linker::linkModules(*Dst, std::move(Src.get()))) {
-#elif LLVM_VERSION_MINOR >= 7
-    if (Linker::LinkModules(Dst, Src->get(), [&](const DiagnosticInfo &DI) { DI.print(DP); })) {
-#else
-    if (Linker::LinkModules(Dst, *Src, [&](const DiagnosticInfo &DI) { DI.print(DP); })) {
-#endif
 #else
-    if (Linker::LinkModules(Dst, *Src, Linker::DestroySource, &Err)) {
+    if (Linker::LinkModules(Dst, Src->get(), [&](const DiagnosticInfo &DI) { DI.print(DP); })) {
 #endif
         LLVMRustSetLastError(Err.c_str());
         return false;
@@ -975,11 +842,7 @@ extern "C" void LLVMWriteDebugLocToString(
     RustStringRef str)
 {
     raw_rust_string_ostream os(str);
-#if LLVM_VERSION_MINOR >= 7
     unwrap(dl)->print(os);
-#else
-    unwrap(dl)->print(*unwrap(C), os);
-#endif
 }
 
 DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
index 3c84cab2a1c5c0571f27f43721b2952e5414230d..9f1b3316da1d36ec225d8296a6113e0ae39cf321 100644 (file)
@@ -12,6 +12,6 @@
 # tarball for a stable release you'll likely see `1.x.0-$date` where `1.x.0` was
 # released on `$date`
 
-rustc: 1.9.0-2016-05-24
-rustc_key: d16b8f0e
-cargo: nightly-2016-04-10
+rustc: 1.10.0-2016-07-05
+rustc_key: e8edd0fd
+cargo: nightly-2016-05-22
index 9f29a90bffbf645932bfa08edf883b2b1f135a27..195125793be8a7430bc08f437a96009b8bbed035 100644 (file)
@@ -56,5 +56,3 @@ fn main()
     //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0]<bool>
     let _: (char, bool) = Trait::without_default_impl_generic(false);
 }
-
-//~ TRANS_ITEM drop-glue i8
index 5ec1f7fbc3ca385addeb66aa98c9797c3beab8f9..afe6ffc8bfe038527c5594e652428bd0635ae8b2 100644 (file)
@@ -60,5 +60,3 @@ fn main() {
     //~ TRANS_ITEM fn generic_functions::foo3[0]<char, (), ()>
     let _ = foo3('v', (), ());
 }
-
-//~ TRANS_ITEM drop-glue i8
index a27515fd39b701dd99b8328f320c0a858bfdf26c..14316a557328b69d1dd264b0fb33932922e43764 100644 (file)
@@ -77,5 +77,3 @@ fn main() {
     //~ TRANS_ITEM fn generic_impl::id[0]<generic_impl::Struct[0]<&str>>
     let _ = (Struct::new(Struct::new("str")).f)(Struct::new("str"));
 }
-
-//~ TRANS_ITEM drop-glue i8
index b77252512200103aad028b238058dfe6c4934eec..06e547f0dd03715cd8ae9cec8afc69fd3d639545 100644 (file)
@@ -40,3 +40,5 @@ fn main() {
     //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0]<u64>
     let _ = &s1 as &Trait;
 }
+
+//~ TRANS_ITEM drop-glue i8
index 45ba441bc8ba6066bef2f85c6eed8fbd37de471c..5c67ab7a826464e2e81846cfb1a97e8237aa99a4 100644 (file)
@@ -78,3 +78,5 @@ fn main()
     //~ TRANS_ITEM fn unsizing::{{impl}}[3]::foo[0]
     let _wrapper_sized = wrapper_sized as Wrapper<Trait>;
 }
+
+//~ TRANS_ITEM drop-glue i8
index e38e676b95c617b9d4d18dd11346a9d70d91e324..f5641f1f2ed7376210ca0a73e2bc34f11c3a840b 100644 (file)
@@ -56,5 +56,3 @@ mod mod2 {
         let _ = generic("abc");
     }
 }
-
-//~ TRANS_ITEM drop-glue i8
diff --git a/src/test/codegen/issue-32031.rs b/src/test/codegen/issue-32031.rs
new file mode 100644 (file)
index 0000000..5d3ccbf
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C no-prepopulate-passes
+
+#![crate_type = "lib"]
+
+#[no_mangle]
+pub struct F32(f32);
+
+// CHECK: define float @add_newtype_f32(float, float)
+#[inline(never)]
+#[no_mangle]
+pub fn add_newtype_f32(a: F32, b: F32) -> F32 {
+    F32(a.0 + b.0)
+}
+
+#[no_mangle]
+pub struct F64(f64);
+
+// CHECK: define double @add_newtype_f64(double, double)
+#[inline(never)]
+#[no_mangle]
+pub fn add_newtype_f64(a: F64, b: F64) -> F64 {
+    F64(a.0 + b.0)
+}
diff --git a/src/test/codegen/issue-32364.rs b/src/test/codegen/issue-32364.rs
new file mode 100644 (file)
index 0000000..926987b
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C no-prepopulate-passes
+
+struct Foo;
+
+impl Foo {
+// CHECK: define internal x86_stdcallcc void @{{.*}}foo{{.*}}()
+    #[inline(never)]
+    pub extern "stdcall" fn foo<T>() {
+    }
+}
+
+fn main() {
+    Foo::foo::<Foo>();
+}
index 21f23b6ea18612dee9c0ae2708964553a47f3639..a65a3e1bb66fe52a446bb25cf4fda9a497ca612f 100644 (file)
@@ -11,6 +11,7 @@
 // compile-flags: -C no-prepopulate-passes
 
 #![crate_type = "lib"]
+#![feature(rustc_attrs)]
 
 pub struct Bytes {
   a: u8,
@@ -21,6 +22,7 @@ pub struct Bytes {
 
 // CHECK-LABEL: @borrow
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn borrow(x: &i32) -> &i32 {
 // CHECK: load {{(i32\*, )?}}i32** %x{{.*}}, !nonnull
     x
@@ -28,6 +30,7 @@ pub fn borrow(x: &i32) -> &i32 {
 
 // CHECK-LABEL: @_box
 #[no_mangle]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 pub fn _box(x: Box<i32>) -> i32 {
 // CHECK: load {{(i32\*, )?}}i32** %x{{.*}}, !nonnull
     *x
index 0a600f4acad1fc6855e0714eedfaed09f081235b..199f7f0201877b95ac0b71a3bc12ed81af01e1a9 100644 (file)
@@ -13,7 +13,7 @@
 // compile-flags: -C no-prepopulate-passes
 
 #![crate_type = "lib"]
-#![feature(naked_functions)]
+#![feature(naked_functions, rustc_attrs)]
 
 // CHECK: Function Attrs: naked uwtable
 // CHECK-NEXT: define internal void @naked_empty()
@@ -26,6 +26,7 @@ fn naked_empty() {
 // CHECK: Function Attrs: naked uwtable
 #[no_mangle]
 #[naked]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 // CHECK-NEXT: define internal void @naked_with_args(i{{[0-9]+}})
 fn naked_with_args(a: isize) {
     // CHECK: %a = alloca i{{[0-9]+}}
@@ -45,6 +46,7 @@ fn naked_with_return() -> isize {
 // CHECK-NEXT: define internal i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}})
 #[no_mangle]
 #[naked]
+#[rustc_no_mir] // FIXME #27840 MIR has different codegen.
 fn naked_with_args_and_return(a: isize) -> isize {
     // CHECK: %a = alloca i{{[0-9]+}}
     // CHECK: ret i{{[0-9]+}} %{{[0-9]+}}
diff --git a/src/test/codegen/zip.rs b/src/test/codegen/zip.rs
new file mode 100644 (file)
index 0000000..6c95636
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C no-prepopulate-passes -O
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: @zip_copy
+#[no_mangle]
+pub fn zip_copy(xs: &[u8], ys: &mut [u8]) {
+// CHECK: memcpy
+    for (x, y) in xs.iter().zip(ys) {
+        *y = *x;
+    }
+}
index 3516f566e8a1ffda8f157786da1479d6def7eb47..a6bc9db199c8be156ec2c2f49ce470b93a720e15 100644 (file)
 #![feature(plugin_registrar, quote, rustc_private)]
 
 extern crate syntax;
+extern crate syntax_pos;
 extern crate rustc;
 extern crate rustc_plugin;
 
-use syntax::ast::{self, TokenTree, Item, MetaItem, ImplItem, TraitItem, ItemKind};
-use syntax::codemap::Span;
+use syntax::ast::{self, Item, MetaItem, ImplItem, TraitItem, ItemKind};
 use syntax::ext::base::*;
 use syntax::parse::{self, token};
 use syntax::ptr::P;
+use syntax::tokenstream::TokenTree;
+use syntax_pos::Span;
 use rustc_plugin::Registry;
 
 #[macro_export]
index cd801fbcd889be760e192fef18b71cf809029299..dade0e946c5bf92587d042bf18e1679a523e46b5 100644 (file)
@@ -22,8 +22,8 @@
 extern crate syntax;
 
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::parse;
+use syntax_pos::Span;
 
 struct ParseSess;
 
index 5f7752bb203c2574da6986722d4ce348665c272e..4245786295b343fa14df160e11f5d447a65483be 100644 (file)
@@ -15,6 +15,7 @@ pub use use_from_trait_xc::Trait;
 
 fn main() {
     match () {
-        Trait { x: 42 } => () //~ ERROR `Trait` does not name a struct
+        Trait { x: 42 } => () //~ ERROR expected variant, struct or type alias, found trait `Trait`
+        //~^ ERROR `Trait` does not name a struct or a struct variant
     }
 }
index 3e153a21e5d38c8f0dfe86adcdb706a4420a7ea7..e29ded8a052c6f73b16462e0a89b0ddd26e7f714 100644 (file)
 #![feature(quote, rustc_private)]
 
 extern crate syntax;
+extern crate syntax_pos;
 
 use syntax::ast;
-use syntax::codemap::{self, DUMMY_SP};
 use syntax::parse;
 use syntax::print::pprust;
+use syntax_pos::DUMMY_SP;
 
 fn main() {
     let ps = syntax::parse::ParseSess::new();
+    let mut loader = syntax::ext::base::DummyMacroLoader;
     let mut cx = syntax::ext::base::ExtCtxt::new(
         &ps, vec![],
         syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
-        &mut Vec::new());
+        &mut loader);
     cx.bt_push(syntax::codemap::ExpnInfo {
         call_site: DUMMY_SP,
         callee: syntax::codemap::NameAndSpan {
diff --git a/src/test/compile-fail/E0024.rs b/src/test/compile-fail/E0024.rs
deleted file mode 100644 (file)
index 18f4dcf..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-enum Number {
-    Zero,
-    One(u32)
-}
-
-fn main() {
-    let x = Number::Zero;
-    match x {
-        Number::Zero(inside) => {}, //~ ERROR E0024
-        Number::One(inside) => {},
-    }
-}
index cb78f4b3bb59765ee184c5ee6727adfbac696448..f86d7ec114b938132fd532a8bc61a08b538b7872 100644 (file)
@@ -19,5 +19,4 @@ fn main() {
     let foo = Foo;
     let ref_foo = &&Foo;
     ref_foo.foo(); //~ ERROR E0055
-                   //~^ ERROR E0275
 }
diff --git a/src/test/compile-fail/E0062.rs b/src/test/compile-fail/E0062.rs
new file mode 100644 (file)
index 0000000..86ec7db
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo {
+    x: i32
+}
+
+fn main() {
+    let x = Foo {
+        x: 0,
+        x: 0, //~ ERROR E0062
+    };
+}
diff --git a/src/test/compile-fail/E0063.rs b/src/test/compile-fail/E0063.rs
new file mode 100644 (file)
index 0000000..c94f807
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo {
+    x: i32,
+    y: i32
+}
+
+fn main() {
+    let x = Foo { x: 0 }; //~ ERROR E0063
+}
diff --git a/src/test/compile-fail/E0067.rs b/src/test/compile-fail/E0067.rs
new file mode 100644 (file)
index 0000000..a3fc30e
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::LinkedList;
+
+fn main() {
+    LinkedList::new() += 1; //~ ERROR E0368
+                            //~^ ERROR E0067
+}
diff --git a/src/test/compile-fail/E0069.rs b/src/test/compile-fail/E0069.rs
new file mode 100644 (file)
index 0000000..d164d86
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn foo() -> u8 {
+    return; //~ ERROR E0069
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0070.rs b/src/test/compile-fail/E0070.rs
new file mode 100644 (file)
index 0000000..ba66bd0
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+const SOME_CONST : i32 = 12;
+
+fn some_other_func() {}
+
+fn some_function() {
+    SOME_CONST = 14; //~ ERROR E0070
+    1 = 3; //~ ERROR E0070
+    some_other_func() = 4; //~ ERROR E0070
+                           //~^ ERROR E0308
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0071.rs b/src/test/compile-fail/E0071.rs
new file mode 100644 (file)
index 0000000..658c8fb
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum Foo { FirstValue(i32) }
+
+fn main() {
+    let u = Foo::FirstValue { value: 0 }; //~ ERROR E0071
+    let t = u32 { value: 4 }; //~ ERROR E0071
+}
diff --git a/src/test/compile-fail/E0072.rs b/src/test/compile-fail/E0072.rs
new file mode 100644 (file)
index 0000000..2f96ba1
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct ListNode { //~ ERROR E0072
+    head: u8,
+    tail: Option<ListNode>,
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0075.rs b/src/test/compile-fail/E0075.rs
new file mode 100644 (file)
index 0000000..d778390
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(repr_simd)]
+
+#[repr(simd)]
+struct Bad; //~ ERROR E0075
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0076.rs b/src/test/compile-fail/E0076.rs
new file mode 100644 (file)
index 0000000..b0f02a0
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(repr_simd)]
+
+#[repr(simd)]
+struct Bad(u16, u32, u32); //~ ERROR E0076
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0077.rs b/src/test/compile-fail/E0077.rs
new file mode 100644 (file)
index 0000000..b074e90
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(repr_simd)]
+
+#[repr(simd)]
+struct Bad(String); //~ ERROR E0077
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0079.rs b/src/test/compile-fail/E0079.rs
new file mode 100644 (file)
index 0000000..23957c7
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum Foo {
+    Q = "32" //~ ERROR E0079
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0080.rs b/src/test/compile-fail/E0080.rs
new file mode 100644 (file)
index 0000000..0329209
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum Enum {
+    X = (1 << 500), //~ ERROR E0080
+    Y = (1 / 0) //~ ERROR E0080
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0081.rs b/src/test/compile-fail/E0081.rs
new file mode 100644 (file)
index 0000000..b632655
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum Enum {
+    P = 3,
+    X = 3, //~ ERROR E0081
+    Y = 5
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0084.rs b/src/test/compile-fail/E0084.rs
new file mode 100644 (file)
index 0000000..c579101
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[repr(i32)]
+enum Foo {} //~ ERROR E0084
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0087.rs b/src/test/compile-fail/E0087.rs
new file mode 100644 (file)
index 0000000..ec559fc
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn foo<T>() {}
+
+fn main() {
+    foo::<f64, bool>(); //~ ERROR E0087
+}
diff --git a/src/test/compile-fail/E0088.rs b/src/test/compile-fail/E0088.rs
new file mode 100644 (file)
index 0000000..0b235aa
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn f() {}
+
+fn main() {
+    f::<'static>(); //~ ERROR E0088
+}
diff --git a/src/test/compile-fail/E0089.rs b/src/test/compile-fail/E0089.rs
new file mode 100644 (file)
index 0000000..3b52f76
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn foo<T, U>() {}
+
+fn main() {
+    foo::<f64>(); //~ ERROR E0089
+}
diff --git a/src/test/compile-fail/E0091.rs b/src/test/compile-fail/E0091.rs
new file mode 100644 (file)
index 0000000..da988db
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+type Foo<T> = u32; //~ ERROR E0091
+type Foo2<A, B> = Box<A>; //~ ERROR E0091
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0092.rs b/src/test/compile-fail/E0092.rs
new file mode 100644 (file)
index 0000000..b08164a
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(intrinsics)]
+extern "rust-intrinsic" {
+    fn atomic_foo(); //~ ERROR E0092
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0093.rs b/src/test/compile-fail/E0093.rs
new file mode 100644 (file)
index 0000000..9b23f6d
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(intrinsics)]
+extern "rust-intrinsic" {
+    fn foo(); //~ ERROR E0093
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0094.rs b/src/test/compile-fail/E0094.rs
new file mode 100644 (file)
index 0000000..3a31874
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(intrinsics)]
+extern "rust-intrinsic" {
+    fn size_of<T, U>() -> usize; //~ ERROR E0094
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0101.rs b/src/test/compile-fail/E0101.rs
new file mode 100644 (file)
index 0000000..7651626
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let x = |_| {}; //~ ERROR E0101
+}
diff --git a/src/test/compile-fail/E0102.rs b/src/test/compile-fail/E0102.rs
new file mode 100644 (file)
index 0000000..c4ddbab
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let x = []; //~ ERROR E0102
+}
diff --git a/src/test/compile-fail/E0106.rs b/src/test/compile-fail/E0106.rs
new file mode 100644 (file)
index 0000000..f1cd530
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo {
+    x: &bool, //~ ERROR E0106
+}
+enum Bar {
+    A(u8),
+    B(&bool), //~ ERROR E0106
+}
+type MyStr = &str; //~ ERROR E0106
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0107.rs b/src/test/compile-fail/E0107.rs
new file mode 100644 (file)
index 0000000..d27b708
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo<'a>(&'a str);
+
+enum Bar {
+    A,
+    B,
+    C,
+}
+
+struct Baz<'a> {
+    foo: Foo, //~ ERROR E0107
+    bar: Bar<'a>, //~ ERROR E0107
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0109.rs b/src/test/compile-fail/E0109.rs
new file mode 100644 (file)
index 0000000..9fc4784
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+type X = u32<i32>; //~ ERROR E0109
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0110.rs b/src/test/compile-fail/E0110.rs
new file mode 100644 (file)
index 0000000..fd169f4
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+type X = u32<'static>; //~ ERROR E0110
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0116.rs b/src/test/compile-fail/E0116.rs
new file mode 100644 (file)
index 0000000..4020aa9
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+impl Vec<u8> {} //~ ERROR E0116
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0117.rs b/src/test/compile-fail/E0117.rs
new file mode 100644 (file)
index 0000000..16d713b
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+impl Drop for u32 {} //~ ERROR E0117
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0118.rs b/src/test/compile-fail/E0118.rs
new file mode 100644 (file)
index 0000000..d37ff34
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+impl (u8, u8) { //~ ERROR E0118
+    fn get_state(&self) -> String {
+        String::new()
+    }
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0119.rs b/src/test/compile-fail/E0119.rs
new file mode 100644 (file)
index 0000000..9528631
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait MyTrait {
+    fn get(&self) -> usize;
+}
+
+impl<T> MyTrait for T {
+    fn get(&self) -> usize { 0 }
+}
+
+struct Foo {
+    value: usize
+}
+
+impl MyTrait for Foo { //~ ERROR E0119
+    fn get(&self) -> usize { self.value }
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0120.rs b/src/test/compile-fail/E0120.rs
new file mode 100644 (file)
index 0000000..de08427
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait MyTrait {}
+
+impl Drop for MyTrait { //~ ERROR E0120
+    fn drop(&mut self) {}
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0121.rs b/src/test/compile-fail/E0121.rs
new file mode 100644 (file)
index 0000000..b26b5f4
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn foo() -> _ { 5 } //~ ERROR E0121
+
+static BAR: _ = "test"; //~ ERROR E0121
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0124.rs b/src/test/compile-fail/E0124.rs
new file mode 100644 (file)
index 0000000..414b19e
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo {
+    field1: i32,
+    field1: i32, //~ ERROR E0124
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0128.rs b/src/test/compile-fail/E0128.rs
new file mode 100644 (file)
index 0000000..3707101
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo<T=U, U=()> { //~ ERROR E0128
+    field1: T,
+    field2: U,
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0130.rs b/src/test/compile-fail/E0130.rs
new file mode 100644 (file)
index 0000000..ef5961e
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern {
+    fn foo((a, b): (u32, u32)); //~ ERROR E0130
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0131.rs b/src/test/compile-fail/E0131.rs
new file mode 100644 (file)
index 0000000..aa11577
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main<T>() { //~ ERROR E0131
+}
diff --git a/src/test/compile-fail/E0132.rs b/src/test/compile-fail/E0132.rs
new file mode 100644 (file)
index 0000000..ff19a57
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(start)]
+
+#[start]
+fn f<T>() {} //~ ERROR E0132
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0133.rs b/src/test/compile-fail/E0133.rs
new file mode 100644 (file)
index 0000000..630ee85
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+unsafe fn f() { return; }
+
+fn main() {
+    f(); //~ ERROR E0133
+}
diff --git a/src/test/compile-fail/E0137.rs b/src/test/compile-fail/E0137.rs
new file mode 100644 (file)
index 0000000..695ce79
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(main)]
+
+#[main]
+fn foo() {}
+
+#[main]
+fn f() {} //~ ERROR E0137
diff --git a/src/test/compile-fail/E0138.rs b/src/test/compile-fail/E0138.rs
new file mode 100644 (file)
index 0000000..97d85e5
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(start)]
+
+#[start]
+fn foo(argc: isize, argv: *const *const u8) -> isize {}
+
+#[start]
+fn f(argc: isize, argv: *const *const u8) -> isize {} //~ ERROR E0138
diff --git a/src/test/compile-fail/E0152.rs b/src/test/compile-fail/E0152.rs
new file mode 100644 (file)
index 0000000..ae501b9
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(lang_items)]
+
+#[lang = "panic_fmt"]
+struct Foo; //~ ERROR E0152
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0161.rs b/src/test/compile-fail/E0161.rs
new file mode 100644 (file)
index 0000000..81adf90
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(box_syntax)]
+
+fn main() {
+    let _x: Box<str> = box *"hello"; //~ ERROR E0161
+                                     //~^ ERROR E0507
+}
diff --git a/src/test/compile-fail/E0162.rs b/src/test/compile-fail/E0162.rs
new file mode 100644 (file)
index 0000000..e13b0af
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Irrefutable(i32);
+
+fn main() {
+    let irr = Irrefutable(0);
+    if let Irrefutable(x) = irr { //~ ERROR E0162
+        println!("{}", x);
+    }
+}
diff --git a/src/test/compile-fail/E0163.rs b/src/test/compile-fail/E0163.rs
new file mode 100644 (file)
index 0000000..5cb6f4d
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum Foo { B(u32) }
+
+fn bar(foo: Foo) -> u32 {
+    match foo {
+        Foo::B { i } => i, //~ ERROR E0163
+    }
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0164.rs b/src/test/compile-fail/E0164.rs
new file mode 100644 (file)
index 0000000..491b2e9
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum Foo { B { i: u32 } }
+
+fn bar(foo: Foo) -> u32 {
+    match foo {
+        Foo::B(i) => i, //~ ERROR E0164
+    }
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0165.rs b/src/test/compile-fail/E0165.rs
new file mode 100644 (file)
index 0000000..cca714b
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Irrefutable(i32);
+
+fn main() {
+    let irr = Irrefutable(0);
+    while let Irrefutable(x) = irr { //~ ERROR E0165
+        // ...
+    }
+}
diff --git a/src/test/compile-fail/E0166.rs b/src/test/compile-fail/E0166.rs
new file mode 100644 (file)
index 0000000..9fa4124
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn foo() -> ! { return; } //~ ERROR E0166
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0172.rs b/src/test/compile-fail/E0172.rs
new file mode 100644 (file)
index 0000000..7011bf0
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn foo(bar: i32+std::fmt::Display) {} //~ ERROR E0172
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0178.rs b/src/test/compile-fail/E0178.rs
new file mode 100644 (file)
index 0000000..f34f383
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Foo {}
+
+struct Bar<'a> {
+    w: &'a Foo + Copy, //~ ERROR E0178
+    x: &'a Foo + 'a, //~ ERROR E0178
+    y: &'a mut Foo + 'a, //~ ERROR E0178
+    z: fn() -> Foo + 'a, //~ ERROR E0178
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0184.rs b/src/test/compile-fail/E0184.rs
new file mode 100644 (file)
index 0000000..5d72d00
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[derive(Copy)] //~ ERROR E0184
+struct Foo;
+
+impl Drop for Foo {
+    fn drop(&mut self) {
+    }
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0185.rs b/src/test/compile-fail/E0185.rs
new file mode 100644 (file)
index 0000000..0e33687
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Foo {
+    fn foo();
+}
+
+struct Bar;
+
+impl Foo for Bar {
+    fn foo(&self) {} //~ ERROR E0185
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0186.rs b/src/test/compile-fail/E0186.rs
new file mode 100644 (file)
index 0000000..aa0a38b
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Foo {
+    fn foo(&self);
+}
+
+struct Bar;
+
+impl Foo for Bar {
+    fn foo() {} //~ ERROR E0186
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0191.rs b/src/test/compile-fail/E0191.rs
new file mode 100644 (file)
index 0000000..489ebb0
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Trait {
+    type Bar;
+}
+
+type Foo = Trait; //~ ERROR E0191
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0192.rs b/src/test/compile-fail/E0192.rs
new file mode 100644 (file)
index 0000000..92f5876
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(optin_builtin_traits)]
+
+trait Trait {
+    type Bar;
+}
+
+struct Foo;
+
+impl !Trait for Foo { } //~ ERROR E0192
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0194.rs b/src/test/compile-fail/E0194.rs
new file mode 100644 (file)
index 0000000..96b3062
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Foo<T> {
+    fn do_something(&self) -> T;
+    fn do_something_else<T: Clone>(&self, bar: T); //~ ERROR E0194
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0195.rs b/src/test/compile-fail/E0195.rs
new file mode 100644 (file)
index 0000000..0630dfe
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Trait {
+    fn bar<'a,'b:'a>(x: &'a str, y: &'b str);
+}
+
+struct Foo;
+
+impl Trait for Foo {
+    fn bar<'a,'b>(x: &'a str, y: &'b str) { //~ ERROR E0195
+    }
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0197.rs b/src/test/compile-fail/E0197.rs
new file mode 100644 (file)
index 0000000..f25fa9b
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo;
+
+unsafe impl Foo { } //~ ERROR E0197
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0199.rs b/src/test/compile-fail/E0199.rs
new file mode 100644 (file)
index 0000000..8bd3ffd
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(optin_builtin_traits)]
+
+struct Foo;
+
+unsafe impl !Clone for Foo { } //~ ERROR E0199
+
+fn main() {
+}
diff --git a/src/test/compile-fail/E0200.rs b/src/test/compile-fail/E0200.rs
new file mode 100644 (file)
index 0000000..6bfea0e
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo;
+
+unsafe trait Bar { }
+
+impl Bar for Foo { } //~ ERROR E0200
+
+fn main() {
+}
index 1134dbfd1c46c9e74bf5c6e70eadbc5d5ce1b3b2..e65230389f9c0412297a2d5fa95c25934e1ada3b 100644 (file)
@@ -10,7 +10,7 @@
 
 const A: &'static [i32] = &[];
 const B: i32 = (&A)[1];
-//~^ ERROR: array index out of bounds
+//~^ ERROR index out of bounds: the len is 0 but the index is 1
 
 fn main() {
     let _ = B;
index e59895cda442ae1adab9a2a493d2ac7efdf88b28..69d84e24c4982db2c7d5549461b425eb2f74a4b3 100644 (file)
@@ -9,7 +9,8 @@
 // except according to those terms.
 
 const A: [i32; 0] = [];
-const B: i32 = A[1]; //~ ERROR: array index out of bounds
+const B: i32 = A[1];
+//~^ ERROR index out of bounds: the len is 0 but the index is 1
 
 fn main() {
     let _ = B;
index 43a0ad6b5f6bf71ea08efb88f8439b31972a0d64..1020a5ba8a42e4a4f6862ea5ccae50b0f1724bea 100644 (file)
@@ -9,6 +9,8 @@
 // except according to those terms.
 
 // ignore-android
+// ignore-arm
+// ignore-aarch64
 
 #![feature(asm, rustc_attrs)]
 
index be949db0281d28ebd70ef01c944450c89cfff47b..6ebe80b5701b9dbcb090da8e330833df9f6cd88f 100644 (file)
@@ -23,5 +23,5 @@ mod bar1 {
 
 fn main() {
     assert_eq!(1, bar1::Foo::ID);
-    //~^ERROR associated const `ID` is private
+    //~^ERROR associated constant `ID` is private
 }
diff --git a/src/test/compile-fail/associated-types/cache/chrono-scan.rs b/src/test/compile-fail/associated-types/cache/chrono-scan.rs
new file mode 100644 (file)
index 0000000..a753527
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+#![allow(warnings)]
+
+pub type ParseResult<T> = Result<T, ()>;
+
+pub enum Item<'a> {     Literal(&'a str),
+ }
+
+pub fn colon_or_space(s: &str) -> ParseResult<&str> {
+    unimplemented!()
+}
+
+pub fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
+        where F: FnMut(&str) -> ParseResult<&str> {
+    unimplemented!()
+}
+
+pub fn parse<'a, I>(mut s: &str, items: I) -> ParseResult<()>
+        where I: Iterator<Item=Item<'a>> {
+    macro_rules! try_consume {
+        ($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
+    }
+    let offset = try_consume!(timezone_offset_zulu(s.trim_left(), colon_or_space));
+    let offset = try_consume!(timezone_offset_zulu(s.trim_left(), colon_or_space));
+    Ok(())
+}
+
+#[rustc_error]
+fn main() { } //~ ERROR compilation successful
diff --git a/src/test/compile-fail/associated-types/cache/elision.rs b/src/test/compile-fail/associated-types/cache/elision.rs
new file mode 100644 (file)
index 0000000..d111732
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+#![allow(warnings)]
+
+// Check that you are allowed to implement using elision but write
+// trait without elision (a bug in this cropped up during
+// bootstrapping, so this is a regression test).
+
+pub struct SplitWhitespace<'a> {
+    x: &'a u8
+}
+
+pub trait UnicodeStr {
+    fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>;
+}
+
+impl UnicodeStr for str {
+    #[inline]
+    fn split_whitespace(&self) -> SplitWhitespace {
+        unimplemented!()
+    }
+}
+
+#[rustc_error]
+fn main() { } //~ ERROR compilation successful
diff --git a/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs b/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs
new file mode 100644 (file)
index 0000000..c5557ce
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(unboxed_closures)]
+#![feature(rustc_attrs)]
+
+// Test for projection cache. We should be able to project distinct
+// lifetimes from `foo` as we reinstantiate it multiple times, but not
+// if we do it just once. In this variant, the region `'a` is used in
+// an contravariant position, which affects the results.
+
+// revisions: ok oneuse transmute krisskross
+
+#![allow(dead_code, unused_variables)]
+
+fn foo<'a>() -> &'a u32 { loop { } }
+
+fn bar<T>(t: T, x: T::Output) -> T::Output
+    where T: FnOnce<()>
+{
+    t()
+}
+
+#[cfg(ok)] // two instantiations: OK
+fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
+    let a = bar(foo, x);
+    let b = bar(foo, y);
+    (a, b)
+}
+
+#[cfg(oneuse)] // one instantiation: OK (surprisingly)
+fn baz<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
+    let f /* : fn() -> &'static u32 */ = foo; // <-- inferred type annotated
+    let a = bar(f, x); // this is considered ok because fn args are contravariant...
+    let b = bar(f, y); // ...and hence we infer T to distinct values in each call.
+    (a, b)
+}
+
+// FIXME(#32330)
+//#[cfg(transmute)] // one instantiations: BAD
+//fn baz<'a,'b>(x: &'a u32) -> &'static u32 {
+//    bar(foo, x) //[transmute] ERROR E0495
+//}
+
+// FIXME(#32330)
+//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
+//fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
+//    let a = bar(foo, y); //[krisskross] ERROR E0495
+//    let b = bar(foo, x); //[krisskross] ERROR E0495
+//    (a, b)
+//}
+
+#[rustc_error]
+fn main() { }
+//[ok]~^ ERROR compilation successful
+//[oneuse]~^^ ERROR compilation successful
+//[transmute]~^^^ ERROR compilation successful
+//[krisskross]~^^^^ ERROR compilation successful
diff --git a/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs b/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs
new file mode 100644 (file)
index 0000000..a15422e
--- /dev/null
@@ -0,0 +1,76 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(unboxed_closures)]
+#![feature(rustc_attrs)]
+
+// Test for projection cache. We should be able to project distinct
+// lifetimes from `foo` as we reinstantiate it multiple times, but not
+// if we do it just once. In this variant, the region `'a` is used in
+// an invariant position, which affects the results.
+
+// revisions: ok oneuse transmute krisskross
+
+#![allow(dead_code, unused_variables)]
+
+use std::marker::PhantomData;
+
+struct Type<'a> {
+    // Invariant
+    data: PhantomData<fn(&'a u32) -> &'a u32>
+}
+
+fn foo<'a>() -> Type<'a> { loop { } }
+
+fn bar<T>(t: T, x: T::Output) -> T::Output
+    where T: FnOnce<()>
+{
+    t()
+}
+
+#[cfg(ok)] // two instantiations: OK
+fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
+    let a = bar(foo, x);
+    let b = bar(foo, y);
+    (a, b)
+}
+
+// FIXME(#32330)
+//#[cfg(oneuse)] // one instantiation: BAD
+//fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
+//    let f = foo; // <-- No consistent type can be inferred for `f` here.
+//    let a = bar(f, x); //[oneuse] ERROR E0495
+//    let b = bar(f, y);
+//    (a, b)
+//}
+
+// FIXME(#32330)
+//#[cfg(transmute)] // one instantiations: BAD
+//fn baz<'a,'b>(x: Type<'a>) -> Type<'static> {
+//    // Cannot instantiate `foo` with any lifetime other than `'a`,
+//    // since it is provided as input.
+//
+//    bar(foo, x) //[transmute] ERROR E0495
+//}
+
+// FIXME(#32330)
+//#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
+//fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
+//    let a = bar(foo, y); //[krisskross] ERROR E0495
+//    let b = bar(foo, x); //[krisskross] ERROR E0495
+//    (a, b)
+//}
+
+#[rustc_error]
+fn main() { }
+//[ok]~^ ERROR compilation successful
+//[oneuse]~^^ ERROR compilation successful
+//[transmute]~^^^ ERROR compilation successful
+//[krisskross]~^^^^ ERROR compilation successful
diff --git a/src/test/compile-fail/associated-types/cache/wasm-issue-32330.rs b/src/test/compile-fail/associated-types/cache/wasm-issue-32330.rs
new file mode 100644 (file)
index 0000000..01db477
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This test was derived from the wasm and parsell crates.  They
+// stopped compiling when #32330 is fixed.
+
+#![allow(dead_code, unused_variables)]
+#![deny(hr_lifetime_in_assoc_type)]
+#![feature(unboxed_closures)]
+
+use std::str::Chars;
+
+pub trait HasOutput<Ch, Str> {
+    type Output;
+}
+
+#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Debug)]
+pub enum Token<'a> {
+    Begin(&'a str)
+}
+
+fn mk_unexpected_char_err<'a>() -> Option<&'a i32> {
+    unimplemented!()
+}
+
+fn foo<'a>(data: &mut Chars<'a>) {
+    bar(mk_unexpected_char_err)
+    //~^ ERROR lifetime parameter `'a` declared on fn `mk_unexpected_char_err`
+    //~| WARNING hard error in a future release
+}
+
+fn bar<F>(t: F)
+    // No type can satisfy this requirement, since `'a` does not
+    // appear in any of the input types:
+    where F: for<'a> Fn() -> Option<&'a i32>
+    //~^ ERROR associated type `Output` references lifetime `'a`, which does not
+    //~| WARNING hard error in a future release
+{
+}
+
+fn main() {
+}
diff --git a/src/test/compile-fail/auxiliary/recursive_reexports.rs b/src/test/compile-fail/auxiliary/recursive_reexports.rs
new file mode 100644 (file)
index 0000000..1186e3d
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub mod foo {
+    pub use foo;
+}
diff --git a/src/test/compile-fail/bad-format-args.rs b/src/test/compile-fail/bad-format-args.rs
new file mode 100644 (file)
index 0000000..8c58c8c
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// error-pattern: requires at least a format string argument
+// error-pattern: in this expansion
+
+// error-pattern: expected token: `,`
+// error-pattern: in this expansion
+// error-pattern: in this expansion
+
+fn main() {
+    format!();
+    format!("" 1);
+    format!("", 1 1);
+}
index 287eab7a5633872c48f5053ffc8eb5f71081c11b..f57727b773d635a87608b4e4e08a4233d1408c6f 100644 (file)
@@ -12,6 +12,6 @@ mod foo { pub struct bar; }
 
 fn main() {
     let bar = 5;
-    //~^ ERROR cannot be named the same
+    //~^ ERROR let bindings cannot shadow structs
     use foo::bar;
 }
index 497b0e63edfc149516456355062e256c767a7a1c..764d05be879b8be674f3e9c3adba83e924c051a8 100644 (file)
@@ -99,7 +99,7 @@ fn assign_field1<'a>(x: Own<Point>) {
 }
 
 fn assign_field2<'a>(x: &'a Own<Point>) {
-    x.y = 3; //~ ERROR cannot assign
+    x.y = 3; //~ ERROR cannot borrow
 }
 
 fn assign_field3<'a>(x: &'a mut Own<Point>) {
index 15771295743c16592dff72545d909f6e18b7198a..f595d9d81cc6ef6bcb64eb67ce9055834880443d 100644 (file)
@@ -24,14 +24,14 @@ pub fn main() {
         Foo { string: "baz".to_string() }
     );
     let x: &[Foo] = &x;
-    match x {
-        [_, tail..] => {
+    match *x {
+        [_, ref tail..] => {
             match tail {
-                [Foo { string: a },
+                &[Foo { string: a },
                 //~^ ERROR cannot move out of borrowed content
                 //~| cannot move out
                 //~| to prevent move
-                 Foo { string: b }] => {
+                  Foo { string: b }] => {
                     //~^ NOTE and here
                 }
                 _ => {
index 98052ad31a7ef13b524fba8b559d493bd834aabd..63e80b90ac81e6f211d885b77285f64ed6c142d6 100644 (file)
@@ -15,7 +15,7 @@ fn a<'a>() -> &'a [isize] {
     let vec = vec!(1, 2, 3, 4);
     let vec: &[isize] = &vec; //~ ERROR does not live long enough
     let tail = match vec {
-        [_, tail..] => tail,
+        &[_, ref tail..] => tail,
         _ => panic!("a")
     };
     tail
@@ -25,7 +25,7 @@ fn b<'a>() -> &'a [isize] {
     let vec = vec!(1, 2, 3, 4);
     let vec: &[isize] = &vec; //~ ERROR does not live long enough
     let init = match vec {
-        [init.., _] => init,
+        &[ref init.., _] => init,
         _ => panic!("b")
     };
     init
@@ -35,7 +35,7 @@ fn c<'a>() -> &'a [isize] {
     let vec = vec!(1, 2, 3, 4);
     let vec: &[isize] = &vec; //~ ERROR does not live long enough
     let slice = match vec {
-        [_, slice.., _] => slice,
+        &[_, ref slice.., _] => slice,
         _ => panic!("c")
     };
     slice
index db635893c81b9f5d4c03c61e1545dba44a5fba5f..9dfd4d779284329ff53909c372604d51869c7a10 100644 (file)
@@ -14,7 +14,7 @@ fn a() {
     let mut v = vec!(1, 2, 3);
     let vb: &mut [isize] = &mut v;
     match vb {
-        [_a, tail..] => {
+        &mut [_a, ref tail..] => {
             v.push(tail[0] + tail[1]); //~ ERROR cannot borrow
         }
         _ => {}
index 97dcaeb0bf1a3d2c2eb8a9b90f821aa0c72c81f5..fddb9838c44b5e945561561ea298d22fc339a6e4 100644 (file)
@@ -13,7 +13,7 @@
 fn main() {
     let mut a = [1, 2, 3, 4];
     let t = match a {
-        [1, 2, tail..] => tail,
+        [1, 2, ref tail..] => tail,
         _ => unreachable!()
     };
     println!("t[0]: {}", t[0]);
index eec6c8473eb3d167bce7d644e773dada2c1120be..d89b4100789f95d7132e969bef3e7e627fdbc8c0 100644 (file)
@@ -28,7 +28,7 @@ fn b() {
     let mut vec = vec!(box 1, box 2, box 3);
     let vec: &mut [Box<isize>] = &mut vec;
     match vec {
-        [_b..] => {
+        &mut [ref _b..] => {
         //~^ borrow of `vec[..]` occurs here
             vec[0] = box 4; //~ ERROR cannot assign
             //~^ assignment to borrowed `vec[..]` occurs here
@@ -40,10 +40,11 @@ fn c() {
     let mut vec = vec!(box 1, box 2, box 3);
     let vec: &mut [Box<isize>] = &mut vec;
     match vec {
-        [_a,         //~ ERROR cannot move out
-        //~| cannot move out
-        //~| to prevent move
-         _b..] => {
+        &mut [_a, //~ ERROR cannot move out of borrowed content
+            //~| cannot move out
+            //~| to prevent move
+            ..
+        ] => {
             // Note: `_a` is *moved* here, but `b` is borrowing,
             // hence illegal.
             //
@@ -61,7 +62,7 @@ fn d() {
     let mut vec = vec!(box 1, box 2, box 3);
     let vec: &mut [Box<isize>] = &mut vec;
     match vec {
-        [_a..,     //~ ERROR cannot move out
+        &mut [ //~ ERROR cannot move out
         //~^ cannot move out
          _b] => {} //~ NOTE to prevent move
         _ => {}
@@ -75,7 +76,7 @@ fn e() {
     let mut vec = vec!(box 1, box 2, box 3);
     let vec: &mut [Box<isize>] = &mut vec;
     match vec {
-        [_a, _b, _c] => {}  //~ ERROR cannot move out
+        &mut [_a, _b, _c] => {}  //~ ERROR cannot move out
         //~| cannot move out
         //~| NOTE to prevent move
         //~| NOTE and here
index 82b3490d7d7e118c79a875ec91288b0cd2960ad9..a849e4e2faf3b51ff5d6cc97d90121ca1fadae82 100644 (file)
@@ -14,7 +14,7 @@ fn a<'a>() -> &'a isize {
     let vec = vec!(1, 2, 3, 4);
     let vec: &[isize] = &vec; //~ ERROR `vec` does not live long enough
     let tail = match vec {
-        [_a, tail..] => &tail[0],
+        &[_a, ref tail..] => &tail[0],
         _ => panic!("foo")
     };
     tail
index b3ef3d72ca3bf77a99f9a3f8205a40b77bdaa344..a4b24fa8b4bf6e04decef823fee7a34b08966a62 100644 (file)
@@ -17,4 +17,6 @@ fn main() {
     //~^ ERROR removing an expression is not supported in this position
     let _ = [1, 2, 3][#[cfg(unset)] 1];
     //~^ ERROR removing an expression is not supported in this position
+    let _ = #[test] ();
+    //~^ ERROR removing an expression is not supported in this position
 }
index 15426febbcdd7aa4ae75651c6f1489b4f15b4cae..faabed4fd5e422d5f73ba2a92fc8b705848d8c72 100644 (file)
@@ -8,13 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// ignore-tidy-linelength
+
 #![feature(const_indexing)]
 
 const FOO: [u32; 3] = [1, 2, 3];
 const BAR: u32 = FOO[5]; // no error, because the error below occurs before regular const eval
 
 const BLUB: [u32; FOO[4]] = [5, 6];
-//~^ ERROR array length constant evaluation error: array index out of bounds [E0250]
+//~^ ERROR array length constant evaluation error: index out of bounds: the len is 3 but the index is 4 [E0250]
 
 fn main() {
     let _ = BAR;
index 7567791c24066040bef4596f7a2fd7213f077a35..f666140970b6e125b0d27f0740c15dfdbfbe1ca2 100644 (file)
@@ -15,8 +15,10 @@ pub const A: i8 = -std::i8::MIN; //~ ERROR attempted to negate with overflow
 pub const B: u8 = 200u8 + 200u8; //~ ERROR attempted to add with overflow
 pub const C: u8 = 200u8 * 4; //~ ERROR attempted to multiply with overflow
 pub const D: u8 = 42u8 - (42u8 + 1); //~ ERROR attempted to subtract with overflow
-pub const E: u8 = [5u8][1]; //~ ERROR index out of bounds
+pub const E: u8 = [5u8][1];
+//~^ ERROR index out of bounds: the len is 1 but the index is 1
 
 fn main() {
-    let _e = [6u8][1]; //~ ERROR: array index out of bounds
+    let _e = [6u8][1];
+    //~^ ERROR index out of bounds: the len is 1 but the index is 1
 }
index a25255c010caf4c5bb6f938d6c52c9882704af30..a1d3888e78ea0dc16fe3d9cd6bd454a0764fc51e 100644 (file)
@@ -8,10 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// compile-flags: -Zforce-overflow-checks=on
+
 // these errors are not actually "const_err", they occur in trans/consts
 // and are unconditional warnings that can't be denied or allowed
 
-#![feature(rustc_attrs)]
 #![allow(exceeding_bitshifts)]
 #![allow(const_err)]
 
@@ -21,10 +22,9 @@ fn black_box<T>(_: T) {
 
 // Make sure that the two uses get two errors.
 const FOO: u8 = [5u8][1];
-//~^ ERROR array index out of bounds
-//~^^ ERROR array index out of bounds
+//~^ ERROR index out of bounds: the len is 1 but the index is 1
+//~^^ ERROR index out of bounds: the len is 1 but the index is 1
 
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let a = -std::i8::MIN;
     //~^ WARN attempted to negate with overflow
@@ -36,7 +36,7 @@ fn main() {
     let d = 42u8 - (42u8 + 1);
     //~^ WARN attempted to subtract with overflow
     let _e = [5u8][1];
-    //~^ WARN array index out of bounds
+    //~^ WARN index out of bounds: the len is 1 but the index is 1
     black_box(a);
     black_box(b);
     black_box(c);
index 96013551ef4927ea17028eaa21e2ae96b31dafde..3dfcb5bb29a24a68701b48d195f62aca3361240c 100644 (file)
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rustc_attrs)]
 #![allow(unused_imports)]
 
 // Note: the relevant lint pass here runs before some of the constant
@@ -104,7 +103,6 @@ const VALS_U64: (u64, u64, u64, u64) =
      //~^ ERROR attempted to multiply with overflow
      );
 
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     foo(VALS_I8);
     foo(VALS_I16);
index 392f391fb5120ce7e6452ccbb531690bd23e6463..75b6397f4ebd71ac5c7260acb3c93b57179aa481 100644 (file)
@@ -19,10 +19,10 @@ use foo::d; //~ NOTE is imported here
 const a: u8 = 2; //~ NOTE is defined here
 
 fn main() {
-    let a = 4; //~ ERROR let variables cannot
-               //~^ NOTE cannot be named the same as a const variable
-    let c = 4; //~ ERROR let variables cannot
-               //~^ NOTE cannot be named the same as a const variable
-    let d = 4; //~ ERROR let variables cannot
-               //~^ NOTE cannot be named the same as a const variable
+    let a = 4; //~ ERROR let bindings cannot shadow constants
+               //~^ NOTE cannot be named the same as a constant
+    let c = 4; //~ ERROR let bindings cannot shadow constants
+               //~^ NOTE cannot be named the same as a constant
+    let d = 4; //~ ERROR let bindings cannot shadow constants
+               //~^ NOTE cannot be named the same as a constant
 }
index b50468c33fd8bf85c4829edbf9f458c2c746ef70..d63b0097e5a0ce3fe0dc5ae0bdc7a66d2fefc212 100644 (file)
@@ -9,7 +9,8 @@
 // except according to those terms.
 
 const FOO: &'static[u32] = &[1, 2, 3];
-const BAR: u32 = FOO[5]; //~ ERROR array index out of bounds
+const BAR: u32 = FOO[5];
+//~^ ERROR index out of bounds: the len is 3 but the index is 5
 
 fn main() {
     let _ = BAR;
diff --git a/src/test/compile-fail/const-unsized.rs b/src/test/compile-fail/const-unsized.rs
new file mode 100644 (file)
index 0000000..72a5c5f
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::fmt::Debug;
+
+const CONST_0: Debug+Sync = *(&0 as &(Debug+Sync));
+//~^ ERROR `std::fmt::Debug + Sync + 'static: std::marker::Sized` is not satisfied
+//~| NOTE does not have a constant size known at compile-time
+//~| NOTE constant expressions must have a statically known size
+
+const CONST_FOO: str = *"foo";
+//~^ ERROR `str: std::marker::Sized` is not satisfied
+//~| NOTE does not have a constant size known at compile-time
+//~| NOTE constant expressions must have a statically known size
+
+static STATIC_1: Debug+Sync = *(&1 as &(Debug+Sync));
+//~^ ERROR `std::fmt::Debug + Sync + 'static: std::marker::Sized` is not satisfied
+//~| NOTE does not have a constant size known at compile-time
+//~| NOTE constant expressions must have a statically known size
+
+static STATIC_BAR: str = *"bar";
+//~^ ERROR `str: std::marker::Sized` is not satisfied
+//~| NOTE does not have a constant size known at compile-time
+//~| NOTE constant expressions must have a statically known size
+
+fn main() {
+    println!("{:?} {:?} {:?} {:?}", &CONST_0, &CONST_FOO, &STATIC_1, &STATIC_BAR);
+}
index 4e089a4e59c427e41e66f2886997531de8e47119..eff734230ee238e011a72bb35f579c9fb3ded34a 100644 (file)
@@ -8,7 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(stmt_expr_attributes)]
+
 #[foo] //~ ERROR The attribute `foo`
 fn main() {
-
+    #[foo] //~ ERROR The attribute `foo`
+    let x = ();
+    #[foo] //~ ERROR The attribute `foo`
+    x
 }
index 0bd96d82095968d9e7394a0148218d8b304a85ae..0522a654a8528200a661fda6393ce0e836675e9e 100644 (file)
@@ -29,9 +29,9 @@ fn main() {
     //     XEmpty1() => () // ERROR unresolved enum variant, struct or const `XEmpty1`
     // }
     match e1 {
-        Empty1(..) => () //~ ERROR unresolved enum variant, struct or const `Empty1`
+        Empty1(..) => () //~ ERROR unresolved variant or struct `Empty1`
     }
     match xe1 {
-        XEmpty1(..) => () //~ ERROR unresolved enum variant, struct or const `XEmpty1`
+        XEmpty1(..) => () //~ ERROR unresolved variant or struct `XEmpty1`
     }
 }
index 6dffd1999d79b6977ca1844712ce743bdd420134..e89b08a8a06510fd63a0f2adf7363f5bb0719e1e 100644 (file)
@@ -11,5 +11,5 @@
 struct hello(isize);
 
 fn main() {
-    let hello = 0; //~ERROR cannot be named the same
+    let hello = 0; //~ERROR let bindings cannot shadow structs
 }
index faf672415bdfac3565d0ffce2b5baf89d7e8c907..c847366a707a7e9cb933460749c826e512fffdfb 100644 (file)
@@ -8,9 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//error-pattern:unresolved enum variant
-
 fn main() {
-    // a bug in the parser is allowing this:
-    let a(1) = 13;
+    let a(1) = 13; //~ ERROR unresolved variant or struct `a`
 }
index 2f74aeba9eb4335d692d82ba2b9ab2e51d532646..77351f6e4f171f4dc40c33ce067700078e2e8d3f 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rustc_attrs)]
+#![feature(custom_attribute, rustc_attrs)]
 
 macro_rules! mac {
     {} => {
@@ -16,7 +16,13 @@ macro_rules! mac {
         mod m {
             #[lang_item]
             fn f() {}
+
+            #[cfg_attr(target_thread_local, custom)]
+            fn g() {}
         }
+
+        #[cfg(attr)]
+        unconfigured_invocation!();
     }
 }
 
diff --git a/src/test/compile-fail/hr-subtype.rs b/src/test/compile-fail/hr-subtype.rs
new file mode 100644 (file)
index 0000000..95e469e
--- /dev/null
@@ -0,0 +1,119 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Targeted tests for the higher-ranked subtyping code.
+
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+// revisions: bound_a_vs_bound_a
+// revisions: bound_a_vs_bound_b
+// revisions: bound_inv_a_vs_bound_inv_b
+// revisions: bound_co_a_vs_bound_co_b
+// revisions: bound_a_vs_free_x
+// revisions: free_x_vs_free_x
+// revisions: free_x_vs_free_y
+// revisions: free_inv_x_vs_free_inv_y
+// revisions: bound_a_b_vs_bound_a
+// revisions: bound_co_a_b_vs_bound_co_a
+// revisions: bound_contra_a_contra_b_ret_co_a
+// revisions: bound_co_a_co_b_ret_contra_a
+// revisions: bound_inv_a_b_vs_bound_inv_a
+// revisions: bound_a_b_ret_a_vs_bound_a_ret_a
+
+fn gimme<T>(_: Option<T>) { }
+
+struct Inv<'a> { x: *mut &'a u32 }
+
+struct Co<'a> { x: fn(&'a u32) }
+
+struct Contra<'a> { x: &'a u32 }
+
+macro_rules! check {
+    ($rev:ident: ($t1:ty, $t2:ty)) => {
+        #[cfg($rev)]
+        fn subtype<'x,'y:'x,'z:'y>() {
+            gimme::<$t2>(None::<$t1>);
+            //[free_inv_x_vs_free_inv_y]~^ ERROR mismatched types
+        }
+
+        #[cfg($rev)]
+        fn supertype<'x,'y:'x,'z:'y>() {
+            gimme::<$t1>(None::<$t2>);
+            //[bound_a_vs_free_x]~^ ERROR mismatched types
+            //[free_x_vs_free_y]~^^ ERROR mismatched types
+            //[bound_inv_a_b_vs_bound_inv_a]~^^^ ERROR mismatched types
+            //[bound_a_b_ret_a_vs_bound_a_ret_a]~^^^^ ERROR mismatched types
+            //[free_inv_x_vs_free_inv_y]~^^^^^ ERROR mismatched types
+            //[bound_a_b_vs_bound_a]~^^^^^^ ERROR mismatched types
+            //[bound_co_a_b_vs_bound_co_a]~^^^^^^^ ERROR mismatched types
+            //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^ ERROR mismatched types
+            //[bound_co_a_co_b_ret_contra_a]~^^^^^^^^^ ERROR mismatched types
+        }
+    }
+}
+
+// If both have bound regions, they are equivalent, regardless of
+// variant.
+check! { bound_a_vs_bound_a: (for<'a> fn(&'a u32),
+                              for<'a> fn(&'a u32)) }
+check! { bound_a_vs_bound_b: (for<'a> fn(&'a u32),
+                              for<'b> fn(&'b u32)) }
+check! { bound_inv_a_vs_bound_inv_b: (for<'a> fn(Inv<'a>),
+                                      for<'b> fn(Inv<'b>)) }
+check! { bound_co_a_vs_bound_co_b: (for<'a> fn(Co<'a>),
+                                    for<'b> fn(Co<'b>)) }
+
+// Bound is a subtype of free.
+check! { bound_a_vs_free_x: (for<'a> fn(&'a u32),
+                             fn(&'x u32)) }
+
+// Two free regions are relatable if subtyping holds.
+check! { free_x_vs_free_x: (fn(&'x u32),
+                            fn(&'x u32)) }
+check! { free_x_vs_free_y: (fn(&'x u32),
+                            fn(&'y u32)) }
+check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
+                                    fn(Inv<'y>)) }
+
+// Somewhat surprisingly, a fn taking two distinct bound lifetimes and
+// a fn taking one bound lifetime can be interchangable, but only if
+// we are co- or contra-variant with respect to both lifetimes.
+//
+// The reason is:
+// - if we are covariant, then 'a and 'b can be set to the call-site
+//   intersection;
+// - if we are contravariant, then 'a can be inferred to 'static.
+//
+// FIXME(#32330) this is true, but we are not currently impl'ing this
+// full semantics
+check! { bound_a_b_vs_bound_a: (for<'a,'b> fn(&'a u32, &'b u32),
+                                for<'a>    fn(&'a u32, &'a u32)) }
+check! { bound_co_a_b_vs_bound_co_a: (for<'a,'b> fn(Co<'a>, Co<'b>),
+                                      for<'a>    fn(Co<'a>, Co<'a>)) }
+check! { bound_contra_a_contra_b_ret_co_a: (for<'a,'b> fn(Contra<'a>, Contra<'b>) -> Co<'a>,
+                                            for<'a>    fn(Contra<'a>, Contra<'a>) -> Co<'a>) }
+check! { bound_co_a_co_b_ret_contra_a: (for<'a,'b> fn(Co<'a>, Co<'b>) -> Contra<'a>,
+                                        for<'a>    fn(Co<'a>, Co<'a>) -> Contra<'a>) }
+
+// If we make those lifetimes invariant, then the two types are not interchangable.
+check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>),
+                                        for<'a>    fn(Inv<'a>, Inv<'a>)) }
+check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32,
+                                            for<'a>    fn(&'a u32, &'a u32) -> &'a u32) }
+
+#[rustc_error]
+fn main() {
+//[bound_a_vs_bound_a]~^ ERROR compilation successful
+//[bound_a_vs_bound_b]~^^ ERROR compilation successful
+//[bound_inv_a_vs_bound_inv_b]~^^^ ERROR compilation successful
+//[bound_co_a_vs_bound_co_b]~^^^^ ERROR compilation successful
+//[free_x_vs_free_x]~^^^^^ ERROR compilation successful
+}
index 7c929d2db16e73f044b9bc17198bcac098484334..1368702b160bff5e399b5e41e7666241ade260d5 100644 (file)
@@ -50,8 +50,4 @@ fn main() {
 
     format!("foo } bar"); //~ ERROR: unmatched `}` found
     format!("foo }"); //~ ERROR: unmatched `}` found
-
-    format!();          //~ ERROR: requires at least a format string argument
-    format!("" 1);      //~ ERROR: expected token: `,`
-    format!("", 1 1);   //~ ERROR: expected token: `,`
 }
index 03bcaab4a9db5e5406526aac9e792608ed09491a..a0ac5d4c7204104237b06da36a47513b8e31d482 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 macro_rules! invalid {
-    _ => (); //~^ ERROR invalid macro matcher
+    _ => (); //~ ERROR invalid macro matcher
 }
 
 fn main() {
index 03d4e9b81eb2924f34a5ecf0e3b05c6aac855607..9eec8487a50874cad88672d256b0fe5dd8c33a6e 100644 (file)
@@ -13,7 +13,7 @@ fn foo(_: usize) -> Foo { Foo(false) }
 
 fn main() {
     match Foo(true) {
-        foo(x) //~ ERROR `foo` is not an enum variant, struct or const
+        foo(x) //~ ERROR expected variant or struct, found function `foo`
         => ()
     }
 }
index 1333bfac64ee86d1edc9f2d0b3c624ec70462a5a..978d6f59b2df455b41b91ecc45d7244c2f7f6e88 100644 (file)
@@ -13,9 +13,9 @@
 fn main() {
     let sl = vec![1,2,3];
     let v: isize = match &*sl {
-        [] => 0,
-        [a,b,c] => 3,
-        [a, rest..] => a,
-        [10,a, rest..] => 10 //~ ERROR: unreachable pattern
+        &[] => 0,
+        &[a,b,c] => 3,
+        &[a, ref rest..] => a,
+        &[10,a, ref rest..] => 10 //~ ERROR: unreachable pattern
     };
 }
index e005de01649a53743da373fbf7689a71fc116014..80f551ebd1f7c9b2962db0c6df0cfc6fa7bcecf2 100644 (file)
@@ -21,5 +21,5 @@ enum Foo {
 }
 
 fn main() {
-    println!("{}", match Bar { Bar => 1, Baz => 2, Bazar => 3 })
+    println!("{}", match Bar { Bar => 1, Baz => 2, Bazar => 3 }) //~ ERROR unresolved name `Bar`
 }
index 1580ec00f94b068b3c59c0884975ed4a8885bad2..32a6ea4f062cbc9819392615e13cd579a288d13e 100644 (file)
 
 fn match_vecs<'a, T>(l1: &'a [T], l2: &'a [T]) {
     match (l1, l2) {
-        ([], []) => println!("both empty"),
-        ([], [hd, tl..]) | ([hd, tl..], []) => println!("one empty"),
-        //~^ ERROR: cannot move out of borrowed content
+        (&[], &[]) => println!("both empty"),
+        (&[], &[hd, ..]) | (&[hd, ..], &[])
+            => println!("one empty"),
         //~^^ ERROR: cannot move out of borrowed content
-        ([hd1, tl1..], [hd2, tl2..]) => println!("both nonempty"),
-        //~^ ERROR: cannot move out of borrowed content
+        //~^^^ ERROR: cannot move out of borrowed content
+        (&[hd1, ..], &[hd2, ..])
+            => println!("both nonempty"),
         //~^^ ERROR: cannot move out of borrowed content
+        //~^^^ ERROR: cannot move out of borrowed content
     }
 }
 
index 07676679ef18f1a50bc927f6bf51710b421b86db..7912410f69ea1f8d90d8647f951fe8667fccd245 100644 (file)
@@ -12,6 +12,6 @@ mod foo { pub fn bar() {} }
 
 fn main() {
     match () {
-        foo::bar => {} //~ ERROR `bar` is not an enum variant, struct or const
+        foo::bar => {} //~ ERROR expected variant, struct or constant, found function `bar`
     }
 }
index fe03373a45d9fd6b08ab2488febec50aea53e3d5..6885c8d94c6b4fd16535103a91fa52bca135d372 100644 (file)
 fn main() {
     let x = [1,2];
     let y = match x {
-        [] => None,
-//~^ ERROR mismatched types
-//~| expected type `[_#1i; 2]`
-//~| found type `[_#7t; 0]`
-//~| expected an array with a fixed size of 2 elements, found one with 0 elements
+        [] => None, //~ ERROR pattern requires 0 elements but array has 2
         [a,_] => Some(a)
     };
 }
index 7ed7f5898b1b7411f4b49e713b8f2203db2eb5f5..82e82df31861ff35ab4a4fe913be379ebab96bda 100644 (file)
 fn main() {
   let x = [1,2];
   let y = match x {
-    [] => None,
-    //~^ ERROR mismatched types
-    //~| expected type `[_; 2]`
-    //~| found type `[_; 0]`
-    //~| expected an array with a fixed size of 2 elements
+    [] => None, //~ ERROR pattern requires 0 elements but array has 2
     [a,_] => Some(a)
   };
 }
diff --git a/src/test/compile-fail/issue-13727.rs b/src/test/compile-fail/issue-13727.rs
new file mode 100644 (file)
index 0000000..28c2c7b
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn test(val: u8) {
+  match val {
+    256 => print!("0b1110\n"),
+    512 => print!("0b1111\n"),
+    //~^ ERROR: unreachable pattern
+    _   => print!("fail\n"),
+  }
+}
+
+fn main() {
+  test(1);
+}
index ec29a84f44e4bd105019295b97c1bb15c69665c6..d0964d2aabea755357b9af916f00c127a642808d 100644 (file)
@@ -13,8 +13,8 @@
 fn main() {
     let values: Vec<u8> = vec![1,2,3,4,5,6,7,8];
 
-    for [x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) {
-        //~^ ERROR refutable pattern in `for` loop binding: `[]` not covered
+    for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) {
+        //~^ ERROR refutable pattern in `for` loop binding: `&[]` not covered
         println!("y={}", y);
     }
 }
index 46b7b933d8796fd38ababd396a2a3a833f30827f..ceac7e968f65c1bceaa43574b75712d3ccd8e7ec 100644 (file)
@@ -28,7 +28,7 @@ impl<'a> Test<'a> for Foo<'a> {
 
 impl<'a> NoLifetime for Foo<'a> {
     fn get<'p, T : Test<'a>>(&self) -> T {
-//~^ ERROR lifetime parameters or bounds on method `get` do not match the trait declaration
+//~^ ERROR E0195
         return *self as T;
     }
 }
index 4954c95fcd1f2ec307c4e997102ad1966024cc2c..60117bd88d46ad2b6e1a2323bd536bd40a2b8638 100644 (file)
@@ -15,7 +15,7 @@ extern {
 fn main() {
     let boolValue = match 42 {
         externalValue => true,
-        //~^ ERROR static variables cannot be referenced in a pattern
+        //~^ ERROR match bindings cannot shadow statics
         _ => false
     };
 }
index da6d081a7acb57e654e2fdadd9862246db842b2e..c6ce0c4c95b8bcbf97a3e0024e391ac4b9e34ccf 100644 (file)
@@ -8,12 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::raw::Slice;
+struct Slice<T> {
+    data: *const T,
+    len: usize,
+}
 
 fn main() {
     let Slice { data: data, len: len } = "foo";
     //~^ ERROR mismatched types
     //~| expected type `&str`
-    //~| found type `std::raw::Slice<_>`
-    //~| expected &-ptr, found struct `std::raw::Slice`
+    //~| found type `Slice<_>`
+    //~| expected &-ptr, found struct `Slice`
 }
index df272a71cee4f70af8daf73c6cb9475e79c76cd6..3060bbea43c3bf8ea0b2520ef1e8ba60136382ba 100644 (file)
@@ -8,15 +8,18 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::raw::Slice;
+struct Slice<T> {
+    data: *const T,
+    len: usize,
+}
 
 fn main() {
     match () {
         Slice { data: data, len: len } => (),
         //~^ ERROR mismatched types
         //~| expected type `()`
-        //~| found type `std::raw::Slice<_>`
-        //~| expected (), found struct `std::raw::Slice`
+        //~| found type `Slice<_>`
+        //~| expected (), found struct `Slice`
         _ => unreachable!()
     }
 }
index de8a4f63476dc027baf07cd6450dcad62bccd427..db43c1cce9947fa348602ea021e5ccf204575760 100644 (file)
@@ -14,6 +14,7 @@ enum Foo {
 
 fn main() {
     match Foo::Bar(1) {
-        Foo { i } => () //~ ERROR `Foo` does not name a struct or a struct variant
+        Foo { i } => () //~ ERROR expected variant, struct or type alias, found enum `Foo`
+        //~^ ERROR `Foo` does not name a struct or a struct variant
     }
 }
index 021edbee566ba526514ac87f515df1bb23c716fa..523a387956a32e7e93b4f6175e588c6ce0d14b08 100644 (file)
 
 extern crate issue_17718_const_privacy as other;
 
-use a::B; //~ ERROR: const `B` is private
+use a::B; //~ ERROR: constant `B` is private
 use other::{
     FOO,
-    BAR, //~ ERROR: const `BAR` is private
+    BAR, //~ ERROR: constant `BAR` is private
     FOO2,
 };
 
index 4e63f667d26fa413e83cc5d3c8ba988093c856a3..b9f5e98b6faa97d112d935888a800e068d25f54e 100644 (file)
@@ -14,8 +14,8 @@ const A3: usize = 1;
 
 fn main() {
     match 1 {
-        A1 => {} //~ ERROR: static variables cannot be referenced in a pattern
-        A2 => {} //~ ERROR: static variables cannot be referenced in a pattern
+        A1 => {} //~ ERROR: match bindings cannot shadow statics
+        A2 => {} //~ ERROR: match bindings cannot shadow statics
         A3 => {}
         _ => {}
     }
index 657b31fa83c7f829419e5bf2aa88d9062c217e43..2313a3fe9c6d518190355db24b1a721b03b42654 100644 (file)
@@ -13,7 +13,7 @@ pub static X: usize = 1;
 fn main() {
     match 1 {
         self::X => { },
-        //~^ ERROR static variables cannot be referenced in a pattern, use a `const` instead
+        //~^ ERROR expected variant, struct or constant, found static `X`
         _       => { },
     }
 }
index d89b2c6ce8cb0221e9a64a922e4e8f683909da57..3591b9824145bbdd2d114d457727baf2e6143425 100644 (file)
@@ -24,4 +24,5 @@ fn print_x(_: &Foo<Item=bool>, extra: &str) {
 
 fn main() {
     print_x(X);  //~error this function takes 2 parameters but 1 parameter was supplied
+    //~^ NOTE the following parameter types were expected: &Foo<Item=bool>, &str
 }
index 3f96a9c342283d0678db124b5d7bfe0e4ca200bc..dac1625159748d81fcb72e92062ee1b505b9107f 100644 (file)
@@ -39,7 +39,6 @@ impl<'a> Publisher<'a> for MyStruct<'a> {
         // Not obvious, but there is an implicit lifetime here -------^
         //~^^ ERROR cannot infer
         //~|  ERROR cannot infer
-        //~|  ERROR cannot infer
         //
         // The fact that `Publisher` is using an implicit lifetime is
         // what was causing the debruijn accounting to be off, so
diff --git a/src/test/compile-fail/issue-22434.rs b/src/test/compile-fail/issue-22434.rs
new file mode 100644 (file)
index 0000000..6effd02
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub trait Foo {
+    type A;
+}
+
+type I<'a> = &'a (Foo + 'a);
+//~^ ERROR the value of the associated type `A` (from the trait `Foo`) must be specified
+
+fn main() {}
diff --git a/src/test/compile-fail/issue-23122-1.rs b/src/test/compile-fail/issue-23122-1.rs
new file mode 100644 (file)
index 0000000..36d8450
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Next {
+    type Next: Next;
+}
+
+struct GetNext<T: Next> { t: T }
+
+impl<T: Next> Next for GetNext<T> {
+    //~^ ERROR overflow evaluating the requirement
+    type Next = <GetNext<T> as Next>::Next;
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/issue-23122-2.rs b/src/test/compile-fail/issue-23122-2.rs
new file mode 100644 (file)
index 0000000..faaf78f
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Next {
+    type Next: Next;
+}
+
+struct GetNext<T: Next> { t: T }
+
+impl<T: Next> Next for GetNext<T> {
+    //~^ ERROR overflow evaluating the requirement
+    type Next = <GetNext<T::Next> as Next>::Next;
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/issue-23281.rs b/src/test/compile-fail/issue-23281.rs
new file mode 100644 (file)
index 0000000..5feeb36
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-tidy-linelength
+
+pub struct Struct;
+
+impl Struct {
+    pub fn function(funs: Vec<Fn() -> ()>) {}
+    //~^ ERROR the trait bound `std::ops::Fn() + 'static: std::marker::Sized` is not satisfied
+}
+
+fn main() {}
index 6b81afe13c671d5fca639f058b7c8fdc4ada1e03..d7ec1ed67397fe939d1585dc32b60f535a8958f2 100644 (file)
@@ -32,7 +32,8 @@ impl MaybeDog {
 impl Groom for cat {
   fn shave(other: usize) {
     whiskers -= other;
-    //~^ ERROR: unresolved name `whiskers`. Did you mean `self.whiskers`?
+    //~^ ERROR: unresolved name `whiskers`
+    //~| HELP this is an associated function
     shave(4);
     //~^ ERROR: unresolved name `shave`. Did you mean to call `Groom::shave`?
     purr();
@@ -77,7 +78,8 @@ impl cat {
 
   pub fn grow_older(other:usize) {
     whiskers = 4;
-    //~^ ERROR: unresolved name `whiskers`. Did you mean `self.whiskers`?
+    //~^ ERROR: unresolved name `whiskers`
+    //~| HELP this is an associated function
     purr_louder();
     //~^ ERROR: unresolved name `purr_louder`
   }
@@ -86,5 +88,6 @@ impl cat {
 fn main() {
     self += 1;
     //~^ ERROR: unresolved name `self`
+    //~| HELP: module `self`
     // it's a bug if this suggests a missing `self` as we're not in a method
 }
index b0d36610b7a51261f62dcc767ddfc7c52746f3ab..5cf80dd172a3de7538ef5a863396dd466c5e0903 100644 (file)
@@ -9,21 +9,21 @@
 // except according to those terms.
 
 static foo: i32 = 0;
-//~^ NOTE static variable defined here
+//~^ NOTE a static `foo` is defined here
 
 fn bar(foo: i32) {}
-//~^ ERROR static variables cannot be referenced in a pattern, use a `const` instead
-//~| static variable used in pattern
+//~^ ERROR function parameters cannot shadow statics
+//~| cannot be named the same as a static
 
 mod submod {
     pub static answer: i32 = 42;
 }
 
 use self::submod::answer;
-//~^ NOTE static variable imported here
+//~^ NOTE a static `answer` is imported here
 
 fn question(answer: i32) {}
-//~^ ERROR static variables cannot be referenced in a pattern, use a `const` instead
-//~| static variable used in pattern
+//~^ ERROR function parameters cannot shadow statics
+//~| cannot be named the same as a static
 fn main() {
 }
diff --git a/src/test/compile-fail/issue-24424.rs b/src/test/compile-fail/issue-24424.rs
new file mode 100644 (file)
index 0000000..8d4ea66
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Trait1<'l0, T0> {}
+trait Trait0<'l0>  {}
+
+impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {}
+//~^ ERROR type annotations required: cannot resolve `T0: Trait0<'l0>`
+//~^^ NOTE required by `Trait0`
+
+fn main() {}
index cbeac77479811fc8d37096d842f4df40473ae24f..b9382520cf9d3166c3ba6461973d4aeff5a4884d 100644 (file)
@@ -11,6 +11,8 @@
 fn main() {
     static foo: Fn() -> u32 = || -> u32 {
         //~^ ERROR: mismatched types
+        //~| ERROR: `std::ops::Fn() -> u32 + 'static: std::marker::Sized` is not satisfied
+
         0
     };
 }
diff --git a/src/test/compile-fail/issue-24819.rs b/src/test/compile-fail/issue-24819.rs
new file mode 100644 (file)
index 0000000..52f5f1c
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashSet;
+
+fn main() {
+    let mut v = Vec::new();
+    foo(&mut v);
+    //~^ ERROR mismatched types
+    //~| expected struct `std::collections::HashSet`, found struct `std::vec::Vec`
+}
+
+fn foo(h: &mut HashSet<u32>) {
+}
diff --git a/src/test/compile-fail/issue-25579.rs b/src/test/compile-fail/issue-25579.rs
new file mode 100644 (file)
index 0000000..849c9aa
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum Sexpression {
+    Num(()),
+    Cons(&'static mut Sexpression)
+}
+
+fn causes_ice(mut l: &mut Sexpression) {
+    loop { match l {
+        &mut Sexpression::Num(ref mut n) => {},
+        &mut Sexpression::Cons(ref mut expr) => { //~ ERROR cannot borrow `l.0`
+            //~| ERROR cannot borrow `l.0`
+            l = &mut **expr; //~ ERROR cannot assign to `l`
+        }
+    }}
+}
+
+fn main() {
+}
index 48eda91fbae50f401e7b07b0d2017dfdb4ca98e3..6cadbef33e7f0b4f9eb555b9d7425a9f8f9a2166 100644 (file)
@@ -11,6 +11,7 @@
 fn main() {
     match 'a' {
         char{ch} => true
-        //~^ ERROR `char` does not name a struct or a struct variant
+        //~^ ERROR expected variant, struct or type alias, found builtin type `char`
+        //~| ERROR `char` does not name a struct or a struct variant
     };
 }
diff --git a/src/test/compile-fail/issue-26480.rs b/src/test/compile-fail/issue-26480.rs
deleted file mode 100644 (file)
index 634a401..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-extern {
-    fn write(fildes: i32, buf: *const i8, nbyte: u64) -> i64;
-}
-
-#[inline(always)]
-fn size_of<T>(_: T) -> usize {
-    ::std::mem::size_of::<T>()
-}
-
-macro_rules! write {
-    ($arr:expr) => {{
-        #[allow(non_upper_case_globals)]
-        const stdout: i32 = 1;
-        unsafe {
-            write(stdout, $arr.as_ptr() as *const i8,
-                  $arr.len() * size_of($arr[0]));
-            //~^ ERROR mismatched types
-            //~| expected u64, found usize
-            //~| expected type
-            //~| found type
-        }
-    }}
-}
-
-macro_rules! cast {
-    ($x:expr) => ($x as ()) //~ ERROR non-scalar cast
-}
-
-fn main() {
-    let hello = ['H', 'e', 'y'];
-    write!(hello);
-    //~^ NOTE in this expansion of write!
-    //~| NOTE in this expansion of write!
-    //~| NOTE in this expansion of write!
-
-    cast!(2);
-    //~^ NOTE in this expansion of cast!
-}
index 2919b0b3caca6b2ae82de94b8ab7092afd4619b5..b6d5e5458ff0866405159e693076477f2cf32a40 100644 (file)
 
 // error-pattern: overflow representing the type `S`
 
+#![feature(rustc_attrs)]
+
 trait Mirror { type It: ?Sized; }
 impl<T: ?Sized> Mirror for T { type It = Self; }
 struct S(Option<<S as Mirror>::It>);
 
+#[rustc_no_mir] // FIXME #27840 MIR tries to represent `std::option::Option<S>` first.
 fn main() {
     let _s = S(None);
 }
index b0904dfeaa7a37798268b8b72fb1b3b2317b9845..2a015adb498e17780c4046056e0d75c7a1925106 100644 (file)
 
 fn main() {
     match Some(1) {
-        None @ _ => {} //~ ERROR cannot be named the same
+        None @ _ => {} //~ ERROR match bindings cannot shadow variants
     };
     const C: u8 = 1;
     match 1 {
-        C @ 2 => { //~ ERROR cannot be named the same
+        C @ 2 => { //~ ERROR match bindings cannot shadow constant
             println!("{}", C);
         }
         _ => {}
index b1ac2dfd1c414b14b564f7f3b9f24ea4d5c2884a..d2f9abd2e316b41539d235ebaaa6599f01cb5049 100644 (file)
@@ -14,7 +14,9 @@ fn main() {
     let u = A { x: 1 }; //~ ERROR `A` does not name a structure
     let v = u32 { x: 1 }; //~ ERROR `u32` does not name a structure
     match () {
-        A { x: 1 } => {} //~ ERROR `A` does not name a struct
-        u32 { x: 1 } => {} //~ ERROR `u32` does not name a struct
+        A { x: 1 } => {} //~ ERROR expected variant, struct or type alias, found module `A`
+        //~^ ERROR `A` does not name a struct or a struct variant
+        u32 { x: 1 } => {} //~ ERROR expected variant, struct or type alias, found builtin type `u32
+        //~^ ERROR `u32` does not name a struct or a struct variant
     }
 }
index f7d53ba23daf50e8a2ac69fa64feeda4fed69cfb..e492d48fdaf0f041f65109bbe4d8f64f661238f1 100644 (file)
@@ -21,6 +21,6 @@ impl S {
 }
 
 fn main() {
-    if let C1(..) = 0 {} //~ ERROR `C1` does not name a tuple variant or a tuple struct
+    if let C1(..) = 0 {} //~ ERROR expected variant or struct, found constant `C1`
     if let S::C2(..) = 0 {} //~ ERROR `S::C2` does not name a tuple variant or a tuple struct
 }
index f7453c45be645d7bd0d3bcbb97ab1eba65f35cef..bc09f61a754c2b081a04986205105bc4764c5cf3 100644 (file)
@@ -12,7 +12,7 @@ mod a {
     struct A;
 
     impl Default for A {
-        pub fn default() -> A {
+        pub fn default() -> A { //~ ERROR unnecessary visibility qualifier
             A;
         }
     }
diff --git a/src/test/compile-fail/issue-30240.rs b/src/test/compile-fail/issue-30240.rs
new file mode 100644 (file)
index 0000000..9b105e7
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    match "world" { //~ ERROR non-exhaustive patterns: `&_`
+        "hello" => {}
+    }
+
+    match "world" { //~ ERROR non-exhaustive patterns: `&_`
+        ref _x if false => {}
+        "hello" => {}
+        "hello" => {} //~ ERROR unreachable pattern
+    }
+}
index 0f7cc2cb72b8e2b05b73dcb6456119e3ade48d33..68046056fb3156eebb5691b80a0f82c6e06eabd7 100644 (file)
@@ -14,6 +14,7 @@ fn main() {
     needlesArr.iter().fold(|x, y| {
     });
     //~^^ ERROR this function takes 2 parameters but 1 parameter was supplied
+    //~^^^ NOTE the following parameter types were expected
     //
     // the first error is, um, non-ideal.
 }
diff --git a/src/test/compile-fail/issue-30715.rs b/src/test/compile-fail/issue-30715.rs
deleted file mode 100644 (file)
index 5cacf8f..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// compile-flags: -Z continue-parse-after-error
-
-macro_rules! parallel {
-    (
-        // If future has `pred`/`moelarry` fragments (where "pred" is
-        // "like expr, but with `{` in its FOLLOW set"), then could
-        // use `pred` instead of future-proof erroring here. See also:
-        //
-        // https://github.com/rust-lang/rfcs/pull/1384#issuecomment-160165525
-        for $id:ident in $iter:expr { //~ WARN `$iter:expr` is followed by `{`
-            $( $inner:expr; )*
-        }
-    ) => {};
-}
-
-
-fn main() {
-    parallel! {
-        for i in 0..n {
-            x += i; //~ ERROR expected `:`, found `+=`
-        } //~ ERROR unexpected end of macro invocation
-    }
-}
index 0227a80fd75d38dea7d279d795450743dc086ecf..8d74154655fcee4b9006a78b57786493f18aceef 100644 (file)
@@ -18,12 +18,12 @@ struct S;
 fn main() {
     match Foo::Baz {
         Foo::Bar => {}
-        //~^ ERROR this pattern has 0 fields, but the corresponding variant
+        //~^ ERROR `Foo::Bar` does not name a tuple variant or a tuple struct
         _ => {}
     }
 
     match S {
         S(()) => {}
-        //~^ ERROR this pattern has 1 field, but the corresponding struct
+        //~^ ERROR `S` does not name a tuple variant or a tuple struct
     }
 }
diff --git a/src/test/compile-fail/issue-32086.rs b/src/test/compile-fail/issue-32086.rs
new file mode 100644 (file)
index 0000000..926f581
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct S(u8);
+const C: S = S(10);
+
+fn main() {
+    let C(a) = S(11); //~ ERROR expected variant or struct, found constant `C`
+    let C(..) = S(11); //~ ERROR expected variant or struct, found constant `C`
+}
diff --git a/src/test/compile-fail/issue-32829.rs b/src/test/compile-fail/issue-32829.rs
new file mode 100644 (file)
index 0000000..9ac7088
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// error-pattern: calls in statics are limited
+
+static S : u64 = { { panic!("foo"); 0 } };
+
+fn main() {
+    println!("{:?}", S);
+}
diff --git a/src/test/compile-fail/issue-32950.rs b/src/test/compile-fail/issue-32950.rs
new file mode 100644 (file)
index 0000000..e8ca1c1
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(type_macros, concat_idents)]
+
+#[derive(Debug)] //~ NOTE in this expansion
+struct Baz<T>(
+    concat_idents!(Foo, Bar) //~ ERROR `derive` cannot be used on items with type macros
+);
+
+fn main() {}
diff --git a/src/test/compile-fail/issue-33293.rs b/src/test/compile-fail/issue-33293.rs
new file mode 100644 (file)
index 0000000..bed577b
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    match 0 {
+        aaa::bbb(_) => ()
+        //~^ ERROR failed to resolve. Use of undeclared type or module `aaa`
+    };
+}
diff --git a/src/test/compile-fail/issue-33571.rs b/src/test/compile-fail/issue-33571.rs
new file mode 100644 (file)
index 0000000..5dfc41c
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[derive(Clone,
+         Sync, //~ ERROR this unsafe trait should be implemented explicitly
+         Copy)]
+enum Foo {}
diff --git a/src/test/compile-fail/issue-33819.rs b/src/test/compile-fail/issue-33819.rs
new file mode 100644 (file)
index 0000000..9c9677c
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+fn main() {
+    let mut op = Some(2);
+    match op {
+        Some(ref v) => { let a = &mut v; },
+        //~^ ERROR:cannot borrow immutable
+        //~| use `ref mut v` here to make mutable
+        None => {},
+    }
+}
diff --git a/src/test/compile-fail/issue-33876.rs b/src/test/compile-fail/issue-33876.rs
new file mode 100644 (file)
index 0000000..d958907
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(reflect_marker)]
+
+use std::marker::Reflect;
+use std::any::Any;
+
+struct Foo;
+
+trait Bar {}
+
+impl Bar for Foo {}
+
+fn main() {
+    let any: &Any = &Bar; //~ ERROR E0425
+                          //~| HELP trait `Bar`
+    if any.is::<u32>() { println!("u32"); }
+}
diff --git a/src/test/compile-fail/issue-34028.rs b/src/test/compile-fail/issue-34028.rs
new file mode 100644 (file)
index 0000000..117ab18
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+macro_rules! m {
+    () => { #[cfg(any())] fn f() {} }
+}
+
+trait T {}
+impl T for () { m!(); }
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
diff --git a/src/test/compile-fail/issue-34047.rs b/src/test/compile-fail/issue-34047.rs
new file mode 100644 (file)
index 0000000..630694d
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+const C: u8 = 0; //~ NOTE a constant `C` is defined here
+
+fn main() {
+    match 1u8 {
+        mut C => {} //~ ERROR match bindings cannot shadow constants
+        //~^ NOTE cannot be named the same as a constant
+        _ => {}
+    }
+}
diff --git a/src/test/compile-fail/issue-34171.rs b/src/test/compile-fail/issue-34171.rs
new file mode 100644 (file)
index 0000000..30dd34a
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+macro_rules! null { ($i:tt) => {} }
+macro_rules! apply_null {
+    ($i:item) => { null! { $i } }
+}
+
+#[rustc_error]
+fn main() { //~ ERROR compilation successful
+    apply_null!(#[cfg(all())] fn f() {});
+}
diff --git a/src/test/compile-fail/issue-34194.rs b/src/test/compile-fail/issue-34194.rs
new file mode 100644 (file)
index 0000000..dd607eb
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(dead_code)]
+
+struct A {
+    a: &'static (),
+}
+
+static B: &'static A = &A { a: &() };
+static C: &'static A = &B;
+//~^ ERROR cannot refer to other statics by value
+
+fn main() {}
diff --git a/src/test/compile-fail/issue-34209.rs b/src/test/compile-fail/issue-34209.rs
new file mode 100644 (file)
index 0000000..a787970
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum S {
+    A,
+}
+
+fn bug(l: S) {
+    match l {
+        S::B{ } => { },
+        //~^ ERROR ambiguous associated type; specify the type using the syntax `<S as Trait>::B`
+        //~| ERROR `S::B` does not name a struct or a struct variant
+    }
+}
+
+fn main () {}
diff --git a/src/test/compile-fail/issue-34334.rs b/src/test/compile-fail/issue-34334.rs
new file mode 100644 (file)
index 0000000..ffcd052
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main () {
+    let sr: Vec<(u32, _, _) = vec![]; //~ ERROR expected one of `+`, `,`, or `>`, found `=`
+    let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect();
+    //~^ ERROR unresolved name `sr`
+}
diff --git a/src/test/compile-fail/issue-34349.rs b/src/test/compile-fail/issue-34349.rs
new file mode 100644 (file)
index 0000000..5917531
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This is a regression test for a problem encountered around upvar
+// inference and trait caching: in particular, we were entering a
+// temporary closure kind during inference, and then caching results
+// based on that temporary kind, which led to no error being reported
+// in this particular test.
+
+fn main() {
+    let inc = || {};
+    inc();
+
+    fn apply<F>(f: F) where F: Fn() {
+        f()
+    }
+
+    let mut farewell = "goodbye".to_owned();
+    let diary = || { //~ ERROR E0525
+        farewell.push_str("!!!");
+        println!("Then I screamed {}.", farewell);
+    };
+
+    apply(diary);
+}
diff --git a/src/test/compile-fail/issue-34418.rs b/src/test/compile-fail/issue-34418.rs
new file mode 100644 (file)
index 0000000..6bc0add
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+macro_rules! make_item {
+    () => { fn f() {} }
+}
+
+macro_rules! make_stmt {
+    () => { let x = 0; }
+}
+
+fn f() {
+    make_item! {}
+}
+
+fn g() {
+    make_stmt! {}
+}
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
diff --git a/src/test/compile-fail/issue-35044.rs b/src/test/compile-fail/issue-35044.rs
new file mode 100644 (file)
index 0000000..64b8151
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[derive(Debug, PartialEq)]
+enum Reg {
+    EAX,
+    EBX,
+    ECX,
+    EDX,
+    ESP,
+    EBP,
+    ISP,
+}
+
+fn string_to_reg(_s:&str) -> Reg {
+    match _s.as_ref() {
+        "EAX" => Reg::EAX,
+        "EBX" => Reg::EBX,
+        "ECX" => Reg::ECX,
+        "EDX" => Reg::EDX,
+        "EBP" => Reg::EBP,
+        "ESP" => Reg::ESP,
+        "ISP" => Reg::ISP, //~ NOTE split literal here
+        &_    => panic!("bla bla bla"), //~ ERROR see issue #35044
+    }
+}
+
+fn main() {
+    let vec = vec!["EAX", "EBX", "ECX", "EDX", "ESP", "EBP", "ISP"];
+    let mut iter = vec.iter();
+    println!("{:?}", string_to_reg(""));
+    println!("{:?}", string_to_reg(iter.next().unwrap()));
+    println!("{:?}", string_to_reg(iter.next().unwrap()));
+    println!("{:?}", string_to_reg(iter.next().unwrap()));
+    println!("{:?}", string_to_reg(iter.next().unwrap()));
+    println!("{:?}",  string_to_reg(iter.next().unwrap()));
+    println!("{:?}", string_to_reg(iter.next().unwrap()));
+    println!("{:?}",  string_to_reg(iter.next().unwrap()));
+}
index b37b8e237edda12eadef63d2e68acdf2313df46b..438d238b6fe6288c8c7202c6208dd42b2114ae30 100644 (file)
@@ -12,3 +12,4 @@
 
 fn foo(a: usize) {}
 fn main() { foo(5, 6) } //~ ERROR this function takes 1 parameter but 2 parameters were supplied
+//~^ NOTE the following parameter type was expected
index e5f091d873df9c1384207472e4f6061713bf5152..3a8ff12429ab374fc0d36f5376a374a63b48b958 100644 (file)
@@ -11,7 +11,7 @@
 
 fn main() {
     let z = match 3 {
-        x(1) => x(1) //~ ERROR unresolved enum variant
+        x(1) => x(1) //~ ERROR unresolved variant or struct `x`
         //~^ ERROR unresolved name `x`
     };
     assert!(z == 3);
diff --git a/src/test/compile-fail/label-static.rs b/src/test/compile-fail/label-static.rs
new file mode 100644 (file)
index 0000000..a0fb25e
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    'static: loop { //~ ERROR invalid label name `'static`
+        break 'static //~ ERROR invalid label name `'static`
+    }
+}
index e0ea1ed74340783001fd75971980b68550f2e7bd..6b22d434804ffecafc2b56e71c8140e1ae6e5428 100644 (file)
@@ -14,7 +14,7 @@ use std::marker::PhantomData;
 
 struct Bar<'x, 'y, 'z> { bar: &'y i32, baz: i32, marker: PhantomData<(&'x(),&'y(),&'z())> }
 fn bar1<'a>(x: &Bar) -> (&'a i32, &'a i32, &'a i32) {
-    //~^ HELP: consider using an explicit lifetime parameter as shown: fn bar1<'a>(x: &'a Bar) -> (&'a i32, &'a i32, &'a i32)
+    //~^ HELP consider using an explicit lifetime parameter as shown: fn bar1<'b, 'c, 'a>(x: &'a Bar<'b, 'a, 'c>) -> (&'a i32, &'a i32, &'a i32)
     (x.bar, &x.baz, &x.baz)
     //~^ ERROR E0312
     //~| ERROR cannot infer
index 73d89beb2202f84369e048588a346945b6cd2179..e34a3c4569d0a4c451d053ff13837bd0e9c7abad 100644 (file)
@@ -49,7 +49,7 @@ struct Baz<'x> {
 
 impl<'a> Baz<'a> {
     fn baz2<'b>(&self, x: &isize) -> (&'b isize, &'b isize) {
-        //~^ HELP consider using an explicit lifetime parameter as shown: fn baz2<'b>(&self, x: &'b isize) -> (&'a isize, &'a isize)
+        //~^ HELP consider using an explicit lifetime parameter as shown: fn baz2<'b>(&self, x: &'a isize) -> (&'a isize, &'a isize)
         (self.bar, x) //~ ERROR E0312
         //~^ ERROR E0312
     }
diff --git a/src/test/compile-fail/lifetime-underscore.rs b/src/test/compile-fail/lifetime-underscore.rs
new file mode 100644 (file)
index 0000000..102d357
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![deny(lifetime_underscore)]
+
+fn _f<'_>() //~ ERROR invalid lifetime name `'_`
+//~^ WARN this was previously accepted
+    -> &'_ u8 //~ ERROR invalid lifetime name `'_`
+    //~^ WARN this was previously accepted
+{
+    panic!();
+}
+
+fn main() {
+    '_: loop { //~ ERROR invalid label name `'_`
+    //~^ WARN this was previously accepted
+        break '_ //~ ERROR invalid label name `'_`
+        //~^ WARN this was previously accepted
+    }
+}
index c2277c3e6d8cdaba024839c618c0d1a59d2cf4ac..9d6da2a53a2227c16b3b9792a3744405b14f27c1 100644 (file)
@@ -21,11 +21,11 @@ macro_rules! myprint {
 }
 
 macro_rules! myprintln {
-    ($fmt:expr) => (myprint!(concat!($fmt, "\n"))); //~ NOTE in this expansion of myprint!
-                                                    //~^ NOTE in this expansion of concat!
+    ($fmt:expr) => (myprint!(concat!($fmt, "\n"))); //~ ERROR invalid reference to argument `0`
+                                                    //~| NOTE in this expansion of concat!
+                                                    //~| NOTE in this expansion of myprint!
 }
 
 fn main() {
-    myprintln!("{}"); //~ ERROR invalid reference to argument `0`
-                      //~^ NOTE in this expansion of
+    myprintln!("{}"); //~ NOTE in this expansion of
 }
index 817b675aedfe8f3dc0707506ad9735ddbf870e63..a69188da58d1667686ab896b9d0d005805b76337 100644 (file)
@@ -8,12 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Check that we report errors at macro definition, not expansion.
+#![feature(type_macros)]
 
 macro_rules! foo {
     ($a:expr) => $a; //~ ERROR macro rhs must be delimited
 }
 
 fn main() {
-    foo!(0);
+    foo!(0); // Check that we report errors at macro definition, not expansion.
+
+    let _: cfg!(foo) = (); //~ ERROR non-type macro in type position
+    derive!(); //~ ERROR `derive` can only be used in attributes
 }
diff --git a/src/test/compile-fail/macro-expanded-include/file.txt b/src/test/compile-fail/macro-expanded-include/file.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/test/compile-fail/macro-expanded-include/foo/mod.rs b/src/test/compile-fail/macro-expanded-include/foo/mod.rs
new file mode 100644 (file)
index 0000000..888bdf5
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-test
+
+macro_rules! m {
+    () => { include!("file.txt"); }
+}
+
+macro_rules! n {
+    () => { unsafe { asm!(include_str!("file.txt")); } }
+}
diff --git a/src/test/compile-fail/macro-expanded-include/test.rs b/src/test/compile-fail/macro-expanded-include/test.rs
new file mode 100644 (file)
index 0000000..e1e85dd
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(asm, rustc_attrs)]
+#![allow(unused)]
+
+#[macro_use]
+mod foo;
+
+m!();
+fn f() { n!(); }
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
index f985340c524108a32c12526bb87b910a9c4d6a90..001bc42274b4daea014bfc5a56f584e27f9dff19 100644 (file)
@@ -12,9 +12,9 @@
 
 // FOLLOW(pat) = {FatArrow, Comma, Eq, Or, Ident(if), Ident(in)}
 macro_rules! follow_pat {
-    ($p:pat ()) => {};       //~WARN  `$p:pat` is followed by `(`
-    ($p:pat []) => {};       //~WARN  `$p:pat` is followed by `[`
-    ($p:pat {}) => {};       //~WARN  `$p:pat` is followed by `{`
+    ($p:pat ()) => {};       //~ERROR  `$p:pat` is followed by `(`
+    ($p:pat []) => {};       //~ERROR  `$p:pat` is followed by `[`
+    ($p:pat {}) => {};       //~ERROR  `$p:pat` is followed by `{`
     ($p:pat :) => {};        //~ERROR `$p:pat` is followed by `:`
     ($p:pat >) => {};        //~ERROR `$p:pat` is followed by `>`
     ($p:pat +) => {};        //~ERROR `$p:pat` is followed by `+`
@@ -32,9 +32,9 @@ macro_rules! follow_pat {
 }
 // FOLLOW(expr) = {FatArrow, Comma, Semicolon}
 macro_rules! follow_expr {
-    ($e:expr ()) => {};       //~WARN  `$e:expr` is followed by `(`
-    ($e:expr []) => {};       //~WARN  `$e:expr` is followed by `[`
-    ($e:expr {}) => {};       //~WARN  `$e:expr` is followed by `{`
+    ($e:expr ()) => {};       //~ERROR  `$e:expr` is followed by `(`
+    ($e:expr []) => {};       //~ERROR  `$e:expr` is followed by `[`
+    ($e:expr {}) => {};       //~ERROR  `$e:expr` is followed by `{`
     ($e:expr =) => {};        //~ERROR `$e:expr` is followed by `=`
     ($e:expr |) => {};        //~ERROR `$e:expr` is followed by `|`
     ($e:expr :) => {};        //~ERROR `$e:expr` is followed by `:`
@@ -57,7 +57,7 @@ macro_rules! follow_expr {
 // FOLLOW(ty) = {OpenDelim(Brace), Comma, FatArrow, Colon, Eq, Gt, Semi, Or,
 //               Ident(as), Ident(where), OpenDelim(Bracket), Nonterminal(Block)}
 macro_rules! follow_ty {
-    ($t:ty ()) => {};       //~WARN  `$t:ty` is followed by `(`
+    ($t:ty ()) => {};       //~ERROR  `$t:ty` is followed by `(`
     ($t:ty []) => {};       // ok (RFC 1462)
     ($t:ty +) => {};        //~ERROR `$t:ty` is followed by `+`
     ($t:ty ident) => {};    //~ERROR `$t:ty` is followed by `ident`
@@ -75,9 +75,9 @@ macro_rules! follow_ty {
 }
 // FOLLOW(stmt) = FOLLOW(expr)
 macro_rules! follow_stmt {
-    ($s:stmt ()) => {};       //~WARN  `$s:stmt` is followed by `(`
-    ($s:stmt []) => {};       //~WARN  `$s:stmt` is followed by `[`
-    ($s:stmt {}) => {};       //~WARN  `$s:stmt` is followed by `{`
+    ($s:stmt ()) => {};       //~ERROR  `$s:stmt` is followed by `(`
+    ($s:stmt []) => {};       //~ERROR  `$s:stmt` is followed by `[`
+    ($s:stmt {}) => {};       //~ERROR  `$s:stmt` is followed by `{`
     ($s:stmt =) => {};        //~ERROR `$s:stmt` is followed by `=`
     ($s:stmt |) => {};        //~ERROR `$s:stmt` is followed by `|`
     ($s:stmt :) => {};        //~ERROR `$s:stmt` is followed by `:`
@@ -99,7 +99,7 @@ macro_rules! follow_stmt {
 }
 // FOLLOW(path) = FOLLOW(ty)
 macro_rules! follow_path {
-    ($p:path ()) => {};       //~WARN  `$p:path` is followed by `(`
+    ($p:path ()) => {};       //~ERROR  `$p:path` is followed by `(`
     ($p:path []) => {};       // ok (RFC 1462)
     ($p:path +) => {};        //~ERROR `$p:path` is followed by `+`
     ($p:path ident) => {};    //~ERROR `$p:path` is followed by `ident`
diff --git a/src/test/compile-fail/macro-invalid-fragment-spec.rs b/src/test/compile-fail/macro-invalid-fragment-spec.rs
new file mode 100644 (file)
index 0000000..ca6cd66
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! foo(
+    ($x:foo) => ()
+    //~^ ERROR invalid fragment specifier
+    //~| HELP valid fragment specifiers are
+);
+
+fn main() {
+    foo!(foo);
+}
diff --git a/src/test/compile-fail/macro-missing-delimiters.rs b/src/test/compile-fail/macro-missing-delimiters.rs
new file mode 100644 (file)
index 0000000..eed2a20
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! baz(
+    baz => () //~ ERROR invalid macro matcher;
+);
+
+fn main() {
+    baz!(baz);
+}
diff --git a/src/test/compile-fail/macro-use-scope.rs b/src/test/compile-fail/macro-use-scope.rs
new file mode 100644 (file)
index 0000000..5256396
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:two_macros.rs
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+fn f() {
+    let _ = macro_one!();
+}
+#[macro_use(macro_one)] // Check that this macro is usable in the above function
+extern crate two_macros;
+
+macro_rules! m { () => {
+    fn g() {
+        macro_two!();
+    }
+    #[macro_use(macro_two)] // Check that this macro is usable in the above function
+    extern crate two_macros as _two_macros;
+} }
+m!();
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
index 95250e36b8685a64ce3fcf84082d8ddc902d82bc..408bb15ba28cdd165cbf9246f2bdf1f891760b4e 100644 (file)
@@ -8,8 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:expected macro name without module separators
-
 fn main() {
-    globnar::brotz!();
+    globnar::brotz!(); //~ ERROR expected macro name without module separators
+    ::foo!(); //~ ERROR expected macro name without module separators
+    foo::<T>!(); //~ ERROR expected macro name without module separators
 }
index 42a0f41dd97c3921ffdc50b02e8e461eac9fae46..723e936212a5b76efa3a82765c1ba03df7b906c8 100644 (file)
@@ -32,7 +32,6 @@ fn main() {
 
     foo::blah!(); //~ ERROR
 
-    format!(); //~ ERROR
     format!(invalid); //~ ERROR
 
     include!(invalid); //~ ERROR
index 88e19af2eea07514deb9b64789f24c8531550e37..5d81e21f05684e1f816522c214a289bc287458b7 100644 (file)
@@ -9,9 +9,9 @@
 // except according to those terms.
 
 macro_rules! my_precioooous {
-    $($t:tt)* => (1);
+    $($t:tt)* => (1); //~ ERROR invalid macro matcher
 }
 
 fn main() {
-    my_precioooous!(); //~ ERROR malformed macro lhs
+    my_precioooous!();
 }
index e63ddf6c7fd9b6ff210acf15b07cfecfcf459221..a4ba93ea1733311c7ccac7f0e4516c7f4510da1a 100644 (file)
@@ -20,7 +20,7 @@ fn main() {
           color::rgb(_, _, _) => { }
           color::cmyk(_, _, _, _) => { }
           color::no_color(_) => { }
-          //~^ ERROR this pattern has 1 field, but the corresponding variant has no fields
+          //~^ ERROR `color::no_color` does not name a tuple variant or a tuple struct
         }
     }
 }
index 2831499c73d8732e5dd676ca3360631524e4f145..375d855d1fd31e6edae51647b965f13dec2e370f 100644 (file)
@@ -13,9 +13,6 @@
 fn main() {
     match () {
         [()] => { }
-        //~^ ERROR mismatched types
-        //~| expected type `()`
-        //~| found type `&[_]`
-        //~| expected (), found &-ptr
+        //~^ ERROR expected an array or slice, found `()`
     }
 }
index ef75213d34b85109fa53a7e76d873d9b063f007d..3ac4958e7db0f6425b5a5d170574d967e1849bdf 100644 (file)
 
 fn main() {
     match "foo".to_string() {
-        ['f', 'o', ..] => {} //~ ERROR mismatched types
+        ['f', 'o', ..] => {}
+        //~^ ERROR expected an array or slice, found `std::string::String`
         _ => { }
-    }
+    };
+
+    match &[0, 1, 2] {
+        [..] => {} //~ ERROR expected an array or slice, found `&[_; 3]`
+    };
+
+    match &[0, 1, 2] {
+        &[..] => {} // ok
+    };
+
+    match [0, 1, 2] {
+        [0] => {}, //~ ERROR pattern requires
+
+        [0, 1, x..] => {
+            let a: [_; 1] = x;
+        }
+        [0, 1, 2, 3, x..] => {} //~ ERROR pattern requires
+    };
+
+    match does_not_exist { //~ ERROR unresolved name
+        [] => {}
+    };
+}
+
+fn another_fn_to_avoid_suppression() {
+    match Default::default()
+    {
+        [] => {}  //~ ERROR the type of this value
+    };
 }
index 48b70b4bda08efc31983e0bffaee437639cb5ae3..57e3a58b5660e27267d66d9af9f9643d6dd2542b 100644 (file)
@@ -13,7 +13,7 @@
 fn main() {
     let x: Vec<(isize, isize)> = Vec::new();
     let x: &[(isize, isize)] = &x;
-    match x {
+    match *x {
         [a, (2, 3), _] => (),
         [(1, 2), (2, 3), b] => (), //~ ERROR unreachable pattern
         _ => ()
@@ -23,7 +23,7 @@ fn main() {
                               "bar".to_string(),
                               "baz".to_string()];
     let x: &[String] = &x;
-    match x {
+    match *x {
         [a, _, _, ..] => { println!("{}", a); }
         [_, _, _, _, _] => { } //~ ERROR unreachable pattern
         _ => { }
@@ -31,8 +31,8 @@ fn main() {
 
     let x: Vec<char> = vec!('a', 'b', 'c');
     let x: &[char] = &x;
-    match x {
-        ['a', 'b', 'c', _tail..] => {}
+    match *x {
+        ['a', 'b', 'c', ref _tail..] => {}
         ['a', 'b', 'c'] => {} //~ ERROR unreachable pattern
         _ => {}
     }
index 3434cf96fce9463c15822e240ff5fd61a73d1a88..212c09364cf4c027c711e40e4f51f277052772bc 100644 (file)
@@ -21,10 +21,13 @@ fn main() {
     let x = Foo;
     x.zero(0)   //~ ERROR this function takes 0 parameters but 1 parameter was supplied
      .one()     //~ ERROR this function takes 1 parameter but 0 parameters were supplied
+     //~^ NOTE the following parameter type was expected
      .two(0);   //~ ERROR this function takes 2 parameters but 1 parameter was supplied
+     //~^ NOTE the following parameter types were expected
 
     let y = Foo;
     y.zero()
      .take()    //~ ERROR no method named `take` found for type `Foo` in the current scope
+     //~^ NOTE the method `take` exists but the following trait bounds were not satisfied
      .one(0);
 }
index 0df824e7f535bd4b83185bad9b2ebe32c4ff1a47..3ae792f9c0f3783985ccbcc0e1d44312ae4d1753 100644 (file)
@@ -19,6 +19,6 @@ impl MyTrait for Foo {}
 fn main() {
     match 0u32 {
         <Foo as MyTrait>::trait_bar => {}
-        //~^ ERROR `trait_bar` is not an associated const
+        //~^ ERROR expected variant, struct or constant, found method `trait_bar`
     }
 }
diff --git a/src/test/compile-fail/mir-dataflow/README.md b/src/test/compile-fail/mir-dataflow/README.md
new file mode 100644 (file)
index 0000000..a3ab14b
--- /dev/null
@@ -0,0 +1,53 @@
+This directory contains unit tests for the MIR-based dataflow
+analysis.
+
+These unit tests check the dataflow analysis by embedding calls to a
+special `rustc_peek` intrinsic within the code, in tandem with an
+attribute `#[rustc_mir(rustc_peek_maybe_init)]` (\*). With that
+attribute in place, `rustc_peek` calls are a signal to the compiler to
+lookup the computed dataflow state for the Lvalue corresponding to the
+argument expression being fed to `rustc_peek`. If the dataflow state
+for that Lvalue is a 1-bit at that point in the control flow, then no
+error is emitted by the compiler at that point; if it is a 0-bit, then
+that invocation of `rustc_peek` will emit an error with the message
+"rustc_peek: bit not set".
+
+(\*): Or `#[rustc_mir(rustc_peek_maybe_uninit)]`, and perhaps other
+variants in the future.
+
+The end effect is that one can write unit tests for MIR dataflow that
+perform simple-queries of the computed dataflow state, and the tests
+should be able to be robust in the face of changes to how MIR is
+represented or constructed.
+
+----
+
+Sometimes understanding the dataflow results is difficult without
+looking at the actual MIR control-flow graph being processed with the
+corresponding GEN and KILL sets.
+
+For a graphviz-rendering with dataflow annotations, add the attribute
+`#[rustc_mir(borrowck_graphviz_postflow="/path/to/suffix.dot")]` to
+the function in question. (You can change the content of
+`"suffix.dot"` to control the filenames used for the output). This
+will generate a separate file for each dataflow analysis, adding a
+prefix (corresponding to the name of the analysis) to the filename in
+each generated output path.
+
+ * For example, the above attribute will currently cause two files to
+   be generated: `/path/to/maybe_init_suffix.dot` and
+   `/path/to/maybe_uninit_suffix.dot`.
+
+ * The generated `.dot` file shows both the computed dataflow results
+   on *entry* to each block, as well as the gen- and kill-sets that
+   were so-called "transfer functions" summarizing the effect of each
+   basic block.
+
+ * (In addition to the `borrowck_graphviz_postflow` attribute-key
+   noted above, there is also `borrowck_graphviz_preflow`; it has the
+   same interface and generates the same set of files, but it renders
+   the dataflow state after building the gen- and kill-sets but
+   *before* running the dataflow analysis itself, so each entry-set is
+   just the initial default state for that dataflow analysis. This is
+   less useful for understanding the error message output in these
+   tests.)
diff --git a/src/test/compile-fail/mir-dataflow/def-inits-1.rs b/src/test/compile-fail/mir-dataflow/def-inits-1.rs
new file mode 100644 (file)
index 0000000..a133ddc
--- /dev/null
@@ -0,0 +1,63 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// General test of maybe_uninits state computed by MIR dataflow.
+
+#![feature(rustc_attrs)]
+#![feature(stmt_expr_attributes)]
+
+use std::intrinsics::rustc_peek;
+use std::mem::{drop, replace};
+
+struct S(i32);
+
+#[rustc_mir_borrowck]
+#[rustc_mir(rustc_peek_definite_init,stop_after_dataflow)]
+fn foo(test: bool, x: &mut S, y: S, mut z: S) -> S {
+    let ret;
+    // `ret` starts off uninitialized
+    unsafe { rustc_peek(&ret); }  //~ ERROR rustc_peek: bit not set
+
+    // All function formal parameters start off initialized.
+
+    unsafe { rustc_peek(&x) };
+    unsafe { rustc_peek(&y) };
+    unsafe { rustc_peek(&z) };
+
+    ret = if test {
+        ::std::mem::replace(x, y)
+    } else {
+        z = y;
+        z
+    };
+
+    // `z` may be uninitialized here.
+    unsafe { rustc_peek(&z); } //~ ERROR rustc_peek: bit not set
+
+    // `y` is definitely uninitialized here.
+    unsafe { rustc_peek(&y); } //~ ERROR rustc_peek: bit not set
+
+    // `x` is still (definitely) initialized (replace above is a reborrow).
+    unsafe { rustc_peek(&x); }
+
+    ::std::mem::drop(x);
+
+    // `x` is *definitely* uninitialized here
+    unsafe { rustc_peek(&x); } //~ ERROR rustc_peek: bit not set
+
+    // `ret` is now definitely initialized (via `if` above).
+    unsafe { rustc_peek(&ret); }
+
+    ret
+}
+fn main() {
+    foo(true, &mut S(13), S(14), S(15));
+    foo(false, &mut S(13), S(14), S(15));
+}
diff --git a/src/test/compile-fail/mir-dataflow/inits-1.rs b/src/test/compile-fail/mir-dataflow/inits-1.rs
new file mode 100644 (file)
index 0000000..9496880
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// General test of maybe_inits state computed by MIR dataflow.
+
+#![feature(rustc_attrs)]
+#![feature(stmt_expr_attributes)]
+
+use std::intrinsics::rustc_peek;
+use std::mem::{drop, replace};
+
+struct S(i32);
+
+#[rustc_mir_borrowck]
+#[rustc_mir(rustc_peek_maybe_init,stop_after_dataflow)]
+fn foo(test: bool, x: &mut S, y: S, mut z: S) -> S {
+    let ret;
+    // `ret` starts off uninitialized, so we get an error report here.
+    unsafe { rustc_peek(&ret); }  //~ ERROR rustc_peek: bit not set
+
+    // All function formal parameters start off initialized.
+
+    unsafe { rustc_peek(&x) };
+    unsafe { rustc_peek(&y) };
+    unsafe { rustc_peek(&z) };
+
+    ret = if test {
+        ::std::mem::replace(x, y)
+    } else {
+        z = y;
+        z
+    };
+
+
+    // `z` may be initialized here.
+    unsafe { rustc_peek(&z); }
+
+    // `y` is definitely uninitialized here.
+    unsafe { rustc_peek(&y); }  //~ ERROR rustc_peek: bit not set
+
+    // `x` is still (definitely) initialized (replace above is a reborrow).
+    unsafe { rustc_peek(&x); }
+
+    ::std::mem::drop(x);
+
+    // `x` is *definitely* uninitialized here
+    unsafe { rustc_peek(&x); } //~ ERROR rustc_peek: bit not set
+
+    // `ret` is now definitely initialized (via `if` above).
+    unsafe { rustc_peek(&ret); }
+
+    ret
+}
+
+fn main() {
+    foo(true, &mut S(13), S(14), S(15));
+    foo(false, &mut S(13), S(14), S(15));
+}
diff --git a/src/test/compile-fail/mir-dataflow/uninits-1.rs b/src/test/compile-fail/mir-dataflow/uninits-1.rs
new file mode 100644 (file)
index 0000000..c13daae
--- /dev/null
@@ -0,0 +1,63 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// General test of maybe_uninits state computed by MIR dataflow.
+
+#![feature(rustc_attrs)]
+#![feature(stmt_expr_attributes)]
+
+use std::intrinsics::rustc_peek;
+use std::mem::{drop, replace};
+
+struct S(i32);
+
+#[rustc_mir_borrowck]
+#[rustc_mir(rustc_peek_maybe_uninit,stop_after_dataflow)]
+fn foo(test: bool, x: &mut S, y: S, mut z: S) -> S {
+    let ret;
+    // `ret` starts off uninitialized
+    unsafe { rustc_peek(&ret); }
+
+    // All function formal parameters start off initialized.
+
+    unsafe { rustc_peek(&x) }; //~ ERROR rustc_peek: bit not set
+    unsafe { rustc_peek(&y) }; //~ ERROR rustc_peek: bit not set
+    unsafe { rustc_peek(&z) }; //~ ERROR rustc_peek: bit not set
+
+    ret = if test {
+        ::std::mem::replace(x, y)
+    } else {
+        z = y;
+        z
+    };
+
+    // `z` may be uninitialized here.
+    unsafe { rustc_peek(&z); }
+
+    // `y` is definitely uninitialized here.
+    unsafe { rustc_peek(&y); }
+
+    // `x` is still (definitely) initialized (replace above is a reborrow).
+    unsafe { rustc_peek(&x); } //~ ERROR rustc_peek: bit not set
+
+    ::std::mem::drop(x);
+
+    // `x` is *definitely* uninitialized here
+    unsafe { rustc_peek(&x); }
+
+    // `ret` is now definitely initialized (via `if` above).
+    unsafe { rustc_peek(&ret); } //~ ERROR rustc_peek: bit not set
+
+    ret
+}
+fn main() {
+    foo(true, &mut S(13), S(14), S(15));
+    foo(false, &mut S(13), S(14), S(15));
+}
diff --git a/src/test/compile-fail/mir-dataflow/uninits-2.rs b/src/test/compile-fail/mir-dataflow/uninits-2.rs
new file mode 100644 (file)
index 0000000..e0bf425
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// General test of maybe_uninits state computed by MIR dataflow.
+
+#![feature(rustc_attrs)]
+#![feature(stmt_expr_attributes)]
+
+use std::intrinsics::rustc_peek;
+use std::mem::{drop, replace};
+
+struct S(i32);
+
+#[rustc_mir_borrowck]
+#[rustc_mir(rustc_peek_maybe_uninit,stop_after_dataflow)]
+fn foo(x: &mut S) {
+    // `x` is initialized here, so maybe-uninit bit is 0.
+
+    unsafe { *rustc_peek(&x) }; //~ ERROR rustc_peek: bit not set
+
+    ::std::mem::drop(x);
+
+    // `x` definitely uninitialized here, so maybe-uninit bit is 1.
+    unsafe { rustc_peek(&x) };
+}
+fn main() {
+    foo(&mut S(13));
+    foo(&mut S(13));
+}
index 662bb7bfe57f05bdbe3a24dc122a6142827081dd..2e2d53c4d40b6400854c1bcc3c15a1296f06ffdb 100644 (file)
@@ -8,10 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:cannot be named the same
 use std::option::*;
 
 fn main() {
-  let None: isize = 42;
+  let None: isize = 42; //~ ERROR let bindings cannot shadow variants
   log(debug, None);
+  //~^ ERROR unresolved name `debug`
+  //~| ERROR unresolved name `log`
 }
diff --git a/src/test/compile-fail/nested-cfg-attrs.rs b/src/test/compile-fail/nested-cfg-attrs.rs
new file mode 100644 (file)
index 0000000..6010b1e
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[cfg_attr(all(), cfg_attr(all(), cfg(foo)))]
+fn f() {}
+
+fn main() { f() } //~ ERROR unresolved name `f`
diff --git a/src/test/compile-fail/no-warn-on-field-replace-issue-34101.rs b/src/test/compile-fail/no-warn-on-field-replace-issue-34101.rs
new file mode 100644 (file)
index 0000000..2940b89
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Issue 34101: Circa 2016-06-05, `fn inline` below issued an
+// erroneous warning from the elaborate_drops pass about moving out of
+// a field in `Foo`, which has a destructor (and thus cannot have
+// content moved out of it). The reason that the warning is erroneous
+// in this case is that we are doing a *replace*, not a move, of the
+// content in question, and it is okay to replace fields within `Foo`.
+//
+// Another more subtle problem was that the elaborate_drops was
+// creating a separate drop flag for that internally replaced content,
+// even though the compiler should enforce an invariant that any drop
+// flag for such subcontent of `Foo` will always have the same value
+// as the drop flag for `Foo` itself.
+//
+// This test is structured in a funny way; we cannot test for emission
+// of the warning in question via the lint system, and therefore
+// `#![deny(warnings)]` does nothing to detect it.
+//
+// So instead we use `#[rustc_error]` and put the test into
+// `compile_fail`, where the emitted warning *will* be caught.
+
+#![feature(rustc_attrs)]
+
+struct Foo(String);
+
+impl Drop for Foo {
+    fn drop(&mut self) {}
+}
+
+fn inline() {
+    // (dummy variable so `f` gets assigned `var1` in MIR for both fn's)
+    let _s = ();
+    let mut f = Foo(String::from("foo"));
+    f.0 = String::from("bar");
+}
+
+fn outline() {
+    let _s = String::from("foo");
+    let mut f = Foo(_s);
+    f.0 = String::from("bar");
+}
+
+#[rustc_error]
+fn main() { //~ ERROR compilation successful
+    inline();
+    outline();
+}
index ad2b8c400e57608c0e2613223c846e1d3ae590f8..1d524217a12a29519653256f9a8785e64d334dbf 100644 (file)
@@ -14,11 +14,11 @@ enum t { a(u), b }
 enum u { c, d }
 
 fn match_nested_vecs<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
-    match (l1, l2) { //~ ERROR non-exhaustive patterns: `(Some([]), Err(_))` not covered
-        (Some([]), Ok([])) => "Some(empty), Ok(empty)",
-        (Some([_, ..]), Ok(_)) | (Some([_, ..]), Err(())) => "Some(non-empty), any",
-        (None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)",
-        (None, Ok([_, _, ..])) => "None, Ok(at least two elements)"
+    match (l1, l2) { //~ ERROR non-exhaustive patterns: `(Some(&[]), Err(_))` not covered
+        (Some(&[]), Ok(&[])) => "Some(empty), Ok(empty)",
+        (Some(&[_, ..]), Ok(_)) | (Some(&[_, ..]), Err(())) => "Some(non-empty), any",
+        (None, Ok(&[])) | (None, Err(())) | (None, Ok(&[_])) => "None, Ok(less than one element)",
+        (None, Ok(&[_, _, ..])) => "None, Ok(at least two elements)"
     }
 }
 
index b9749c2696e32adf505b7df91a53df19dba6bafd..017baacc9d329b771f7e40f295acbbfafeb03a24 100644 (file)
@@ -39,20 +39,20 @@ fn main() {
     }
     let vec = vec!(Some(42), None, Some(21));
     let vec: &[Option<isize>] = &vec;
-    match vec { //~ ERROR non-exhaustive patterns: `[]` not covered
-        [Some(..), None, tail..] => {}
-        [Some(..), Some(..), tail..] => {}
+    match *vec { //~ ERROR non-exhaustive patterns: `[]` not covered
+        [Some(..), None, ref tail..] => {}
+        [Some(..), Some(..), ref tail..] => {}
         [None] => {}
     }
     let vec = vec!(1);
     let vec: &[isize] = &vec;
-    match vec {
-        [_, tail..] => (),
+    match *vec {
+        [_, ref tail..] => (),
         [] => ()
     }
     let vec = vec!(0.5f32);
     let vec: &[f32] = &vec;
-    match vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _]` not covered
+    match *vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _]` not covered
         [0.1, 0.2, 0.3] => (),
         [0.1, 0.2] => (),
         [0.1] => (),
@@ -60,11 +60,11 @@ fn main() {
     }
     let vec = vec!(Some(42), None, Some(21));
     let vec: &[Option<isize>] = &vec;
-    match vec {
-        [Some(..), None, tail..] => {}
-        [Some(..), Some(..), tail..] => {}
-        [None, None, tail..] => {}
-        [None, Some(..), tail..] => {}
+    match *vec {
+        [Some(..), None, ref tail..] => {}
+        [Some(..), Some(..), ref tail..] => {}
+        [None, None, ref tail..] => {}
+        [None, Some(..), ref tail..] => {}
         [Some(_)] => {}
         [None] => {}
         [] => {}
index b986878f78396ae823bf9e3fac40e93486dd65f2..0b12a9acbcb9e9d56de662b81d4234bda7fc35a7 100644 (file)
@@ -80,7 +80,7 @@ enum Enum {
 
 fn vectors_with_nested_enums() {
     let x: &'static [Enum] = &[Enum::First, Enum::Second(false)];
-    match x {
+    match *x {
     //~^ ERROR non-exhaustive patterns: `[Second(true), Second(false)]` not covered
         [] => (),
         [_] => (),
@@ -88,7 +88,7 @@ fn vectors_with_nested_enums() {
         [Enum::Second(true), Enum::First] => (),
         [Enum::Second(true), Enum::Second(true)] => (),
         [Enum::Second(false), _] => (),
-        [_, _, tail.., _] => ()
+        [_, _, ref tail.., _] => ()
     }
 }
 
index 0b7b33de42193850bd41802a56d2b5d3f2c0aeb6..50240b475578c6a66f903e1f8cee08cfc476951f 100644 (file)
@@ -60,22 +60,22 @@ unsafe fn test_Foo_cxchg(p: &mut Foo, v: Foo) {
 
 unsafe fn test_Bar_load(p: &mut Bar, v: Bar) {
     intrinsics::atomic_load(p);
-    //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static`
+    //~^ ERROR expected basic integer type, found `&std::ops::Fn()`
 }
 
 unsafe fn test_Bar_store(p: &mut Bar, v: Bar) {
     intrinsics::atomic_store(p, v);
-    //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static`
+    //~^ ERROR expected basic integer type, found `&std::ops::Fn()`
 }
 
 unsafe fn test_Bar_xchg(p: &mut Bar, v: Bar) {
     intrinsics::atomic_xchg(p, v);
-    //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static`
+    //~^ ERROR expected basic integer type, found `&std::ops::Fn()`
 }
 
 unsafe fn test_Bar_cxchg(p: &mut Bar, v: Bar) {
     intrinsics::atomic_cxchg(p, v, v);
-    //~^ ERROR expected basic integer type, found `&'static std::ops::Fn() + 'static`
+    //~^ ERROR expected basic integer type, found `&std::ops::Fn()`
 }
 
 unsafe fn test_Quux_load(p: &mut Quux, v: Quux) {
index c952906e5e89ae2d41fb5d8854288858dfc015a5..1f5a54477dd6d0fa83f4597179b819deec94c5db 100644 (file)
@@ -19,4 +19,5 @@ fn foo(a: isize, b: isize, c: isize, d:isize) {
 fn main() {
   foo(1, 2, 3);
   //~^ ERROR this function takes 4 parameters but 3
+  //~^^ NOTE the following parameter types were expected
 }
index 58c0791b84ec5b25c05e30dc063c1a6a444e9b4c..7107211fc914b47479b2569515fee614191cca08 100644 (file)
@@ -9,13 +9,12 @@
 // except according to those terms.
 
 #![allow(dead_code)]
-#![feature(recover)]
 
-use std::panic::RecoverSafe;
+use std::panic::UnwindSafe;
 use std::rc::Rc;
 use std::cell::RefCell;
 
-fn assert<T: RecoverSafe + ?Sized>() {}
+fn assert<T: UnwindSafe + ?Sized>() {}
 
 fn main() {
     assert::<Rc<RefCell<i32>>>();
index 481ffb802812a02f30718384c32fa9b4f6690d57..76c34e4dc0b448613ddaef1d2041ad8bc15f18dd 100644 (file)
@@ -9,13 +9,12 @@
 // except according to those terms.
 
 #![allow(dead_code)]
-#![feature(recover)]
 
-use std::panic::RecoverSafe;
+use std::panic::UnwindSafe;
 use std::sync::Arc;
 use std::cell::RefCell;
 
-fn assert<T: RecoverSafe + ?Sized>() {}
+fn assert<T: UnwindSafe + ?Sized>() {}
 
 fn main() {
     assert::<Arc<RefCell<i32>>>();
index 47302d3af78b2909d8159a4d3fb5dab267267e23..177a43e2a7f71650a74955496b37f75cfc1b508f 100644 (file)
@@ -9,12 +9,11 @@
 // except according to those terms.
 
 #![allow(dead_code)]
-#![feature(recover)]
 
-use std::panic::RecoverSafe;
+use std::panic::UnwindSafe;
 use std::cell::RefCell;
 
-fn assert<T: RecoverSafe + ?Sized>() {}
+fn assert<T: UnwindSafe + ?Sized>() {}
 
 fn main() {
     assert::<&RefCell<i32>>();
index 0301c8dd935c7fe71876b226c9d18041028c5959..627a0fe78cf06ae9bb8cb6c56fdbd637e4eb27e6 100644 (file)
@@ -9,12 +9,11 @@
 // except according to those terms.
 
 #![allow(dead_code)]
-#![feature(recover)]
 
-use std::panic::RecoverSafe;
+use std::panic::UnwindSafe;
 use std::cell::UnsafeCell;
 
-fn assert<T: RecoverSafe + ?Sized>() {}
+fn assert<T: UnwindSafe + ?Sized>() {}
 
 fn main() {
     assert::<*const UnsafeCell<i32>>(); //~ ERROR E0277
index fe13b0a75c9eb18dd7f7de6ad99b7e42c74e7543..f03e1d545a8083786f324118f27ebc25eb9f9a94 100644 (file)
@@ -9,12 +9,11 @@
 // except according to those terms.
 
 #![allow(dead_code)]
-#![feature(recover)]
 
-use std::panic::RecoverSafe;
+use std::panic::UnwindSafe;
 use std::cell::RefCell;
 
-fn assert<T: RecoverSafe + ?Sized>() {}
+fn assert<T: UnwindSafe + ?Sized>() {}
 
 fn main() {
     assert::<*mut RefCell<i32>>();
index 77ac97bc8b89951427bf7aaa9d69d0e45675f55a..8763fb0913a87287ef21d84b391dc7be7bc6bf68 100644 (file)
@@ -36,7 +36,13 @@ fn main() {
         y: 3,
     };
     let ans = s("what");    //~ ERROR mismatched types
-    let ans = s();  //~ ERROR this function takes 1 parameter but 0 parameters were supplied
+    //~^ NOTE expected isize, found &-ptr
+    //~| NOTE expected type
+    //~| NOTE found type
+    let ans = s();
+    //~^ ERROR this function takes 1 parameter but 0 parameters were supplied
+    //~| NOTE the following parameter type was expected
     let ans = s("burma", "shave");
     //~^ ERROR this function takes 1 parameter but 2 parameters were supplied
+    //~| NOTE the following parameter type was expected
 }
index 4a8513e10d755959a7b6a356bd43e7b722c0811a..f1683e51c648d2b0b069262794984c2c0b0cab43 100644 (file)
@@ -11,5 +11,5 @@
 struct foo(usize);
 
 fn main() {
-    let (foo, _) = (2, 3); //~ ERROR `foo` cannot be named the same as
+    let (foo, _) = (2, 3); //~ ERROR let bindings cannot shadow structs
 }
diff --git a/src/test/compile-fail/pat-slice-old-style.rs b/src/test/compile-fail/pat-slice-old-style.rs
new file mode 100644 (file)
index 0000000..ccb25d8
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(slice_patterns)]
+
+fn slice_pat(x: &[u8]) {
+    // OLD!
+    match x {
+        [a, b..] => {}
+        //~^ ERROR expected an array or slice, found `&[u8]`
+        //~| HELP the semantics of slice patterns changed recently; see issue #23121
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/pat-tuple-bad-type.rs b/src/test/compile-fail/pat-tuple-bad-type.rs
new file mode 100644 (file)
index 0000000..0d50a30
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+fn main() {
+    let x;
+
+    match x {
+        (..) => {} //~ ERROR the type of this value must be known in this context
+        _ => {}
+    }
+
+    match 0u8 {
+        (..) => {} //~ ERROR mismatched types
+        _ => {}
+    }
+
+    x = 10;
+}
diff --git a/src/test/compile-fail/pat-tuple-feature-gate.rs b/src/test/compile-fail/pat-tuple-feature-gate.rs
new file mode 100644 (file)
index 0000000..55ca05b
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    match 0 {
+        (..) => {} //~ ERROR `..` in tuple patterns is experimental
+        (pat, ..) => {} //~ ERROR `..` in tuple patterns is experimental
+        S(pat, ..) => {} //~ ERROR `..` in tuple struct patterns is experimental
+    }
+}
diff --git a/src/test/compile-fail/pat-tuple-overfield.rs b/src/test/compile-fail/pat-tuple-overfield.rs
new file mode 100644 (file)
index 0000000..034ef4a
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+struct S(u8, u8, u8);
+
+fn main() {
+    match (1, 2, 3) {
+        (1, 2, 3, 4) => {} //~ ERROR mismatched types
+        (1, 2, .., 3, 4) => {} //~ ERROR mismatched types
+        _ => {}
+    }
+    match S(1, 2, 3) {
+        S(1, 2, 3, 4) => {}
+        //~^ ERROR this pattern has 4 fields, but the corresponding struct has 3 fields
+        S(1, 2, .., 3, 4) => {}
+        //~^ ERROR this pattern has 4 fields, but the corresponding struct has 3 fields
+        _ => {}
+    }
+}
index d9f3bb3c40f8d0d18a2b137ad81dfb6aec75cbf5..507012e8c5c06803a7c3a2426678c08516fa3758 100644 (file)
@@ -25,7 +25,7 @@ fn f(_c: char) {}
 fn main() {
     match A::B(1, 2) {
         A::B(_, _, _) => (), //~ ERROR this pattern has 3 fields, but
-        A::D(_) => (),       //~ ERROR this pattern has 1 field, but
+        A::D(_) => (),       //~ ERROR `A::D` does not name a tuple variant or a tuple struct
         _ => ()
     }
     match 'c' {
index 43d112b8aa0043aee7937ee05d0ecebc23d185e3..8901d8c08e50cef5e5375d001964b38a1eaab6cb 100644 (file)
@@ -8,8 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub extern {
-    //~^ ERROR unnecessary visibility
+pub extern { //~ ERROR unnecessary visibility qualifier
     pub fn bar();
 }
 
@@ -19,10 +18,10 @@ trait A {
 
 struct B;
 
-pub impl B {} //~ ERROR: unnecessary visibility
+pub impl B {} //~ ERROR unnecessary visibility qualifier
 
-pub impl A for B { //~ ERROR: unnecessary visibility
-    pub fn foo(&self) {} //~ ERROR: unnecessary visibility
+pub impl A for B { //~ ERROR unnecessary visibility qualifier
+    pub fn foo(&self) {} //~ ERROR unnecessary visibility qualifier
 }
 
 pub fn main() {}
index ab423620d6866ff64e0e3e81160395251d9b91b0..ae60c4366ee33521583cac84de334014064e3e92 100644 (file)
@@ -16,9 +16,11 @@ macro_rules! m {
 
 struct S<T>(T);
 m!{ S<u8> } //~ ERROR type or lifetime parameters in visibility path
+//~^ ERROR failed to resolve module path. Not a module `S`
 
 mod foo {
     struct S(pub(foo<T>) ()); //~ ERROR type or lifetime parameters in visibility path
+    //~^ ERROR type name `T` is undefined or not in scope
 }
 
 fn main() {}
index 002080f4cb44c09f647d66dae25c395a94952002..86873022f0ff10b666941c3cd86cdefcac106ffa 100644 (file)
@@ -27,7 +27,7 @@ impl S {
 
 fn main() {
     match 10 {
-        <S as Tr>::A::f::<u8> => {} //~ ERROR `f` is not an associated const
+        <S as Tr>::A::f::<u8> => {} //~ ERROR associated items in match patterns must be constants
         0 ... <S as Tr>::A::f::<u8> => {} //~ ERROR only char and numeric types are allowed in range
     }
 }
index 5b0dd256b4c41788670d51f2c66f4fddc3d30a4e..c00be91a2d74daf067a0d99abb6251d0652239c9 100644 (file)
@@ -17,9 +17,8 @@ pub fn main() {
 
     // Bool => does not implement iterator.
     for i in false..true {}
-    //~^ ERROR `bool: std::num::One` is not satisfied
-    //~^^ ERROR `bool: std::iter::Step` is not satisfied
-    //~^^^ ERROR `for<'a> &'a bool: std::ops::Add` is not satisfied
+    //~^ ERROR `bool: std::iter::Step` is not satisfied
+    //~^^ ERROR `for<'a> &'a bool: std::ops::Add` is not satisfied
 
     // Unsized type.
     let arr: &[_] = &[1, 2, 3];
diff --git a/src/test/compile-fail/range_traits-1.rs b/src/test/compile-fail/range_traits-1.rs
new file mode 100644 (file)
index 0000000..8521971
--- /dev/null
@@ -0,0 +1,93 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(inclusive_range)]
+
+use std::ops::*;
+
+// FIXME #34229 duplicated errors
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+struct AllTheRanges {
+    a: Range<usize>,
+    //~^ ERROR PartialOrd
+    //~^^ ERROR PartialOrd
+    //~^^^ ERROR Ord
+    //~^^^^ ERROR binary operation
+    //~^^^^^ ERROR binary operation
+    //~^^^^^^ ERROR binary operation
+    //~^^^^^^^ ERROR binary operation
+    //~^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^^ ERROR binary operation
+    b: RangeTo<usize>,
+    //~^ ERROR PartialOrd
+    //~^^ ERROR PartialOrd
+    //~^^^ ERROR Ord
+    //~^^^^ ERROR binary operation
+    //~^^^^^ ERROR binary operation
+    //~^^^^^^ ERROR binary operation
+    //~^^^^^^^ ERROR binary operation
+    //~^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^^ ERROR binary operation
+    c: RangeFrom<usize>,
+    //~^ ERROR PartialOrd
+    //~^^ ERROR PartialOrd
+    //~^^^ ERROR Ord
+    //~^^^^ ERROR binary operation
+    //~^^^^^ ERROR binary operation
+    //~^^^^^^ ERROR binary operation
+    //~^^^^^^^ ERROR binary operation
+    //~^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^^ ERROR binary operation
+    d: RangeFull,
+    //~^ ERROR PartialOrd
+    //~^^ ERROR PartialOrd
+    //~^^^ ERROR Ord
+    //~^^^^ ERROR binary operation
+    //~^^^^^ ERROR binary operation
+    //~^^^^^^ ERROR binary operation
+    //~^^^^^^^ ERROR binary operation
+    //~^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^^ ERROR binary operation
+    e: RangeInclusive<usize>,
+    //~^ ERROR PartialOrd
+    //~^^ ERROR PartialOrd
+    //~^^^ ERROR Ord
+    //~^^^^ ERROR binary operation
+    //~^^^^^ ERROR binary operation
+    //~^^^^^^ ERROR binary operation
+    //~^^^^^^^ ERROR binary operation
+    //~^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^^ ERROR binary operation
+    f: RangeToInclusive<usize>,
+    //~^ ERROR PartialOrd
+    //~^^ ERROR PartialOrd
+    //~^^^ ERROR Ord
+    //~^^^^ ERROR binary operation
+    //~^^^^^ ERROR binary operation
+    //~^^^^^^ ERROR binary operation
+    //~^^^^^^^ ERROR binary operation
+    //~^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^ ERROR binary operation
+    //~^^^^^^^^^^^ ERROR binary operation
+}
+
+fn main() {}
+
diff --git a/src/test/compile-fail/range_traits-2.rs b/src/test/compile-fail/range_traits-2.rs
new file mode 100644 (file)
index 0000000..64fcd25
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::ops::*;
+
+#[derive(Copy, Clone)] //~ ERROR Copy
+struct R(Range<usize>);
+
+fn main() {}
+
diff --git a/src/test/compile-fail/range_traits-3.rs b/src/test/compile-fail/range_traits-3.rs
new file mode 100644 (file)
index 0000000..d26b795
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::ops::*;
+
+#[derive(Copy, Clone)] //~ ERROR Copy
+struct R(RangeFrom<usize>);
+
+fn main() {}
+
diff --git a/src/test/compile-fail/range_traits-4.rs b/src/test/compile-fail/range_traits-4.rs
new file mode 100644 (file)
index 0000000..630969b
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+use std::ops::*;
+
+#[derive(Copy, Clone)]
+struct R(RangeTo<usize>);
+
+#[rustc_error]
+fn main() {} //~ ERROR success
+
diff --git a/src/test/compile-fail/range_traits-5.rs b/src/test/compile-fail/range_traits-5.rs
new file mode 100644 (file)
index 0000000..5963c4a
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+use std::ops::*;
+
+#[derive(Copy, Clone)]
+struct R(RangeFull);
+
+#[rustc_error]
+fn main() {} //~ ERROR success
+
diff --git a/src/test/compile-fail/range_traits-6.rs b/src/test/compile-fail/range_traits-6.rs
new file mode 100644 (file)
index 0000000..7c62711
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(inclusive_range)]
+
+use std::ops::*;
+
+#[derive(Copy, Clone)] //~ ERROR Copy
+struct R(RangeInclusive<usize>);
+
+fn main() {}
+
diff --git a/src/test/compile-fail/range_traits-7.rs b/src/test/compile-fail/range_traits-7.rs
new file mode 100644 (file)
index 0000000..b6fec77
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs, inclusive_range)]
+
+use std::ops::*;
+
+#[derive(Copy, Clone)]
+struct R(RangeToInclusive<usize>);
+
+#[rustc_error]
+fn main() {} //~ ERROR success
+
diff --git a/src/test/compile-fail/recursive-reexports.rs b/src/test/compile-fail/recursive-reexports.rs
new file mode 100644 (file)
index 0000000..6fd52be
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:recursive_reexports.rs
+
+fn f() -> recursive_reexports::S {} //~ ERROR undeclared
+
+fn main() {}
index abffd33e3f83e857a192aa63a1264621be8f52a5..6e60a373d9b0cecb02fb8bebea804d577a72bac5 100644 (file)
@@ -34,7 +34,8 @@ impl<'a, 't> Foo<'a, 't> for &'a isize {
     }
 
     fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
-        //~^ ERROR method `wrong_bound1` has an incompatible type for trait
+        //~^ ERROR method not compatible with trait
+        //~^^ ERROR method not compatible with trait
         //
         // Note: This is a terrible error message. It is caused
         // because, in the trait, 'b is early bound, and in the impl,
index 924044647d84a08473c547d8da2366d2bb8b34b6..b70ec59420db1f7cf0347ccd993194285521b1e7 100644 (file)
@@ -19,7 +19,7 @@ trait SomeTrait { fn get(&self) -> isize; }
 fn make_object1<A:SomeTrait>(v: A) -> Box<SomeTrait+'static> {
     box v as Box<SomeTrait+'static>
         //~^ ERROR the parameter type `A` may not live long enough
-        //~^^ ERROR the parameter type `A` may not live long enough
+        //~| ERROR the parameter type `A` may not live long enough
 }
 
 fn make_object2<'a,A:SomeTrait+'a>(v: A) -> Box<SomeTrait+'a> {
@@ -29,7 +29,7 @@ fn make_object2<'a,A:SomeTrait+'a>(v: A) -> Box<SomeTrait+'a> {
 fn make_object3<'a,'b,A:SomeTrait+'a>(v: A) -> Box<SomeTrait+'b> {
     box v as Box<SomeTrait+'b>
         //~^ ERROR the parameter type `A` may not live long enough
-        //~^^ ERROR the parameter type `A` may not live long enough
+        //~| ERROR the parameter type `A` may not live long enough
 }
 
 fn main() { }
index f6a0c86de6626ec01f16d39d37ad13d375acdb07..eaf9a750570dbdb8966c100fe112068a9f02827c 100644 (file)
@@ -28,11 +28,7 @@ impl<'a> GetRef<'a> for Box<'a> {
 impl<'a> Box<'a> {
     fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize {
         g2.get()
-        //~^ ERROR mismatched types
-        //~| expected type `&'a isize`
-        //~| found type `&'b isize`
-        //~| lifetime mismatch
-
+        //~^ ERROR E0312
     }
 }
 
index 1fc3b4b3c6a620279653f39d68b0143be1961817..90a3395004776a2310e0164af183973d322ab323 100644 (file)
@@ -27,7 +27,7 @@ impl<'a,T:Clone> GetRef<'a,T> for Box<'a,T> {
 
 fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
     g1.get()
-    //~^ ERROR mismatched types
+    //~^ ERROR E0312
 }
 
 fn main() {
index ced0afcebd97908554e2aecb1210e59c31cdcfd5..87b5efbfadd865c5e1afa30690f1106f65b460b0 100644 (file)
@@ -10,7 +10,7 @@
 
 
 struct Invariant<'a> {
-    f: Box<for<'b> FnOnce() -> &'b mut &'a isize + 'static>,
+    f: Box<FnOnce() -> *mut &'a isize + 'static>,
 }
 
 fn to_same_lifetime<'r>(b_isize: Invariant<'r>) {
index 01439ce5e68775f83454622d295e33ac0aa92ac2..9cd08656b62c5a71afcdb1cf286f8feb5330ec65 100644 (file)
@@ -23,7 +23,7 @@ impl<'a> get_ctxt for has_ctxt<'a> {
 
     // Here an error occurs because we used `&self` but
     // the definition used `&`:
-    fn get_ctxt(&self) -> &'a ctxt { //~ ERROR method `get_ctxt` has an incompatible type
+    fn get_ctxt(&self) -> &'a ctxt { //~ ERROR method not compatible with trait
         self.c
     }
 
index 8c7c0c275db8ec74c14afe841756b8c386606489..d35f2cbb584c767958051215f4ad1dc1e962ed9b 100644 (file)
 #![stable(feature = "rust1", since = "1.0.0")]
 
 mod bogus_attribute_types_1 {
-    #[stable(feature = "a", since = "a", reason)] //~ ERROR unknown meta item 'reason'
+    #[stable(feature = "a", since = "a", reason)] //~ ERROR unknown meta item 'reason' [E0541]
     fn f1() { }
 
-    #[stable(feature = "a", since)] //~ ERROR incorrect meta item
+    #[stable(feature = "a", since)] //~ ERROR incorrect meta item [E0539]
     fn f2() { }
 
-    #[stable(feature, since = "a")] //~ ERROR incorrect meta item
+    #[stable(feature, since = "a")] //~ ERROR incorrect meta item [E0539]
     fn f3() { }
 
-    #[stable(feature = "a", since(b))] //~ ERROR incorrect meta item
+    #[stable(feature = "a", since(b))] //~ ERROR incorrect meta item [E0539]
     fn f5() { }
 
-    #[stable(feature(b), since = "a")] //~ ERROR incorrect meta item
+    #[stable(feature(b), since = "a")] //~ ERROR incorrect meta item [E0539]
     fn f6() { }
 }
 
 mod bogus_attribute_types_2 {
-    #[unstable] //~ ERROR incorrect stability attribute type
+    #[unstable] //~ ERROR incorrect stability attribute type [E0548]
     fn f1() { }
 
-    #[unstable = "a"] //~ ERROR incorrect stability attribute type
+    #[unstable = "a"] //~ ERROR incorrect stability attribute type [E0548]
     fn f2() { }
 
-    #[stable] //~ ERROR incorrect stability attribute type
+    #[stable] //~ ERROR incorrect stability attribute type [E0548]
     fn f3() { }
 
-    #[stable = "a"] //~ ERROR incorrect stability attribute type
+    #[stable = "a"] //~ ERROR incorrect stability attribute type [E0548]
     fn f4() { }
 
     #[stable(feature = "a", since = "b")]
-    #[rustc_deprecated] //~ ERROR incorrect stability attribute type
+    #[rustc_deprecated] //~ ERROR incorrect stability attribute type [E0548]
     fn f5() { }
 
     #[stable(feature = "a", since = "b")]
-    #[rustc_deprecated = "a"] //~ ERROR incorrect stability attribute type
+    #[rustc_deprecated = "a"] //~ ERROR incorrect stability attribute type [E0548]
     fn f6() { }
 }
 
 mod missing_feature_names {
-    #[unstable(issue = "0")] //~ ERROR missing 'feature'
+    #[unstable(issue = "0")] //~ ERROR missing 'feature' [E0546]
     fn f1() { }
 
-    #[unstable(feature = "a")] //~ ERROR missing 'issue'
+    #[unstable(feature = "a")] //~ ERROR missing 'issue' [E0547]
     fn f2() { }
 
-    #[stable(since = "a")] //~ ERROR missing 'feature'
+    #[stable(since = "a")] //~ ERROR missing 'feature' [E0546]
     fn f3() { }
 }
 
 mod missing_version {
-    #[stable(feature = "a")] //~ ERROR missing 'since'
+    #[stable(feature = "a")] //~ ERROR missing 'since' [E0542]
     fn f1() { }
 
     #[stable(feature = "a", since = "b")]
-    #[rustc_deprecated(reason = "a")] //~ ERROR missing 'since'
+    #[rustc_deprecated(reason = "a")] //~ ERROR missing 'since' [E0542]
     fn f2() { }
 }
 
 #[unstable(feature = "a", issue = "0")]
-#[stable(feature = "a", since = "b")]
-fn multiple1() { } //~ ERROR multiple stability levels
+#[stable(feature = "a", since = "b")] //~ ERROR multiple stability levels [E0544]
+fn multiple1() { }
 
 #[unstable(feature = "a", issue = "0")]
-#[unstable(feature = "a", issue = "0")]
-fn multiple2() { } //~ ERROR multiple stability levels
+#[unstable(feature = "a", issue = "0")] //~ ERROR multiple stability levels [E0544]
+fn multiple2() { }
 
 #[stable(feature = "a", since = "b")]
-#[stable(feature = "a", since = "b")]
-fn multiple3() { } //~ ERROR multiple stability levels
+#[stable(feature = "a", since = "b")] //~ ERROR multiple stability levels [E0544]
+fn multiple3() { }
 
 #[stable(feature = "a", since = "b")]
 #[rustc_deprecated(since = "b", reason = "text")]
 #[rustc_deprecated(since = "b", reason = "text")]
-fn multiple4() { } //~ ERROR multiple rustc_deprecated attributes
+fn multiple4() { } //~ ERROR multiple rustc_deprecated attributes [E0540]
 //~^ ERROR Invalid stability or deprecation version found
 
 #[rustc_deprecated(since = "a", reason = "text")]
-fn deprecated_without_unstable_or_stable() { } //~ ERROR rustc_deprecated attribute must be paired
+fn deprecated_without_unstable_or_stable() { }
+//~^ ERROR rustc_deprecated attribute must be paired with either stable or unstable attribute
 
 fn main() { }
index 30eb4112c3fb2e40a0814f1baff9baf825163d3c..6f8c95c384032c343b99df245b2c5003a04b74af 100644 (file)
@@ -12,8 +12,8 @@
 // language and lib features.
 
 #![deny(stable_features)]
-#![feature(test_accepted_feature)] //~ ERROR this feature is stable
-#![feature(rust1)] //~ ERROR this feature is stable
+#![feature(test_accepted_feature)] //~ ERROR this feature has been stable since 1.0.0
+#![feature(rust1)] //~ ERROR this feature has been stable since 1.0.0
 
 fn main() {
     let _foo: Vec<()> = Vec::new();
index 76fecea0c3a6ab77a27d9f3708e4d4a05f36dfc0..351a47fdf3923191d85505cd034f57f8eaf41ff6 100644 (file)
@@ -20,7 +20,7 @@ fn main() {
     // instead of spitting out a custom error about some identifier collisions
     // (we should allow shadowing)
     match 4 {
-        a => {} //~ ERROR static variables cannot be referenced in a pattern
+        a => {} //~ ERROR match bindings cannot shadow statics
         _ => {}
     }
 }
@@ -44,7 +44,7 @@ fn mutable_statics() {
     match (Foo { bar: Some(Direction::North), baz: NewBool(true) }) {
         Foo { bar: None, baz: NewBool(true) } => (),
         STATIC_MUT_FOO => (),
-        //~^ ERROR static variables cannot be referenced in a pattern
+        //~^ ERROR match bindings cannot shadow statics
         Foo { bar: Some(Direction::South), .. } => (),
         Foo { bar: Some(EAST), .. } => (),
         Foo { bar: Some(Direction::North), baz: NewBool(true) } => (),
index 412c90fd214c17ae5c3a585e0cd3d22fc032d4d9..4a816ea75727669eef145b565c648e82b4a8d9b7 100644 (file)
@@ -26,51 +26,51 @@ pub mod a {
 fn h1() -> i32 {
     a.I
         //~^ ERROR E0425
-        //~| HELP To reference an item from the `a` module, use `a::I`
+        //~| HELP to reference an item from the `a` module, use `a::I`
 }
 
 fn h2() -> i32 {
     a.g()
         //~^ ERROR E0425
-        //~| HELP To call a function from the `a` module, use `a::g(..)`
+        //~| HELP to call a function from the `a` module, use `a::g(..)`
 }
 
 fn h3() -> i32 {
     a.b.J
         //~^ ERROR E0425
-        //~| HELP To reference an item from the `a` module, use `a::b`
+        //~| HELP to reference an item from the `a` module, use `a::b`
 }
 
 fn h4() -> i32 {
     a::b.J
         //~^ ERROR E0425
-        //~| HELP To reference an item from the `a::b` module, use `a::b::J`
+        //~| HELP to reference an item from the `a::b` module, use `a::b::J`
 }
 
 fn h5() {
     a.b.f();
         //~^ ERROR E0425
-        //~| HELP To reference an item from the `a` module, use `a::b`
+        //~| HELP to reference an item from the `a` module, use `a::b`
     let v = Vec::new();
     v.push(a::b);
         //~^ ERROR E0425
-        //~| HELP Module `a::b` cannot be used as an expression
+        //~| HELP module `a::b` cannot be used as an expression
 }
 
 fn h6() -> i32 {
     a::b.f()
         //~^ ERROR E0425
-        //~| HELP To call a function from the `a::b` module, use `a::b::f(..)`
+        //~| HELP to call a function from the `a::b` module, use `a::b::f(..)`
 }
 
 fn h7() {
     a::b
         //~^ ERROR E0425
-        //~| HELP Module `a::b` cannot be used as an expression
+        //~| HELP module `a::b` cannot be used as an expression
 }
 
 fn h8() -> i32 {
     a::b()
         //~^ ERROR E0425
-        //~| HELP Module `a::b` cannot be used as an expression
+        //~| HELP module `a::b` cannot be used as an expression
 }
index 39bee26da20b8b1e0de20e4982903191f1edc645..93fa48b880fdf9a599263134a3eb5a78c7fae8dc 100644 (file)
@@ -25,7 +25,7 @@ mod bar {
     use foo::Foo;
 
     impl Foo {
-        #[rustc_symbol_name] //~ ERROR _ZN5impl13bar26_$LT$impl$u20$foo..Foo$GT$3baz
+        #[rustc_symbol_name] //~ ERROR _ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz
         #[rustc_item_path] //~ ERROR item-path(bar::<impl foo::Foo>::baz)
         fn baz() { }
     }
index 38a6834a9c46034772d41ffd94c7bebd76cabac3..3e36b126523a7f84828dd5fd7180ae54724c5aaf 100644 (file)
@@ -8,12 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// this now fails (correctly, I claim) because hygiene prevents
-// the assembled identifier from being a reference to the binding.
-#![feature(concat_idents)]
+#![feature(concat_idents, type_macros)]
 
 pub fn main() {
+    struct Foo;
+    let _: concat_idents!(F, oo) = Foo; // Test that `concat_idents!` can be used in type positions
+
     let asdf_fdsa = "<.<".to_string();
+    // this now fails (correctly, I claim) because hygiene prevents
+    // the assembled identifier from being a reference to the binding.
     assert!(concat_idents!(asd, f_f, dsa) == "<.<".to_string());
     //~^ ERROR: unresolved name `asdf_fdsa`
 
index d627de24d679451b66a8d9778720593479bc886b..8dcd67db999cb5496ccac04117e3bd1373191bdf 100644 (file)
@@ -23,8 +23,8 @@ fn main() {
     // of the below being caught.
 
     macro_rules! expando {
-        ($x: ident) => { trace_macros!($x) }
+        ($x: ident) => { trace_macros!($x) } //~ ERROR `trace_macros` is not stable
     }
 
-    expando!(true); //~ ERROR `trace_macros` is not stable
+    expando!(true); //~ NOTE in this expansion
 }
index c04e197b6bd774ad72489772baf1f66a3c58914e..1fe8f6294da2118c0e9506de421aeca10f5a0579 100644 (file)
@@ -14,7 +14,7 @@ mod a {
 trait A {
 }
 
-impl A for a { //~ ERROR type name `a` is undefined or not in scope
+impl A for a { //~ ERROR expected type, found module
 }
 
 fn main() {
diff --git a/src/test/compile-fail/unresolved_static_type_field.rs b/src/test/compile-fail/unresolved_static_type_field.rs
new file mode 100644 (file)
index 0000000..80f6108
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn f(_: bool) {}
+
+struct Foo {
+    cx: bool,
+}
+
+impl Foo {
+    fn bar() {
+        f(cx); //~ ERROR E0425
+               //~| HELP this is an associated function
+    }
+}
+
+fn main() {}
index d721d428f29f597e7957a63fc9938ee1f6b2e6a9..1d0d60a775fda7d02699ee2bcf682ea72a1fa92b 100644 (file)
@@ -9,10 +9,17 @@
 // except according to those terms.
 
 #![feature(rustc_attrs)]
+#![allow(unused_imports, dead_code)]
+
+struct S;
+struct Z;
 
 mod foo {
+    use ::super::{S, Z}; //~ WARN global paths cannot start with `super`
+    //~^ WARN this was previously accepted by the compiler but is being phased out
+
     pub fn g() {
-        use ::super::main; //~ WARN expected identifier, found keyword `super`
+        use ::super::main; //~ WARN global paths cannot start with `super`
         //~^ WARN this was previously accepted by the compiler but is being phased out
         main();
     }
index 268b937c2916581f1631074ef823f0df4d675e6e..064062df753b6761b5bf7557355e381e975fef05 100644 (file)
@@ -15,14 +15,12 @@ pub trait E {
 }
 
 impl E for A {
-    pub fn foo(&self) {}             //~ ERROR: unnecessary visibility
+    pub fn foo(&self) {} //~ ERROR: unnecessary visibility qualifier
 }
 
 enum Foo {
     V1 { pub f: i32 }, //~ ERROR unnecessary visibility qualifier
-                       //| NOTE visibility qualifiers have no effect on variant fields
     V2(pub i32), //~ ERROR unnecessary visibility qualifier
-                 //| NOTE visibility qualifiers have no effect on variant fields
 }
 
 fn main() {}
index b43159b0d96b042fbd51c2494632730897602223..d8620ead836397d8a290c961cfb38c939913f047 100644 (file)
@@ -17,7 +17,9 @@ extern "C" fn bar(f: isize, x: u8) {}
 fn main() {
     unsafe {
         foo(); //~ ERROR: this function takes at least 2 parameters but 0 parameters were supplied
+        //~^ NOTE the following parameter types were expected
         foo(1); //~ ERROR: this function takes at least 2 parameters but 1 parameter was supplied
+        //~^ NOTE the following parameter types were expected
 
         let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
         //~^ ERROR: mismatched types
index d007b507556188f4f5bc6c6dec1c7f89b9bf70a0..add0938409405d6b9cf0523c6d731e6edef51ce9 100644 (file)
 #![omit_gdb_pretty_printer_section]
 
 fn immediate_args(a: isize, b: bool, c: f64) {
-    println!("") // #break
+    zzz(); // #break
 }
 
 struct BigStruct {
@@ -243,7 +243,7 @@ struct BigStruct {
 }
 
 fn non_immediate_args(a: BigStruct, b: BigStruct) {
-    println!("") // #break
+    zzz(); // #break
 }
 
 fn binding(a: i64, b: u64, c: f64) {
@@ -257,7 +257,7 @@ fn assignment(mut a: u64, b: u64, c: f64) {
 }
 
 fn function_call(x: u64, y: u64, z: f64) {
-    println!("Hi!") // #break
+    zzz(); // #break
 }
 
 fn identifier(x: u64, y: u64, z: f64) -> u64 {
@@ -333,3 +333,5 @@ fn main() {
     while_expr(40, 41, 42);
     loop_expr(43, 44, 45);
 }
+
+fn zzz() {()}
index f0ecda92993706ce145e3c1961298400e6b12076..b5b6ca7572703cc65fab869385623d0d4b924be5 100644 (file)
 // lldb-command:continue
 
 #![allow(dead_code, unused_assignments, unused_variables)]
-#![feature(omit_gdb_pretty_printer_section, rustc_attrs)]
+#![feature(omit_gdb_pretty_printer_section)]
 #![omit_gdb_pretty_printer_section]
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn immediate_args(a: isize, b: bool, c: f64) {
     println!("");
 }
@@ -268,51 +267,43 @@ struct BigStruct {
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn non_immediate_args(a: BigStruct, b: BigStruct) {
     println!("");
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn binding(a: i64, b: u64, c: f64) {
     let x = 0;
     println!("");
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn assignment(mut a: u64, b: u64, c: f64) {
     a = b;
     println!("");
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn function_call(x: u64, y: u64, z: f64) {
     println!("Hi!")
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn identifier(x: u64, y: u64, z: f64) -> u64 {
     x
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn return_expr(x: u64, y: u64, z: f64) -> u64 {
     return x;
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn arithmetic_expr(x: u64, y: u64, z: f64) -> u64 {
     x + y
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn if_expr(x: u64, y: u64, z: f64) -> u64 {
     if x + y < 1000 {
         x
@@ -322,7 +313,6 @@ fn if_expr(x: u64, y: u64, z: f64) -> u64 {
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn while_expr(mut x: u64, y: u64, z: u64) -> u64 {
     while x + y < 1000 {
         x += z
@@ -331,7 +321,6 @@ fn while_expr(mut x: u64, y: u64, z: u64) -> u64 {
 }
 
 #[no_stack_check]
-#[rustc_no_mir] // FIXME(#32949) MIR debuginfo shadows arguments with uninit vars.
 fn loop_expr(mut x: u64, y: u64, z: u64) -> u64 {
     loop {
         x += z;
diff --git a/src/test/debuginfo/pretty-huge-vec.rs b/src/test/debuginfo/pretty-huge-vec.rs
new file mode 100644 (file)
index 0000000..bb53852
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright 2013-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-windows failing on win32 bot
+// ignore-freebsd: gdb package too new
+// ignore-android: FIXME(#10381)
+// compile-flags:-g
+// min-gdb-version 7.7
+// min-lldb-version: 310
+
+// === GDB TESTS ===================================================================================
+
+// gdb-command: run
+
+// gdb-command: print vec
+// gdb-check:$1 = Vec<u8>(len: 1000000000, cap: 1000000000) = {[...]...}
+
+// gdb-command: print slice
+// gdb-check:$2 = &[u8](len: 1000000000) = {[...]...}
+
+
+#![allow(unused_variables)]
+
+fn main() {
+
+    // Vec
+    let mut vec: Vec<u8> = Vec::with_capacity(1_000_000_000);
+    unsafe{ vec.set_len(1_000_000_000) }
+    let slice = &vec[..];
+
+    zzz(); // #break
+}
+
+fn zzz() { () }
diff --git a/src/test/debuginfo/pretty-uninitialized-vec.rs b/src/test/debuginfo/pretty-uninitialized-vec.rs
new file mode 100644 (file)
index 0000000..7516965
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright 2013-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-windows failing on win32 bot
+// ignore-freebsd: gdb package too new
+// ignore-android: FIXME(#10381)
+// compile-flags:-g
+// min-gdb-version 7.7
+// min-lldb-version: 310
+
+// === GDB TESTS ===================================================================================
+
+// gdb-command: run
+
+// gdb-command: print vec
+// gdb-check:$1 = Vec<i32>(len: [...], cap: [...])[...]
+
+
+#![allow(unused_variables)]
+
+fn main() {
+
+    let vec;
+    zzz(); // #break
+    vec = vec![0];
+
+}
+
+fn zzz() { () }
index 0d396c885eb85f2efbad184cc3e3ee9846702d04..cdb5bc4ecfb6ec371976059ad75cc9d7c85ac047 100644 (file)
 // gdb-command:print *((int64_t[2]*)('vec_slices::MUT_VECT_SLICE'.data_ptr))
 // gdb-check:$15 = {64, 65}
 
+//gdb-command:print mut_slice.length
+//gdb-check:$16 = 5
+//gdb-command:print *((int64_t[5]*)(mut_slice.data_ptr))
+//gdb-check:$17 = {1, 2, 3, 4, 5}
+
 
 // === LLDB TESTS ==================================================================================
 
@@ -106,6 +111,8 @@ fn main() {
         MUT_VECT_SLICE = VECT_SLICE;
     }
 
+    let mut_slice: &mut [i64] = &mut [1, 2, 3, 4, 5];
+
     zzz(); // #break
 }
 
diff --git a/src/test/incremental/struct_add_field.rs b/src/test/incremental/struct_add_field.rs
new file mode 100644 (file)
index 0000000..cc8ef8a
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test incremental compilation tracking where we change field names
+// in between revisions (hashing should be stable).
+
+// revisions:rpass1 rpass2
+
+#![feature(rustc_attrs)]
+
+pub struct X {
+    pub x: u32,
+
+    #[cfg(rpass2)]
+    pub x2: u32,
+}
+
+pub struct EmbedX {
+    x: X
+}
+
+pub struct Y {
+    pub y: char
+}
+
+#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_X(x: X) -> u32 {
+    x.x as u32
+}
+
+#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_EmbedX(embed: EmbedX) -> u32 {
+    embed.x.x as u32
+}
+
+#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_Y() {
+    let x: Y = Y { y: 'c' };
+}
+
+pub fn main() { }
diff --git a/src/test/incremental/struct_change_field_name.rs b/src/test/incremental/struct_change_field_name.rs
new file mode 100644 (file)
index 0000000..fe29ad6
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test incremental compilation tracking where we change field names
+// in between revisions (hashing should be stable).
+
+// revisions:rpass1 cfail2
+
+#![feature(rustc_attrs)]
+
+#[cfg(rpass1)]
+pub struct X {
+    pub x: u32
+}
+
+#[cfg(cfail2)]
+pub struct X {
+    pub y: u32
+}
+
+pub struct EmbedX {
+    x: X
+}
+
+pub struct Y {
+    pub y: char
+}
+
+#[rustc_dirty(label="TypeckItemBody", cfg="cfail2")]
+pub fn use_X() -> u32 {
+    let x: X = X { x: 22 };
+    //[cfail2]~^ ERROR structure `X` has no field named `x`
+    x.x as u32
+    //[cfail2]~^ ERROR attempted access of field `x`
+}
+
+#[rustc_dirty(label="TypeckItemBody", cfg="cfail2")]
+pub fn use_EmbedX(embed: EmbedX) -> u32 {
+    embed.x.x as u32
+    //[cfail2]~^ ERROR attempted access of field `x`
+}
+
+#[rustc_clean(label="TypeckItemBody", cfg="cfail2")]
+pub fn use_Y() {
+    let x: Y = Y { y: 'c' };
+}
+
+pub fn main() { }
diff --git a/src/test/incremental/struct_change_field_type.rs b/src/test/incremental/struct_change_field_type.rs
new file mode 100644 (file)
index 0000000..1a50d51
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test incremental compilation tracking where we change nothing
+// in between revisions (hashing should be stable).
+
+// revisions:rpass1 rpass2
+
+#![feature(rustc_attrs)]
+
+#[cfg(rpass1)]
+pub struct X {
+    pub x: u32
+}
+
+#[cfg(rpass2)]
+pub struct X {
+    pub x: i32
+}
+
+pub struct EmbedX {
+    x: X
+}
+
+pub struct Y {
+    pub y: char
+}
+
+#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_X() -> u32 {
+    let x: X = X { x: 22 };
+    x.x as u32
+}
+
+#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_EmbedX(x: EmbedX) -> u32 {
+    let x: X = X { x: 22 };
+    x.x as u32
+}
+
+#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_Y() {
+    let x: Y = Y { y: 'c' };
+}
+
+pub fn main() { }
diff --git a/src/test/incremental/struct_change_field_type_cross_crate/auxiliary/a.rs b/src/test/incremental/struct_change_field_type_cross_crate/auxiliary/a.rs
new file mode 100644 (file)
index 0000000..2ddcaf1
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_type="rlib"]
+
+ #[cfg(rpass1)]
+pub struct X {
+    pub x: u32
+}
+
+#[cfg(rpass2)]
+pub struct X {
+    pub x: i32
+}
+
+pub struct EmbedX {
+    pub x: X
+}
+
+pub struct Y {
+    pub y: char
+}
diff --git a/src/test/incremental/struct_change_field_type_cross_crate/b.rs b/src/test/incremental/struct_change_field_type_cross_crate/b.rs
new file mode 100644 (file)
index 0000000..7a4900d
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:a.rs
+// revisions:rpass1 rpass2
+
+#![feature(rustc_attrs)]
+
+extern crate a;
+
+use a::*;
+
+#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_X() -> u32 {
+    let x: X = X { x: 22 };
+    x.x as u32
+}
+
+#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_EmbedX(embed: EmbedX) -> u32 {
+    embed.x.x as u32
+}
+
+#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_Y() {
+    let x: Y = Y { y: 'c' };
+}
+
+pub fn main() { }
diff --git a/src/test/incremental/struct_change_nothing.rs b/src/test/incremental/struct_change_nothing.rs
new file mode 100644 (file)
index 0000000..8095e1e
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test incremental compilation tracking where we change nothing
+// in between revisions (hashing should be stable).
+
+// revisions:rpass1 rpass2
+
+#![feature(rustc_attrs)]
+
+#[cfg(rpass1)]
+pub struct X {
+    pub x: u32
+}
+
+#[cfg(rpass2)]
+pub struct X {
+    pub x: u32
+}
+
+pub struct EmbedX {
+    x: X
+}
+
+pub struct Y {
+    pub y: char
+}
+
+#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_X() -> u32 {
+    let x: X = X { x: 22 };
+    x.x as u32
+}
+
+#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_EmbedX(x: EmbedX) -> u32 {
+    let x: X = X { x: 22 };
+    x.x as u32
+}
+
+#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_Y() {
+    let x: Y = Y { y: 'c' };
+}
+
+pub fn main() { }
diff --git a/src/test/incremental/struct_remove_field.rs b/src/test/incremental/struct_remove_field.rs
new file mode 100644 (file)
index 0000000..ae63994
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test incremental compilation tracking where we change field names
+// in between revisions (hashing should be stable).
+
+// revisions:rpass1 rpass2
+
+#![feature(rustc_attrs)]
+
+#[cfg(rpass1)]
+pub struct X {
+    pub x: u32,
+    pub x2: u32,
+}
+
+#[cfg(rpass2)]
+pub struct X {
+    pub x: u32,
+}
+
+pub struct EmbedX {
+    x: X
+}
+
+pub struct Y {
+    pub y: char
+}
+
+#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_X(x: X) -> u32 {
+    x.x as u32
+}
+
+#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_EmbedX(embed: EmbedX) -> u32 {
+    embed.x.x as u32
+}
+
+#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+pub fn use_Y() {
+    let x: Y = Y { y: 'c' };
+}
+
+pub fn main() { }
index 2494dca0509b16ea79923b3681388b21e0422adb..e1dba1317703d6c448b969d1327a3884b8cc113c 100644 (file)
@@ -16,6 +16,8 @@ pub type X = u32;
 #[cfg(rpass2)]
 pub type X = i32;
 
-pub type Y = char;
+// this version doesn't actually change anything:
+#[cfg(rpass3)]
+pub type X = i32;
 
-pub fn foo() { }
+pub type Y = char;
index b4e9b7601010a9e69c5b0f1d5ff0b41f278f768c..c5421fcbf5cb2310362327ad5ffe0f9e1e8b470e 100644 (file)
@@ -9,19 +9,21 @@
 // except according to those terms.
 
 // aux-build:a.rs
-// revisions:rpass1 rpass2
+// revisions:rpass1 rpass2 rpass3
 
 #![feature(rustc_attrs)]
 
 extern crate a;
 
 #[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+#[rustc_clean(label="TypeckItemBody", cfg="rpass3")]
 pub fn use_X() -> u32 {
     let x: a::X = 22;
     x as u32
 }
 
 #[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+#[rustc_clean(label="TypeckItemBody", cfg="rpass3")]
 pub fn use_Y() {
     let x: a::Y = 'c';
 }
diff --git a/src/test/parse-fail/issue-33455.rs b/src/test/parse-fail/issue-33455.rs
new file mode 100644 (file)
index 0000000..9607033
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use foo.bar; //~ ERROR expected one of `::`, `;`, or `as`, found `.`
diff --git a/src/test/parse-fail/issue-33569.rs b/src/test/parse-fail/issue-33569.rs
new file mode 100644 (file)
index 0000000..130278d
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z no-analysis
+
+macro_rules! foo {
+    { $+ } => { //~ ERROR expected identifier, found `+`
+        $(x)(y) //~ ERROR expected `*` or `+`
+                //~^ ERROR no rules expected the token `y`
+    }
+}
index 5ed8f6dee8cd86b598730b668ef4a1931ddab91c..fb78e558a951a7c6cbac3968b5f0a3dfaeb9f669 100644 (file)
@@ -9,6 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    let Test(&desc[..]) = x; //~ error: expected one of `,` or `@`, found `[`
-    //~^ ERROR expected one of `:`, `;`, `=`, or `@`, found `[`
+    let Test(&desc[..]) = x; //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
 }
index 00681e61497852663f046212b30f8656613fbc39..d75589d8889e3381613a6f55a755dac09fb896c2 100644 (file)
@@ -9,6 +9,5 @@
 // except according to those terms.
 
 fn main() {
-    for thing(x[]) in foo {} //~ error: expected one of `,` or `@`, found `[`
-    //~^ ERROR expected one of `@` or `in`, found `[`
+    for thing(x[]) in foo {} //~ ERROR: expected one of `)`, `,`, or `@`, found `[`
 }
diff --git a/src/test/parse-fail/pat-tuple-1.rs b/src/test/parse-fail/pat-tuple-1.rs
new file mode 100644 (file)
index 0000000..945d074
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (, ..) => {} //~ ERROR expected pattern, found `,`
+    }
+}
diff --git a/src/test/parse-fail/pat-tuple-2.rs b/src/test/parse-fail/pat-tuple-2.rs
new file mode 100644 (file)
index 0000000..ad52fa5
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (pat, ..,) => {} //~ ERROR expected pattern, found `)`
+    }
+}
diff --git a/src/test/parse-fail/pat-tuple-3.rs b/src/test/parse-fail/pat-tuple-3.rs
new file mode 100644 (file)
index 0000000..029dc7a
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (.., pat, ..) => {} //~ ERROR `..` can only be used once per tuple or tuple struct pattern
+    }
+}
diff --git a/src/test/parse-fail/pat-tuple-4.rs b/src/test/parse-fail/pat-tuple-4.rs
new file mode 100644 (file)
index 0000000..f4c3afa
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (.. pat) => {} //~ ERROR expected one of `)` or `,`, found `pat`
+    }
+}
diff --git a/src/test/parse-fail/pat-tuple-5.rs b/src/test/parse-fail/pat-tuple-5.rs
new file mode 100644 (file)
index 0000000..145d1f9
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (pat ..) => {} //~ ERROR expected one of `)`, `,`, or `@`, found `..`
+    }
+}
diff --git a/src/test/parse-fail/pat-tuple-6.rs b/src/test/parse-fail/pat-tuple-6.rs
new file mode 100644 (file)
index 0000000..3252d92
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -Z parse-only
+
+fn main() {
+    match 0 {
+        (pat) => {} //~ ERROR expected one of `,` or `@`, found `)`
+    }
+}
diff --git a/src/test/parse-fail/trait-non-item-macros.rs b/src/test/parse-fail/trait-non-item-macros.rs
new file mode 100644 (file)
index 0000000..fd356f4
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! bah {
+    ($a:expr) => ($a)
+    //~^ ERROR expected one of `const`, `extern`, `fn`, `type`, or `unsafe`, found `2`
+}
+
+trait bar {
+    bah!(2);
+}
+
+fn main() {}
diff --git a/src/test/pretty/attr-variant-data.rs b/src/test/pretty/attr-variant-data.rs
new file mode 100644 (file)
index 0000000..1ffacaa
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// pp-exact
+// Testing that both the inner item and next outer item are
+// preserved, and that the first outer item parsed in main is not
+// accidentally carried over to each inner function
+
+#![feature(custom_attribute)]
+#![feature(custom_derive)]
+
+#[derive(Serialize, Deserialize)]
+struct X;
+
+#[derive(Serialize, Deserialize)]
+struct WithRef<'a, T: 'a> {
+    #[serde(skip_deserializing)]
+    t: Option<&'a T>,
+    #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
+    x: X,
+}
+
+#[derive(Serialize, Deserialize)]
+enum EnumWith<T> {
+    Unit,
+    Newtype(
+            #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
+            X),
+    Tuple(T,
+          #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
+          X),
+    Struct {
+        t: T,
+        #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
+        x: X,
+    },
+}
+
+#[derive(Serialize, Deserialize)]
+struct Tuple<T>(T,
+                #[serde(serialize_with = "ser_x", deserialize_with = "de_x")]
+                X);
+
+fn main() { }
diff --git a/src/test/pretty/lifetime.rs b/src/test/pretty/lifetime.rs
new file mode 100644 (file)
index 0000000..2cc7153
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// pp-exact
+
+fn f1<'a, 'b, 'c>(_x: &'a u32, _y: &'b u32, _z: &'c u32) where 'c: 'a + 'b { }
+
+fn main() { }
index cca7707509f0c7f335d3581aa99345e5403df8d7..388064a8be8546cd0078ef0abf0899b00d1c733d 100644 (file)
@@ -10,6 +10,6 @@
 
 // pp-exact
 
-fn f<'a, 'b, T>(t: T) -> isize where T: 'a, 'a:'b, T: Eq { 0 }
+fn f<'a, 'b, T>(t: T) -> isize where T: 'a, 'a: 'b, T: Eq { 0 }
 
 fn main() { }
index 41a6fd05c3741fb6090cc956442c5ee0bfb8ae2c..e1461c7847e4ca5050cc33ad598533cce26096da 100644 (file)
 #![feature(quote, rustc_private)]
 
 extern crate syntax;
+extern crate syntax_pos;
 
 use syntax::ast;
-use syntax::codemap::{self, DUMMY_SP};
+use syntax::codemap;
 use syntax::parse;
 use syntax::print::pprust;
+use syntax_pos::DUMMY_SP;
 
 fn main() {
     let ps = syntax::parse::ParseSess::new();
-    let mut feature_gated_cfgs = vec![];
+    let mut loader = syntax::ext::base::DummyMacroLoader;
     let mut cx = syntax::ext::base::ExtCtxt::new(
         &ps, vec![],
         syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
-        &mut feature_gated_cfgs);
+        &mut loader);
     cx.bt_push(syntax::codemap::ExpnInfo {
         call_site: DUMMY_SP,
         callee: syntax::codemap::NameAndSpan {
index 47831f1af737bb47f465d936ec1b94d805bdb6c2..b8fb03faf0802b9680f74292606238ad5e09351b 100644 (file)
 #![allow(unknown_features)]
 #![feature(box_syntax)]
 
-fn f(_a: isize, _b: isize, _c: Box<isize>) { panic!("moop"); }
+fn f(_a: isize, _b: isize, _c: Box<isize>) {
+    panic!("moop");
+}
 
-fn main() { f(1, panic!("meep"), box 42); }
+fn main() {
+    f(1, panic!("meep"), box 42);
+}
index 0b35062b186e9227bebbbb35c022fa4454f9df5a..a3e0a1f904faa0353ff30665470ef3441b706fde 100644 (file)
@@ -11,5 +11,5 @@
 // error-pattern:assertion failed: `(left == right)` (left: `14`, right: `15`)
 
 fn main() {
-    assert_eq!(14,15);
+    assert_eq!(14, 15);
 }
index 8cabd3b326296aa34362aeedaa51285fb63bd50b..5be9cd4a9bc345be939b9d3880b69721c092e08b 100644 (file)
@@ -9,7 +9,9 @@
 // except according to those terms.
 
 // error-pattern:quux
-fn foo() -> ! { panic!("quux"); }
+fn foo() -> ! {
+    panic!("quux");
+}
 fn main() {
     foo() == foo(); // these types wind up being defaulted to ()
 }
index 159c33198a6e40f52ee4d643e5036b405cae0b0f..fb2db7ea9985c1ab57862d0b8668829ed0016d56 100644 (file)
@@ -9,5 +9,10 @@
 // except according to those terms.
 
 // error-pattern:quux
-fn my_err(s: String) -> ! { println!("{}", s); panic!("quux"); }
-fn main() { 3_usize == my_err("bye".to_string()); }
+fn my_err(s: String) -> ! {
+    println!("{}", s);
+    panic!("quux");
+}
+fn main() {
+    3_usize == my_err("bye".to_string());
+}
index 5e3da8476af3fe8f5c74f982c4e8b8f28e620755..4a294d8fabc38a1564c24c8bdafccd43a54356e8 100644 (file)
@@ -20,7 +20,7 @@ fn main() {
     // address of the 0th cell in the array (even though the index is
     // huge).
 
-    let x = vec!(1_usize,2_usize,3_usize);
+    let x = vec![1_usize, 2_usize, 3_usize];
 
     let base = x.as_ptr() as usize;
     let idx = base / mem::size_of::<usize>();
@@ -28,7 +28,7 @@ fn main() {
     println!("ov1 idx = 0x{:x}", idx);
     println!("ov1 sizeof::<usize>() = 0x{:x}", mem::size_of::<usize>());
     println!("ov1 idx * sizeof::<usize>() = 0x{:x}",
-           idx * mem::size_of::<usize>());
+             idx * mem::size_of::<usize>());
 
     // This should panic.
     println!("ov1 0x{:x}", x[idx]);
index fc64d7c1ba356768fecfbb81462d409dbe188a48..c0e02ccd61be25ba5b4a28574cb73333c4f3cf1c 100644 (file)
@@ -12,7 +12,9 @@
 
 use std::marker::PhantomData;
 
-fn test00_start(ch: chan_t<isize>, message: isize) { send(ch, message); }
+fn test00_start(ch: chan_t<isize>, message: isize) {
+    send(ch, message);
+}
 
 type task_id = isize;
 type port_id = isize;
@@ -23,6 +25,10 @@ struct chan_t<T> {
     marker: PhantomData<*mut T>,
 }
 
-fn send<T:Send>(_ch: chan_t<T>, _data: T) { panic!(); }
+fn send<T: Send>(_ch: chan_t<T>, _data: T) {
+    panic!();
+}
 
-fn main() { panic!("quux"); }
+fn main() {
+    panic!("quux");
+}
index d3817b25d6100f6d364ec3515c20102ad0e7b665..3d9bee3c86a569fe8bc6691925afa1c04e2c3cd1 100644 (file)
@@ -12,8 +12,6 @@
 
 // error-pattern:attempted to divide by zero
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let y = 0;
     let _z = 1 / y;
index 3835a16a5c2631beb29f745a31e1e9f80042c00d..936ebd96897371008746fc059cf48e56776a6437 100644 (file)
@@ -10,7 +10,7 @@
 
 #![allow(unreachable_code)]
 
-//error-pattern:One
+// error-pattern:One
 fn main() {
     panic!("One");
     panic!("Two");
index c9c04e5f2daab8614e703fc17eb3d1839e5b1929..907fae02b419b5ec76c52c30a69cdafb60b5282f 100644 (file)
@@ -14,6 +14,8 @@
 // error-pattern:wooooo
 fn main() {
     let mut a = 1;
-    if 1 == 1 { a = 2; }
+    if 1 == 1 {
+        a = 2;
+    }
     panic!(format!("woooo{}", "o"));
 }
index 4699897bf8abea7d73caa7b5ff4bdcca4610ad07..928e7326b66cd8cb6b1d10a43395729bcbc66159 100644 (file)
@@ -12,4 +12,6 @@
 
 
 // error-pattern:explicit
-fn main() { panic!(); }
+fn main() {
+    panic!();
+}
index 8cf018fb7702dc44ccf92760a2191e891777be95..f7a889754257b574821f822350b2fec2e2d58a54 100644 (file)
 
 // error-pattern:explicit panic
 
-fn f() -> ! { panic!() }
+fn f() -> ! {
+    panic!()
+}
 
-fn main() { f(); }
+fn main() {
+    f();
+}
index e9f493c16f1716d2a4d26d0d873ada4c895e731e..a8ec8f3f414621f8443658d04a8e37bb6405be63 100644 (file)
 
 // error-pattern:explicit panic
 
-fn f() -> ! { panic!() }
+fn f() -> ! {
+    panic!()
+}
 
-fn g() -> isize { let x = if true { f() } else { 10 }; return x; }
+fn g() -> isize {
+    let x = if true {
+        f()
+    } else {
+        10
+    };
+    return x;
+}
 
-fn main() { g(); }
+fn main() {
+    g();
+}
index b6791271a11ba98ba66024f69f5af625a22e32b2..25bf43751f0b3c1f76f4478a350d5e3cdbb15eab 100644 (file)
 
 // error-pattern:explicit panic
 
-fn main() { let _x = if false { 0 } else if true { panic!() } else { 10 }; }
+fn main() {
+    let _x = if false {
+        0
+    } else if true {
+        panic!()
+    } else {
+        10
+    };
+}
index 0269eb0af9c34347f4c819263eede16318f130c1..6758ac6c4d4d8eb2045446ce2d0d798e4710c8ba 100644 (file)
 
 // error-pattern:explicit panic
 
-fn f() -> ! { panic!() }
+fn f() -> ! {
+    panic!()
+}
 
-fn g() -> isize { let x = match true { true => { f() } false => { 10 } }; return x; }
+fn g() -> isize {
+    let x = match true {
+        true => f(),
+        false => 10,
+    };
+    return x;
+}
 
-fn main() { g(); }
+fn main() {
+    g();
+}
index 3a6bd59b3acaa44133f76487ecb5377b2872c097..8876fb1f49b8234150a5d57f5c3e290b5e44d4fa 100644 (file)
@@ -10,4 +10,9 @@
 
 // error-pattern:explicit panic
 
-fn main() { let _x = match true { false => { 0 } true => { panic!() } }; }
+fn main() {
+    let _x = match true {
+        false => 0,
+        true => panic!(),
+    };
+}
index a1a760c040c75f75eab27e19e3e05e008794ce5c..a462d83601921578cd71cae7a5d2b40a8c7704d5 100644 (file)
@@ -10,4 +10,8 @@
 
 // error-pattern:moop
 
-fn main() { for _ in 0_usize..10_usize { panic!("moop"); } }
+fn main() {
+    for _ in 0_usize..10_usize {
+        panic!("moop");
+    }
+}
index 8c4caccdb65978e079afece2869d1ee363020876..f8b2d11cb646d720aec1b46062d069087b61bd82 100644 (file)
 fn even(x: usize) -> bool {
     if x < 2 {
         return false;
-    } else if x == 2 { return true; } else { return even(x - 2); }
+    } else if x == 2 {
+        return true;
+    } else {
+        return even(x - 2);
+    }
 }
 
 fn foo(x: usize) {
@@ -23,4 +27,6 @@ fn foo(x: usize) {
     }
 }
 
-fn main() { foo(3); }
+fn main() {
+    foo(3);
+}
index f38b00ab46d90ee92785f022456ee5a04393a025..203bc8fc65ff8a17f5ff7d7770473e766eb3074f 100644 (file)
@@ -9,5 +9,11 @@
 // except according to those terms.
 
 // error-pattern:quux
-fn my_err(s: String) -> ! { println!("{}", s); panic!("quux"); }
-fn main() { if my_err("bye".to_string()) { } }
+fn my_err(s: String) -> ! {
+    println!("{}", s);
+    panic!("quux");
+}
+fn main() {
+    if my_err("bye".to_string()) {
+    }
+}
index cbc92c640d2ca6816e311bfca1336a5b3bb03357..39a819f3d5204cb0229f298578d8389a2cdf7512 100644 (file)
@@ -11,5 +11,6 @@
 // error-pattern:explicit panic
 
 pub fn main() {
-    panic!(); println!("{}", 1);
+    panic!();
+    println!("{}", 1);
 }
index e326949458ebf86fe43ded57c79ec0a2d8bd853f..88fb8f1b0c4ccd87446df3a64add2c5aa6674289 100644 (file)
@@ -20,4 +20,4 @@ fn main() {
     let pointer = other;
     pointer();
 }
-extern fn other() {}
+extern "C" fn other() {}
index 818f0e139419670a29241a6aed975b5be0591634..e433a45731f95e5159f5f7f96813c1ec3b5a59c0 100644 (file)
@@ -19,16 +19,13 @@ pub trait Parser {
 
 impl Parser for () {
     type Input = ();
-    fn parse(&mut self, input: ()) {
-
-    }
+    fn parse(&mut self, input: ()) {}
 }
 
-pub fn many() -> Box<Parser<Input=<() as Parser>::Input> + 'static> {
+pub fn many() -> Box<Parser<Input = <() as Parser>::Input> + 'static> {
     panic!("Hello, world!")
 }
 
 fn main() {
-    many()
-        .parse(());
+    many().parse(());
 }
index ce91af95d96b042bbde270876f02c22182a5b478..f55b1ba03dee87ae8edfb1fb0bae84a93841c879 100644 (file)
 
 use std::sync::Arc;
 
-enum e<T> { ee(Arc<T>) }
+enum e<T> {
+    ee(Arc<T>),
+}
 
-fn foo() -> e<isize> {panic!();}
+fn foo() -> e<isize> {
+    panic!();
+}
 
 fn main() {
-   let _f = foo();
+    let _f = foo();
 }
index 2f437c7a814793f91dc7410aa7f949e26c26ce16..b32a504cb6beee0e9b2044b34f2dd725a419ac7f 100644 (file)
@@ -17,9 +17,14 @@ struct Parser<'i: 't, 't>(&'i u8, &'t u8);
 
 impl<'i, 't> Parser<'i, 't> {
     fn parse_nested_block<F, T>(&mut self, parse: F) -> Result<T, ()>
-        where for<'tt> F: FnOnce(&mut Parser<'i, 'tt>) -> T { panic!() }
+        where for<'tt> F: FnOnce(&mut Parser<'i, 'tt>) -> T
+    {
+        panic!()
+    }
 
-    fn expect_exhausted(&mut self) -> Result<(), ()> { Ok(()) }
+    fn expect_exhausted(&mut self) -> Result<(), ()> {
+        Ok(())
+    }
 }
 
 fn main() {
index 4d048fe0fcf4b2c8e3f1286e9f84275502fc0a84..1ada7771cd66c718a706f36b8f86eca06f5d4636 100644 (file)
@@ -16,7 +16,7 @@
 // error-pattern:so long
 fn main() {
     let mut x = Vec::new();
-    let y = vec!(3);
+    let y = vec![3];
     panic!("so long");
     x.extend(y.into_iter());
 }
diff --git a/src/test/run-fail/issue-30380.rs b/src/test/run-fail/issue-30380.rs
new file mode 100644 (file)
index 0000000..eb66851
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+// check that panics in destructors during assignment do not leave
+// destroyed values lying around for other destructors to observe.
+
+// error-pattern:panicking destructors ftw!
+
+struct Observer<'a>(&'a mut FilledOnDrop);
+
+struct FilledOnDrop(u32);
+impl Drop for FilledOnDrop {
+    fn drop(&mut self) {
+        if self.0 == 0 {
+            // this is only set during the destructor - safe
+            // code should not be able to observe this.
+            self.0 = 0x1c1c1c1c;
+            panic!("panicking destructors ftw!");
+        }
+    }
+}
+
+impl<'a> Drop for Observer<'a> {
+    fn drop(&mut self) {
+        assert_eq!(self.0 .0, 1);
+    }
+}
+
+#[rustc_mir]
+fn foo(b: &mut Observer) {
+    *b.0 = FilledOnDrop(1);
+}
+
+fn main() {
+    let mut bomb = FilledOnDrop(0);
+    let mut observer = Observer(&mut bomb);
+    foo(&mut observer);
+}
index 631517f6a3ca6ca937a5a52134dc65425d09aa36..7da27bd15f24ac4679c5b42e151cbc7379323060 100644 (file)
@@ -11,4 +11,6 @@
 // error-pattern:explicit panic
 
 fn foo<T>(t: T) {}
-fn main() { foo(panic!()) }
+fn main() {
+    foo(panic!())
+}
index 272d85d7b508bbf2e0fa3c6985a6f4a311493ac6..4a1bc856a39c3e86b681fedbdbe705306b771bb7 100644 (file)
 
 #![allow(unused_variables)]
 
-struct Point { x: isize, y: isize }
+struct Point {
+    x: isize,
+    y: isize,
+}
 
 fn main() {
-    let origin = Point {x: 0, y: 0};
-    let f: Point = Point {x: (panic!("beep boop")),.. origin};
+    let origin = Point { x: 0, y: 0 };
+    let f: Point = Point { x: (panic!("beep boop")), ..origin };
 }
index b8ff1be71bf2b0e231161fd90680656c0f412d9c..87f77681fa02dd3b2a36b3191aab3a76b8cdee0f 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:thread '<main>' panicked at
+// error-pattern:thread 'main' panicked at
 
 fn main() {
     panic!()
index c1f90bb8f2b3c81d22fa78b4daa89bcfc20f6e20..5a6eedb4863a920cf7bf68ce2414dd5cc9b8d1cf 100644 (file)
 #![allow(unreachable_code)]
 #![allow(unused_variables)]
 
-fn foo(s: String) { }
+fn foo(s: String) {}
 
 fn main() {
-    let i =
-        match Some::<isize>(3) { None::<isize> => { panic!() } Some::<isize>(_) => { panic!() } };
+    let i = match Some::<isize>(3) {
+        None::<isize> => panic!(),
+        Some::<isize>(_) => panic!(),
+    };
     foo(i);
 }
index 90b729a6dd271ee1ea5d4890db711e0a7a9122f5..a369a9889cc59c9438d416c526715ed745d05e3b 100644 (file)
@@ -9,6 +9,15 @@
 // except according to those terms.
 
 // error-pattern:quux
-fn f() -> ! { panic!("quux") }
-fn g() -> isize { match f() { true => { 1 } false => { 0 } } }
-fn main() { g(); }
+fn f() -> ! {
+    panic!("quux")
+}
+fn g() -> isize {
+    match f() {
+        true => 1,
+        false => 0,
+    }
+}
+fn main() {
+    g();
+}
index 54e24de31653106614e92b960b08d277e70f05e9..61bfd38c5f4c8ed6798504c371c2f395d7a3e66a 100644 (file)
 // error-pattern:squirrelcupcake
 fn cmp() -> isize {
     match (Some('a'), None::<char>) {
-        (Some(_), _) => { panic!("squirrelcupcake"); }
-        (_, Some(_)) => { panic!(); }
-        _                    => { panic!("wat"); }
+        (Some(_), _) => {
+            panic!("squirrelcupcake");
+        }
+        (_, Some(_)) => {
+            panic!();
+        }
+        _ => {
+            panic!("wat");
+        }
     }
 }
 
-fn main() { println!("{}", cmp()); }
+fn main() {
+    println!("{}", cmp());
+}
index bf521d4b4e5e4eb638634be687fea8785bf2da3e..0b7c464800aa82e05a8b62b6cda27c0c57994194 100644 (file)
 //[foo] error-pattern:bar
 //[bar] error-pattern:foo
 
-#[cfg(foo)] fn die() {panic!("foo");}
-#[cfg(bar)] fn die() {panic!("bar");}
+#[cfg(foo)]
+fn die() {
+    panic!("foo");
+}
+#[cfg(bar)]
+fn die() {
+    panic!("bar");
+}
 
-fn main() { die(); }
+fn main() {
+    die();
+}
index f74ec39fdf27a031234680707277e75e410d01e6..99dd332c558b0d93e27f0f50961d35222c60fcc0 100644 (file)
 //[foo] error-pattern:foo
 //[bar] error-pattern:bar
 
-#[cfg(foo)] fn die() {panic!("foo");}
-#[cfg(bar)] fn die() {panic!("bar");}
+#[cfg(foo)]
+fn die() {
+    panic!("foo");
+}
+#[cfg(bar)]
+fn die() {
+    panic!("bar");
+}
 
-fn main() { die(); }
+fn main() {
+    die();
+}
index 590b9fbe43cf5e49130a26c13cc74dcf2cb55d4f..16160a1496ff9d1dc20eb2824e7cd9bbbbd9bdb4 100644 (file)
@@ -27,7 +27,7 @@ impl<'a> Drop for Droppable<'a> {
 }
 
 #[rustc_mir]
-fn mir(){
+fn mir() {
     let (mut xv, mut yv) = (false, false);
     let x = Droppable(&mut xv, 1);
     let y = Droppable(&mut yv, 2);
index eafd3d351fb939030615ba69482c9798a854c3c1..803ca53bf7a848c00aa4b13a4653b4d237950ff7 100644 (file)
@@ -26,7 +26,7 @@ impl<'a> Drop for Droppable<'a> {
 }
 
 #[rustc_mir]
-fn mir<'a>(d: Droppable<'a>){
+fn mir<'a>(d: Droppable<'a>) {
     loop {
         let x = d;
         break;
index 730d9c8f22681be8860431e28499f25a5774c506..afc037f48aa43723b04e29454062927dde1416c6 100644 (file)
@@ -33,7 +33,7 @@ fn may_panic<'a>() -> Droppable<'a> {
 }
 
 #[rustc_mir]
-fn mir<'a>(d: Droppable<'a>){
+fn mir<'a>(d: Droppable<'a>) {
     let (mut a, mut b) = (false, false);
     let y = Droppable(&mut a, 2);
     let x = [Droppable(&mut b, 1), y, d, may_panic()];
index 7a151c8c572f6dfe7db13bf5576f34e1c9a1acb5..686c3eb2f83b860ad66b29eb85cd439463973289 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:attempted remainder with a divisor of zero
+// error-pattern:attempted to calculate the remainder with a divisor of zero
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let y = 0;
     let _z = 1 % y;
index c989cc594536ba11c02a1a65a218eef1dc01929c..ecb8c676cf700d689cda252865747949a14bc3f5 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to add with overflow'
 // compile-flags: -C debug-assertions
 
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 200u8 + 200u8 + 200u8;
 }
index a27210112982a6a76f932f21264f2d757ce8efe8..e277886d003dce43e3ea65bc456386b109158061 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift left with overflow'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 1_i32 << 32;
 }
index fe0bcc5b98545f62ec02fae195cd229d648a4437..42cb0f2d55bc1ba90c2a1223c995ad3d2bed71a5 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift left with overflow'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 1 << -1;
 }
index aac220d32d9ce4dfa51e12670fb866914d31d5b7..8c6623dcf50ceeff50c1225bbf6e54c0013f47f2 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift left with overflow'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 1_u64 << 64;
 }
index 7e8b266da49bee83dcbd81e26ba980107861a028..3b7a00a2c73c6dd3cba02828e4bdec4b19428f32 100644 (file)
@@ -10,7 +10,7 @@
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift left with overflow'
 // compile-flags: -C debug-assertions
 
 // This function is checking that our automatic truncation does not
@@ -18,8 +18,6 @@
 
 #![warn(exceeding_bitshifts)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     // this signals overflow when checking is on
     let x = 1_i8 << 17;
index 8cba700bbf9a3e0b59faa56765dadaf25407a4b4..0e168bf6ffbc6e3c60b8ec0e60438b472c344a6a 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to multiply with overflow'
 // compile-flags: -C debug-assertions
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let x = 200u8 * 4;
 }
index 2d9d746bef324517277816ca0a88064601a8ea48..84e41ea848809b2ff82637112cba4e0682879500 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'attempted to negate with overflow'
+// error-pattern:thread 'main' panicked at 'attempted to negate with overflow'
 // compile-flags: -C debug-assertions
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = -std::i8::MIN;
 }
index 15335b8dfb12e4d6a9912e8db3fe297cc1af1b75..9172374ec2818b41e7a1a2e48b7de662271eef68 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to multiply with overflow'
 // compile-flags: -C debug-assertions
 
 fn main() {
index 63c808dc80a4ecebe1b0caa986d2f519d70d0b57..d275792485d5126851e89d77f94714d86f2df4b1 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = -1_i32 >> 32;
 }
index 8b89e57c85bb5e33e254d83c4ae244e271dc4347..1b888cddf64436a9e7ffe5ea54e33b0512adacdd 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = -1_i32 >> -1;
 }
index 8874587064c35ec26f45bf7cc1e4ebdc09f565ba..be5c213493d6dbb90dfa2d2489d127f17920e033 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = -1_i64 >> 64;
 }
index d74fd8a6b8e417abcd3d87c568668898dbea34c0..820d9611d6acb973922b2cf36fadb603dd136612 100644 (file)
@@ -10,7 +10,7 @@
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
 // compile-flags: -C debug-assertions
 
 // This function is checking that our (type-based) automatic
@@ -18,8 +18,6 @@
 
 #![warn(exceeding_bitshifts)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     // this signals overflow when checking is on
     let x = 2_i8 >> 17;
index 249b952a5dca2643fec1d751066561c8327a3e64..b87be696fcb213ba699d2715985a990b30b567d1 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _n = 1i64 >> [64][0];
 }
index 1227f35444a60399a1f60125f405c1e3079fbf6c..554675686b5e293d80b54f9bb905d112ffe10f61 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'shift operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to shift right with overflow'
 // compile-flags: -C debug-assertions
 
 #![warn(exceeding_bitshifts)]
 #![feature(const_indexing)]
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _n = 1i64 >> [64][0];
 }
index ce243a50e0b66f2065b84c038af2279041d09404..1cb207240ca492e2b8185733e7a51ae136d701a2 100644 (file)
 
 // ignore-pretty : (#23623) problems when  ending with // comments
 
-// error-pattern:thread '<main>' panicked at 'arithmetic operation overflowed'
+// error-pattern:thread 'main' panicked at 'attempted to subtract with overflow'
 // compile-flags: -C debug-assertions
 
-#![feature(rustc_attrs)]
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     let _x = 42u8 - (42u8 + 1);
 }
index 0e029b6ecbc85a71068fcd89862c016ad6b00328..9023784050f2f096c8e8691a025a49618708b817 100644 (file)
@@ -9,6 +9,10 @@
 // except according to those terms.
 
 // error-pattern:woe
-fn f(a: isize) { println!("{}", a); }
+fn f(a: isize) {
+    println!("{}", a);
+}
 
-fn main() { f(panic!("woe")); }
+fn main() {
+    f(panic!("woe"));
+}
index ce6a5d46cc740b1ec75a3395fa7d0887a4ee7e85..7ca45565d854bee82f59a485a11ce83353ca86c0 100644 (file)
@@ -14,5 +14,5 @@
 #![feature(box_syntax)]
 
 fn main() {
-    panic!(box 413 as Box<::std::any::Any+Send>);
+    panic!(box 413 as Box<::std::any::Any + Send>);
 }
index 877ea9cd0a4308524c0da40fcc115dd8bcccd0fb..fd2919bfe12fcb8faa1d21e87209a8b87ad70b16 100644 (file)
@@ -9,4 +9,6 @@
 // except according to those terms.
 
 // error-pattern:moop
-fn main() { panic!("moop"); }
+fn main() {
+    panic!("moop");
+}
index 06655e4c6813277c0b0b70e7fab4cdfc8964d7fc..1917a7e2a7ff18db7bca82340edbfbcef6eabca7 100644 (file)
 // error-pattern:oops
 
 fn bigpanic() {
-    while (panic!("oops")) { if (panic!()) {
-        match (panic!()) { () => {
+    while (panic!("oops")) {
+        if (panic!()) {
+            match (panic!()) {
+                () => {}
+            }
         }
-                     }
-    }};
+    }
 }
 
-fn main() { bigpanic(); }
+fn main() {
+    bigpanic();
+}
index bfeb407dd25a439dbfb51497fb1fe2883de0cc4e..b589544ae156359eb62e260d9f232856090ebf89 100644 (file)
 
 // error-pattern:greetings from the panic handler
 
-#![feature(std_panic, panic_handler)]
+#![feature(panic_handler)]
+
 use std::panic;
 use std::io::{self, Write};
 
 fn main() {
-    panic::set_handler(|i| {
+    panic::set_hook(Box::new(|i| {
         write!(io::stderr(), "greetings from the panic handler");
-    });
+    }));
     panic!("foobar");
 }
index 6999aa715e791b7d2346581afe5cb9c2bab60b15..6741c2d9c2c2028d40bf1c252e6e22f0738e9e15 100644 (file)
@@ -8,16 +8,17 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:thread '<main>' panicked at 'foobar'
+// error-pattern:thread 'main' panicked at 'foobar'
+
+#![feature(panic_handler)]
 
-#![feature(std_panic, panic_handler)]
 use std::panic;
 use std::io::{self, Write};
 
 fn main() {
-    panic::set_handler(|i| {
+    panic::set_hook(Box::new(|i| {
         write!(io::stderr(), "greetings from the panic handler");
-    });
-    panic::take_handler();
+    }));
+    panic::take_hook();
     panic!("foobar");
 }
index fec1db24adf09cc50446bff45b74ef0eb0a2f6b7..0add63c6d64f22db63633884f735b119c47f4676 100644 (file)
@@ -8,12 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:thread '<main>' panicked at 'foobar'
+// error-pattern:thread 'main' panicked at 'foobar'
+
+#![feature(panic_handler)]
 
-#![feature(std_panic, panic_handler)]
 use std::panic;
 
 fn main() {
-    panic::take_handler();
+    panic::take_hook();
     panic!("foobar");
 }
index 3a5ac5a10095742c13772734fc3e9f1c8914e458..ab50503830534e7546ba0e89dd58807c8e7be3fd 100644 (file)
@@ -13,8 +13,9 @@
 use std::thread;
 
 fn main() {
-    let r: Result<(),_> = thread::spawn(move|| {
-        panic!("test");
-    }).join();
+    let r: Result<(), _> = thread::spawn(move || {
+                               panic!("test");
+                           })
+                               .join();
     assert!(r.is_ok());
 }
index 561f141100ca7c3a7c962f4a50c7b893598b162c..2d2371f5ce77c6d05fc58e85249b9442bdb580ea 100644 (file)
 use std::thread::Builder;
 
 fn main() {
-    let r: () = Builder::new().name("owned name".to_string()).spawn(move|| {
-        panic!("test");
-        ()
-    }).unwrap().join().unwrap();
+    let r: () = Builder::new()
+                    .name("owned name".to_string())
+                    .spawn(move || {
+                        panic!("test");
+                        ()
+                    })
+                    .unwrap()
+                    .join()
+                    .unwrap();
     panic!();
 }
index 6773c6b9b5184cb07995efd429e774a0acaee1bc..f59e6001794eb94de0eabab0c245f08633c2f7ea 100644 (file)
@@ -9,4 +9,6 @@
 // except according to those terms.
 
 // error-pattern:1 == 2
-fn main() { assert!(1 == 2); }
+fn main() {
+    assert!(1 == 2);
+}
index dbded1075442c10240261e204b4ffd238d6e6c1c..6378b4ec795405617901cc37b1987c8d6f50c372 100644 (file)
@@ -13,5 +13,5 @@
 use std::result::Result::Err;
 
 fn main() {
-    println!("{}", Err::<isize,String>("kitty".to_string()).unwrap());
+    println!("{}", Err::<isize, String>("kitty".to_string()).unwrap());
 }
index ff4040ded5f382722a9a1873f1a89f9e5050fe88..e16ce9c8edb85f5a29418fe4f86cba580f523c6d 100644 (file)
 #![allow(unreachable_code)]
 #![allow(unused_variables)]
 
-struct T { t: String }
+struct T {
+    t: String,
+}
 
 fn main() {
     let pth = panic!("bye");
-    let _rs: T = T {t: pth};
+    let _rs: T = T { t: pth };
 }
index 0e218740ab1cfb83db5db6819b8486077a9910d0..8158333ade818ae71af12135355cd246e10d6f13 100644 (file)
 // ignore-pretty: does not work well with `--test`
 
 mod m {
-    pub fn exported() { }
+    pub fn exported() {}
 
     #[test]
-    fn unexported() { panic!("runned an unexported test"); }
+    fn unexported() {
+        panic!("runned an unexported test");
+    }
 }
index 7eff1fee62579e5b112202e0eb804adf3243722b..e3d8aa2e460af8c98b18b3d190ccfaface31eeee 100644 (file)
@@ -9,4 +9,6 @@
 // except according to those terms.
 
 // error-pattern:not yet implemented
-fn main() { unimplemented!() }
+fn main() {
+    unimplemented!()
+}
index 83b2bb91f0073a59ba3418da547d8df276e47677..3dc3d0afda10b3fec88e81b4be5c97a6ed75327e 100644 (file)
@@ -10,4 +10,6 @@
 
 // error-pattern: panic
 
-fn main() { Box::new(panic!()); }
+fn main() {
+    Box::new(panic!());
+}
index 07e05c6fed95400a5ccf3ea8f706f0ee3289daea..493fe7ee4f8e71d454a6317eb2893f9423348247 100644 (file)
@@ -9,4 +9,6 @@
 // except according to those terms.
 
 // error-pattern:internal error: entered unreachable code
-fn main() { unreachable!() }
+fn main() {
+    unreachable!()
+}
index 25894a885413f02752baabd1fd4745e55e848ed0..0a9dee3d0b99c1e56f8d4f3c805dbe4f852bd4bf 100644 (file)
@@ -9,4 +9,6 @@
 // except according to those terms.
 
 // error-pattern:internal error: entered unreachable code: uhoh
-fn main() { unreachable!("uhoh") }
+fn main() {
+    unreachable!("uhoh")
+}
index 07e05c6fed95400a5ccf3ea8f706f0ee3289daea..493fe7ee4f8e71d454a6317eb2893f9423348247 100644 (file)
@@ -9,4 +9,6 @@
 // except according to those terms.
 
 // error-pattern:internal error: entered unreachable code
-fn main() { unreachable!() }
+fn main() {
+    unreachable!()
+}
index 91a33329a4f8431c672e990bdf0c03346f5b9ecb..0a7be154d5d152b324b0f4c3c5b0f555cca8caf4 100644 (file)
 
 // error-pattern:fail
 
-fn a() { }
+fn a() {}
 
-fn b() { panic!(); }
+fn b() {
+    panic!();
+}
 
 fn main() {
-    let _x = vec!(0);
+    let _x = vec![0];
     a();
-    let _y = vec!(0);
+    let _y = vec![0];
     b();
 }
index 6df279b047f64889133acf8045f95bee33e8a639..5177b4091d73db1e4a2e030b914552b6e21b6b37 100644 (file)
@@ -15,10 +15,10 @@ fn build() -> Vec<isize> {
     panic!();
 }
 
-struct Blk { node: Vec<isize> }
+struct Blk {
+    node: Vec<isize>,
+}
 
 fn main() {
-    let _blk = Blk {
-        node: build()
-    };
+    let _blk = Blk { node: build() };
 }
index d5d60d18924d542e28a62c6ccbaf764174bf2190..3be5036b216e01bc063a74890fef339bb5320fd0 100644 (file)
 
 
 fn build1() -> Vec<isize> {
-    vec!(0,0,0,0,0,0,0)
+    vec![0, 0, 0, 0, 0, 0, 0]
 }
 
 fn build2() -> Vec<isize> {
     panic!();
 }
 
-struct Blk { node: Vec<isize> , span: Vec<isize> }
+struct Blk {
+    node: Vec<isize>,
+    span: Vec<isize>,
+}
 
 fn main() {
     let _blk = Blk {
         node: build1(),
-        span: build2()
+        span: build2(),
     };
 }
index da52cd56a1a080d39044defa6051864cf4e8cc3d..457ae75a451e909767decdc8ee4678c458d850e6 100644 (file)
@@ -12,7 +12,7 @@
 
 
 fn main() {
-    let v: Vec<isize> = vec!(10);
+    let v: Vec<isize> = vec![10];
     let x: usize = 0;
     assert_eq!(v[x], 10);
     // Bounds-check panic.
index cfe499f8a4aeb9a28c85bc0fd61de0167581be88..29482612c24c741fe33914aeeee91e4f9519813b 100644 (file)
 #![allow(while_true)]
 
 // error-pattern:quux
-fn main() { let _x: isize = { while true { panic!("quux"); } ; 8 } ; }
+fn main() {
+    let _x: isize = {
+        while true {
+            panic!("quux");
+        }
+        8
+    };
+}
index f6081e497bffefaf16ae06f9706acd149b252233..e410684cd349a13468c497030a0dd9959714fddd 100644 (file)
 
 // error-pattern:giraffe
 fn main() {
-    panic!({ while true { panic!("giraffe") }; "clandestine" });
+    panic!({
+        while true {
+            panic!("giraffe")
+        }
+        "clandestine"
+    });
 }
index 8731cd960f33c133195f940f0e922f4e484d3154..023f2218b87ae7e7afaf853e5d953b53363da95e 100644 (file)
@@ -18,6 +18,8 @@ extern "rust-intrinsic" {
 
 #[lang = "sized"]
 trait Sized {}
+#[lang = "copy"]
+trait Copy {}
 
 #[cfg(target_has_atomic = "8")]
 pub unsafe fn atomic_u8(x: *mut u8) {
index fb54161c2c127db8dafd85cd069a9905bc173a89..65682cb86c36881c1ddaca9a586f469d3b97337f 100644 (file)
@@ -37,7 +37,6 @@ fn debug_assert() {
 }
 
 fn overflow() {
-    #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
     fn add(a: u8, b: u8) -> u8 { a + b }
 
     add(200u8, 200u8);
diff --git a/src/test/run-make/dep-info-no-analysis/Makefile b/src/test/run-make/dep-info-no-analysis/Makefile
deleted file mode 100644 (file)
index 5d2cfad..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
--include ../tools.mk
-
-all:
-       $(RUSTC) -o $(TMPDIR)/input.dd -Z no-analysis --emit dep-info input.rs
-       sed -i'.bak' 's/^.*input.dd/input.dd/g' $(TMPDIR)/input.dd
-       diff -u $(TMPDIR)/input.dd input.dd
diff --git a/src/test/run-make/dep-info-no-analysis/input.dd b/src/test/run-make/dep-info-no-analysis/input.dd
deleted file mode 100644 (file)
index f2c8676..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-input.dd: input.rs
-
-input.rs:
diff --git a/src/test/run-make/dep-info-no-analysis/input.rs b/src/test/run-make/dep-info-no-analysis/input.rs
deleted file mode 100644 (file)
index 523b0f0..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Tests that dep info can be emitted without resolving external crates.
-extern crate not_there;
-
-fn main() {}
index 0e84a0f52218c4bb75c9e01cec89c21135dfa2e5..21e1463b6ef955efdf9929e42acf2f4944c7deaa 100644 (file)
@@ -18,6 +18,8 @@ extern crate rustc_lint;
 extern crate rustc_llvm as llvm;
 extern crate rustc_metadata;
 extern crate rustc_resolve;
+extern crate rustc_errors;
+extern crate rustc_errors as errors;
 #[macro_use] extern crate syntax;
 
 use std::ffi::{CStr, CString};
@@ -38,7 +40,7 @@ use rustc_metadata::creader::read_local_crates;
 use rustc_metadata::cstore::CStore;
 use libc::c_void;
 
-use syntax::diagnostics::registry::Registry;
+use rustc_errors::registry::Registry;
 use syntax::parse::token;
 
 fn main() {
@@ -238,15 +240,10 @@ fn compile_program(input: &str, sysroot: PathBuf)
 
         let krate = panictry!(driver::phase_1_parse_input(&sess, cfg, &input));
 
-        let krate = driver::phase_2_configure_and_expand(&sess, &cstore, krate, &id, None)
-            .expect("phase_2 returned `None`");
-
-        let krate = driver::assign_node_ids(&sess, krate);
-        let mut defs = ast_map::collect_definitions(&krate);
-        read_local_crates(&sess, &cstore, &defs, &krate, &id, &dep_graph);
-        let (analysis, resolutions, mut hir_forest) = {
-            driver::lower_and_resolve(&sess, &id, &mut defs, &krate,
-                                      &sess.dep_graph, MakeGlobMap::No)
+        let driver::ExpansionResult { defs, analysis, resolutions, mut hir_forest, .. } = {
+            driver::phase_2_configure_and_expand(
+                &sess, &cstore, krate, &id, None, MakeGlobMap::No, |_| Ok(()),
+            ).expect("phase_2 returned `None`")
         };
 
         let arenas = ty::CtxtArenas::new();
index 41d250eadec4e1a64d2c53fe435f2bcdd6836664..aa3495ec5eebb56869e1476cbf3674752af59855 100644 (file)
@@ -14,6 +14,7 @@ extern crate rustc;
 extern crate rustc_driver;
 extern crate rustc_lint;
 extern crate rustc_metadata;
+extern crate rustc_errors;
 extern crate syntax;
 
 use rustc::dep_graph::DepGraph;
@@ -21,7 +22,7 @@ use rustc::session::{build_session, Session};
 use rustc::session::config::{basic_options, build_configuration, Input, OutputType};
 use rustc_driver::driver::{compile_input, CompileController, anon_src};
 use rustc_metadata::cstore::CStore;
-use syntax::diagnostics::registry::Registry;
+use rustc_errors::registry::Registry;
 use syntax::parse::token;
 
 use std::path::PathBuf;
diff --git a/src/test/run-make/llvm-phase/Makefile b/src/test/run-make/llvm-phase/Makefile
new file mode 100644 (file)
index 0000000..6a8e172
--- /dev/null
@@ -0,0 +1,5 @@
+-include ../tools.mk
+
+all:
+       $(RUSTC) test.rs
+       $(call RUN,test $(RUSTC))
diff --git a/src/test/run-make/llvm-phase/test.rs b/src/test/run-make/llvm-phase/test.rs
new file mode 100644 (file)
index 0000000..402b5ed
--- /dev/null
@@ -0,0 +1,82 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(plugin, rustc_private, box_syntax)]
+
+extern crate rustc;
+extern crate rustc_driver;
+extern crate rustc_llvm;
+#[macro_use] extern crate syntax;
+extern crate getopts;
+
+use rustc_driver::{CompilerCalls, Compilation};
+use rustc_driver::driver::CompileController;
+use rustc::session::Session;
+use syntax::codemap::FileLoader;
+use std::io;
+use std::path::{PathBuf, Path};
+
+struct JitLoader;
+
+impl FileLoader for JitLoader {
+    fn file_exists(&self, _: &Path) -> bool { true }
+    fn abs_path(&self, _: &Path) -> Option<PathBuf> { None }
+    fn read_file(&self, _: &Path) -> io::Result<String> {
+        Ok(r#"
+#[no_mangle]
+pub fn test_add(a: i32, b: i32) -> i32 { a + b }
+"#.to_string())
+    }
+}
+
+#[derive(Copy, Clone)]
+struct JitCalls;
+
+impl<'a> CompilerCalls<'a> for JitCalls {
+    fn build_controller(&mut self,
+                        _: &Session,
+                        _: &getopts::Matches)
+                        -> CompileController<'a> {
+        let mut cc = CompileController::basic();
+        cc.after_llvm.stop = Compilation::Stop;
+        cc.after_llvm.run_callback_on_error = true;
+        cc.after_llvm.callback = Box::new(|state| {
+            state.session.abort_if_errors();
+            let trans = state.trans.unwrap();
+            assert_eq!(trans.modules.len(), 1);
+            let rs_llmod = trans.modules[0].llmod;
+            unsafe { rustc_llvm::LLVMDumpModule(rs_llmod) };
+        });
+        cc
+    }
+}
+
+fn main() {
+    use rustc_driver;
+
+    let mut path = match std::env::args().nth(2) {
+        Some(path) => PathBuf::from(&path),
+        None => panic!("missing rustc path")
+    };
+
+    // Remove two segments from rustc path to get sysroot.
+    path.pop();
+    path.pop();
+
+    let args: Vec<String> =
+        format!("_ _ --sysroot {} --crate-type dylib", path.to_str().unwrap())
+        .split(' ').map(|s| s.to_string()).collect();
+
+    let (result, _) = rustc_driver::run_compiler_with_file_loader(
+        &args, &mut JitCalls, box JitLoader);
+    if let Err(n) = result {
+        panic!("Error {}", n);
+    }
+}
index 3963d20df8873dcc405a79e19654c84f126e09a5..da8769e616c3f0ae545554aebb55edcc0eb6e964 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(rand, core)]
+#![feature(rand)]
 
 use std::fs::File;
 use std::io::prelude::*;
@@ -18,6 +18,11 @@ use std::process::Command;
 use std::__rand::{thread_rng, Rng};
 use std::{char, env};
 
+pub fn check_old_skool() -> bool {
+    use std::env;
+    env::var("RUST_NEW_ERROR_FORMAT").is_err()
+}
+
 // creates a file with `fn main() { <random ident> }` and checks the
 // compiler emits a span of the appropriate length (for the
 // "unresolved name" message); currently just using the number of code
@@ -65,10 +70,17 @@ fn main() {
 
         let err = String::from_utf8_lossy(&result.stderr);
 
-        // the span should end the line (e.g no extra ~'s)
-        let expected_span = format!("^{}\n", repeat("~").take(n - 1)
-                                                        .collect::<String>());
-        assert!(err.contains(&expected_span));
+        if check_old_skool() {
+            // the span should end the line (e.g no extra ~'s)
+            let expected_span = format!("^{}\n", repeat("~").take(n - 1)
+                                                            .collect::<String>());
+            assert!(err.contains(&expected_span));
+        } else {
+            // the span should end the line (e.g no extra ~'s)
+            let expected_span = format!("^{}\n", repeat("^").take(n - 1)
+                                                            .collect::<String>());
+            assert!(err.contains(&expected_span));
+        }
     }
 
     // Test multi-column characters and tabs
@@ -77,9 +89,6 @@ fn main() {
                        r#"extern "路濫狼á́́" fn foo() {{}} extern "路濫狼á́" fn bar() {{}}"#);
     }
 
-    // Extra characters. Every line is preceded by `filename:lineno <actual code>`
-    let offset = main_file.to_str().unwrap().len() + 3;
-
     let result = Command::new("sh")
                          .arg("-c")
                          .arg(format!("{} {}",
@@ -91,17 +100,31 @@ fn main() {
 
     // Test both the length of the snake and the leading spaces up to it
 
-    // First snake is 8 ~s long, with 7 preceding spaces (excluding file name/line offset)
-    let expected_span = format!("\n{}^{}\n",
-                                repeat(" ").take(offset + 7).collect::<String>(),
-                                repeat("~").take(8).collect::<String>());
-    assert!(err.contains(&expected_span));
-    // Second snake is only 7 ~s long, with 36 preceding spaces,
-    // because rustc counts chars() now rather than width(). This
-    // is because width() functions are to be removed from
-    // librustc_unicode
-    let expected_span = format!("\n{}^{}\n",
-                                repeat(" ").take(offset + 36).collect::<String>(),
-                                repeat("~").take(7).collect::<String>());
-    assert!(err.contains(&expected_span));
+    if check_old_skool() {
+        // Extra characters. Every line is preceded by `filename:lineno <actual code>`
+        let offset = main_file.to_str().unwrap().len() + 3;
+
+        // First snake is 8 ~s long, with 7 preceding spaces (excluding file name/line offset)
+        let expected_span = format!("\n{}^{}\n",
+                                    repeat(" ").take(offset + 7).collect::<String>(),
+                                    repeat("~").take(8).collect::<String>());
+        assert!(err.contains(&expected_span));
+        // Second snake is only 7 ~s long, with 36 preceding spaces,
+        // because rustc counts chars() now rather than width(). This
+        // is because width() functions are to be removed from
+        // librustc_unicode
+        let expected_span = format!("\n{}^{}\n",
+                                    repeat(" ").take(offset + 36).collect::<String>(),
+                                    repeat("~").take(7).collect::<String>());
+        assert!(err.contains(&expected_span));
+    } else {
+        let expected_span = format!("\n  |>{}{}\n",
+                                    repeat(" ").take(8).collect::<String>(),
+                                    repeat("^").take(9).collect::<String>());
+        assert!(err.contains(&expected_span));
+        let expected_span = format!("\n  |>{}{}\n",
+                                    repeat(" ").take(37).collect::<String>(),
+                                    repeat("^").take(8).collect::<String>());
+        assert!(err.contains(&expected_span));
+    }
 }
index ed971faf8c6a19a7bd3c75475c735a6a5e5fee83..64747002a65b03542eab3abc54bd4f4950be6a02 100644 (file)
@@ -86,7 +86,7 @@ fn check_expr_attrs(es: &str, expected: &[&str]) {
     let actual = &e.attrs;
     str_compare(es,
                 &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
-                actual.as_attr_slice(),
+                &actual,
                 pprust::attribute_to_string);
 }
 
index 0132014de0ab51a26c3e5fdd2805896ea154c185..42135703b75a4e9f3f9576ad71dc28f5c55bf109 100644 (file)
 
 extern crate syntax;
 extern crate syntax_ext;
+extern crate syntax_pos;
 extern crate rustc;
 extern crate rustc_plugin;
 
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::ext::base::{MultiDecorator, ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
 use syntax::ptr::P;
 use syntax_ext::deriving::generic::{cs_fold, TraitDef, MethodDef, combine_substructure};
 use syntax_ext::deriving::generic::ty::{Literal, LifetimeBounds, Path, borrowed_explicit_self};
+use syntax_pos::Span;
 use rustc_plugin::Registry;
 
 #[plugin_registrar]
index 6fa78913839b756d4342ad960ab2a84b0bfc6582..eeecd0b24e29ee60ef314c8c8a4b8b8e88279f5c 100644 (file)
 
 extern crate syntax;
 extern crate syntax_ext;
+extern crate syntax_pos;
 extern crate rustc;
 extern crate rustc_plugin;
 
 use syntax::ast;
 use syntax::attr::AttrMetaMethods;
-use syntax::codemap::Span;
 use syntax::ext::base::{MultiDecorator, ExtCtxt, Annotatable};
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
@@ -29,6 +29,7 @@ use syntax::ptr::P;
 use syntax_ext::deriving::generic::{cs_fold, TraitDef, MethodDef, combine_substructure};
 use syntax_ext::deriving::generic::{Substructure, Struct, EnumMatching};
 use syntax_ext::deriving::generic::ty::{Literal, LifetimeBounds, Path, borrowed_explicit_self};
+use syntax_pos::Span;
 use rustc_plugin::Registry;
 
 #[plugin_registrar]
index 0abf71ba444aefa49a236485305d5684fed997ee..604933d40a12c688d4ee26a13033dbbc8eb2c24c 100644 (file)
@@ -29,6 +29,7 @@ use rustc_plugin::Registry;
 struct Pass;
 
 impl transform::Pass for Pass {}
+
 impl<'tcx> MirPass<'tcx> for Pass {
     fn run_pass<'a>(&mut self, _: TyCtxt<'a, 'tcx, 'tcx>,
                     _: MirSource, mir: &mut Mir<'tcx>) {
index 25a75c2d2952e406bb94478833e2839259063a3c..7f8a741465b30598b32e32b73592c94a8720ab1c 100644 (file)
 extern crate syntax;
 extern crate rustc;
 extern crate rustc_plugin;
+extern crate syntax_pos;
 
 use syntax::ast;
-use syntax::codemap;
 use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
 use syntax::util::small_vector::SmallVector;
+use syntax::tokenstream;
 use rustc_plugin::Registry;
 
 #[plugin_registrar]
@@ -28,7 +29,8 @@ pub fn plugin_registrar(reg: &mut Registry) {
     reg.register_macro("multiple_items", expand)
 }
 
-fn expand(cx: &mut ExtCtxt, _: codemap::Span, _: &[ast::TokenTree]) -> Box<MacResult+'static> {
+fn expand(cx: &mut ExtCtxt, _: syntax_pos::Span, _: &[tokenstream::TokenTree])
+          -> Box<MacResult+'static> {
     MacEager::items(SmallVector::many(vec![
         quote_item!(cx, struct Struct1;).unwrap(),
         quote_item!(cx, struct Struct2;).unwrap()
index 3516f566e8a1ffda8f157786da1479d6def7eb47..11d81eda55625960ae69fcca3483de8072e7de35 100644 (file)
 extern crate syntax;
 extern crate rustc;
 extern crate rustc_plugin;
+extern crate syntax_pos;
 
-use syntax::ast::{self, TokenTree, Item, MetaItem, ImplItem, TraitItem, ItemKind};
-use syntax::codemap::Span;
+use syntax::ast::{self, Item, MetaItem, ImplItem, TraitItem, ItemKind};
 use syntax::ext::base::*;
 use syntax::parse::{self, token};
 use syntax::ptr::P;
+use syntax::tokenstream::TokenTree;
+use syntax_pos::Span;
 use rustc_plugin::Registry;
 
 #[macro_export]
@@ -62,15 +64,16 @@ fn expand_identity(cx: &mut ExtCtxt, _span: Span, tts: &[TokenTree])
 fn expand_into_foo_multi(cx: &mut ExtCtxt,
                          sp: Span,
                          attr: &MetaItem,
-                         it: Annotatable) -> Annotatable {
+                         it: Annotatable) -> Vec<Annotatable> {
     match it {
-        Annotatable::Item(it) => {
+        Annotatable::Item(it) => vec![
             Annotatable::Item(P(Item {
                 attrs: it.attrs.clone(),
                 ..(*quote_item!(cx, enum Foo2 { Bar2, Baz2 }).unwrap()).clone()
-            }))
-        }
-        Annotatable::ImplItem(it) => {
+            })),
+            Annotatable::Item(quote_item!(cx, enum Foo3 { Bar }).unwrap()),
+        ],
+        Annotatable::ImplItem(it) => vec![
             quote_item!(cx, impl X { fn foo(&self) -> i32 { 42 } }).unwrap().and_then(|i| {
                 match i.node {
                     ItemKind::Impl(_, _, _, _, _, mut items) => {
@@ -79,8 +82,8 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt,
                     _ => unreachable!("impl parsed to something other than impl")
                 }
             })
-        }
-        Annotatable::TraitItem(it) => {
+        ],
+        Annotatable::TraitItem(it) => vec![
             quote_item!(cx, trait X { fn foo(&self) -> i32 { 0 } }).unwrap().and_then(|i| {
                 match i.node {
                     ItemKind::Trait(_, _, _, mut items) => {
@@ -89,7 +92,7 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt,
                     _ => unreachable!("trait parsed to something other than trait")
                 }
             })
-        }
+        ],
     }
 }
 
index 99321ad42418dd8f0b2dddb61b84e1289e25e514..f0edc0f2b120f887dde338ce2b9ae1774a1c4f9a 100644 (file)
 #![feature(box_syntax, rustc_private)]
 
 extern crate syntax;
+extern crate syntax_pos;
 extern crate rustc;
 extern crate rustc_plugin;
 
 use std::borrow::ToOwned;
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::ext::build::AstBuilder;
 use syntax::ext::base::{TTMacroExpander, ExtCtxt, MacResult, MacEager, NormalTT};
 use syntax::parse::token;
 use syntax::print::pprust;
 use syntax::ptr::P;
+use syntax_pos::Span;
+use syntax::tokenstream;
 use rustc_plugin::Registry;
 
 struct Expander {
@@ -35,7 +37,7 @@ impl TTMacroExpander for Expander {
     fn expand<'cx>(&self,
                    ecx: &'cx mut ExtCtxt,
                    sp: Span,
-                   _: &[ast::TokenTree]) -> Box<MacResult+'cx> {
+                   _: &[tokenstream::TokenTree]) -> Box<MacResult+'cx> {
         let args = self.args.iter().map(|i| pprust::meta_item_to_string(&*i))
             .collect::<Vec<_>>().join(", ");
         let interned = token::intern_and_get_ident(&args[..]);
index 713a7d1e811a2c891281f4ab3a763e0fd44c7a87..5b1e210b0b2586b9449b341868910557cc1dbdb7 100644 (file)
 #![feature(plugin_registrar, quote, rustc_private)]
 
 extern crate syntax;
+extern crate syntax_pos;
 extern crate rustc;
 extern crate rustc_plugin;
 
-use syntax::codemap::Span;
 use syntax::parse::token::{self, str_to_ident, NtExpr, NtPat};
-use syntax::ast::{TokenTree, Pat};
+use syntax::ast::{Pat};
+use syntax::tokenstream::{TokenTree};
 use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
 use syntax::ext::build::AstBuilder;
 use syntax::ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal};
 use syntax::ext::tt::macro_parser::{Success, Failure, Error};
 use syntax::ptr::P;
+use syntax_pos::Span;
 use rustc_plugin::Registry;
 
 fn expand_mbe_matches(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
index 839ece49c3eb5d4c90cf2fea1321e2c98af91890..0c8af013fd12d2ed69c467285ff233fa7f492415 100644 (file)
 #![feature(slice_patterns)]
 
 extern crate syntax;
+extern crate syntax_pos;
 extern crate rustc;
 extern crate rustc_plugin;
 
-use syntax::codemap::Span;
-use syntax::ast::TokenTree;
 use syntax::parse::token;
+use syntax::tokenstream::TokenTree;
 use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
 use syntax::ext::build::AstBuilder;  // trait for expr_usize
+use syntax_pos::Span;
 use rustc_plugin::Registry;
 
 // WARNING WARNING WARNING WARNING WARNING
index 7281698a7fb3491f2f132130b98394f7c2c0a7ce..72d262853555698199c17c2a9ce4d16bfd0b24c4 100644 (file)
 
 extern crate syntax_extension_with_dll_deps_1 as other;
 extern crate syntax;
+extern crate syntax_pos;
 extern crate rustc;
 extern crate rustc_plugin;
 
-use syntax::ast::{TokenTree, Item, MetaItem};
-use syntax::codemap::Span;
+use syntax::ast::{Item, MetaItem};
 use syntax::ext::base::*;
+use syntax::tokenstream::TokenTree;
+use syntax_pos::Span;
 use rustc_plugin::Registry;
 
 #[plugin_registrar]
index af641d717edd9224b4719bc4ef419fb8c01979bb..ff57e9d6b73684dd6190dff73980e8d472e0a853 100644 (file)
@@ -19,11 +19,11 @@ extern crate getopts;
 extern crate rustc;
 extern crate rustc_driver;
 extern crate syntax;
+extern crate rustc_errors as errors;
 
 use rustc::session::Session;
 use rustc::session::config::{self, Input};
 use rustc_driver::{driver, CompilerCalls, Compilation};
-use syntax::{diagnostics, errors};
 
 use std::path::PathBuf;
 
@@ -35,7 +35,7 @@ impl<'a> CompilerCalls<'a> for TestCalls {
     fn early_callback(&mut self,
                       _: &getopts::Matches,
                       _: &config::Options,
-                      _: &diagnostics::registry::Registry,
+                      _: &errors::registry::Registry,
                       _: config::ErrorOutputType)
                       -> Compilation {
         self.count *= 2;
@@ -64,7 +64,7 @@ impl<'a> CompilerCalls<'a> for TestCalls {
                 _: &config::Options,
                 _: &Option<PathBuf>,
                 _: &Option<PathBuf>,
-                _: &diagnostics::registry::Registry)
+                _: &errors::registry::Registry)
                 -> Option<(Input, Option<PathBuf>)> {
         panic!("This shouldn't happen");
     }
index 3a3819636691500e36e495288d32143b901ba29d..d17adff007c6335b440c4c42063a753f99f2c91c 100644 (file)
@@ -40,6 +40,8 @@ pub fn main() {
     assert_eq!(Foo2::Bar2, Foo2::Bar2);
     test(None::<Foo2>);
 
+    let _ = Foo3::Bar;
+
     let x = 10i32;
     assert_eq!(x.foo(), 42);
     let x = 10u8;
index 0bb3e610020a118b925154bc2340b7131fb6743d..a4f0e35cc5ac70792ef2da6cfc047c3ed3681b93 100644 (file)
 #![feature(quote, rustc_private)]
 
 extern crate syntax;
+extern crate syntax_pos;
 
-use syntax::codemap::DUMMY_SP;
 use syntax::print::pprust::*;
 use syntax::parse::token::intern;
+use syntax_pos::DUMMY_SP;
 
 fn main() {
     let ps = syntax::parse::ParseSess::new();
-    let mut feature_gated_cfgs = vec![];
+    let mut loader = syntax::ext::base::DummyMacroLoader;
     let mut cx = syntax::ext::base::ExtCtxt::new(
         &ps, vec![],
         syntax::ext::expand::ExpansionConfig::default("qquote".to_string()),
-        &mut feature_gated_cfgs);
+        &mut loader);
     cx.bt_push(syntax::codemap::ExpnInfo {
         call_site: DUMMY_SP,
         callee: syntax::codemap::NameAndSpan {
index 4397da35d7a34f8794048c3f26568f7527984b7c..710e2fd1d07a38ab3b7c36c0f03064ec514de4e9 100644 (file)
@@ -20,8 +20,8 @@ use syntax::ptr::P;
 use syntax::parse::PResult;
 
 fn syntax_extension(cx: &ExtCtxt) {
-    let e_toks : Vec<syntax::ast::TokenTree> = quote_tokens!(cx, 1 + 2);
-    let p_toks : Vec<syntax::ast::TokenTree> = quote_tokens!(cx, (x, 1 .. 4, *));
+    let e_toks : Vec<syntax::tokenstream::TokenTree> = quote_tokens!(cx, 1 + 2);
+    let p_toks : Vec<syntax::tokenstream::TokenTree> = quote_tokens!(cx, (x, 1 .. 4, *));
 
     let a: P<syntax::ast::Expr> = quote_expr!(cx, 1 + 2);
     let _b: Option<P<syntax::ast::Item>> = quote_item!(cx, static foo : isize = $e_toks; );
@@ -39,7 +39,7 @@ fn syntax_extension(cx: &ExtCtxt) {
 
     let _l: P<syntax::ast::Ty> = quote_ty!(cx, &isize);
 
-    let _m: Vec<syntax::ast::TokenTree> = quote_matcher!(cx, $($foo:tt,)* bar);
+    let _m: Vec<syntax::tokenstream::TokenTree> = quote_matcher!(cx, $($foo:tt,)* bar);
     let _n: syntax::ast::Attribute = quote_attr!(cx, #![cfg(foo, bar = "baz")]);
 
     let _o: Option<P<syntax::ast::Item>> = quote_item!(cx, fn foo<T: ?Sized>() {});
index 9662e1ff33d11238c4a3f6fde6ab312889a278c3..3110e22d5da135eb22798c7708e0daf46c448a1b 100644 (file)
@@ -16,4 +16,7 @@ pub fn main() {
     assert_eq!("abc".to_string(),"abc".to_string());
     assert_eq!(Box::new(Point{x:34}),Box::new(Point{x:34}));
     assert_eq!(&Point{x:34},&Point{x:34});
+    assert_eq!(42, 42, "foo bar");
+    assert_eq!(42, 42, "a {} c", "b");
+    assert_eq!(42, 42, "{x}, {y}, {z}", x = 1, y = 2, z = 3);
 }
diff --git a/src/test/run-pass/auxiliary/issue34796aux.rs b/src/test/run-pass/auxiliary/issue34796aux.rs
new file mode 100644 (file)
index 0000000..9131b60
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_type = "lib"]
+pub trait Future {
+    type Item;
+    type Error;
+}
+
+impl Future for u32 {
+    type Item = ();
+    type Error = Box<()>;
+}
+
+fn foo() -> Box<Future<Item=(), Error=Box<()>>> {
+    Box::new(0u32)
+}
+
+pub fn bar<F, A, B>(_s: F)
+    where F: Fn(A) -> B,
+{
+    foo();
+}
diff --git a/src/test/run-pass/auxiliary/xcrate_generic_fn_nested_return.rs b/src/test/run-pass/auxiliary/xcrate_generic_fn_nested_return.rs
new file mode 100644 (file)
index 0000000..48fb05f
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub struct Request {
+    pub id: String,
+    pub arg: String,
+}
+
+pub fn decode<T>() -> Result<Request, ()> {
+    (|| {
+        Ok(Request {
+            id: "hi".to_owned(),
+            arg: match Err(()) {
+                Ok(v) => v,
+                Err(e) => return Err(e)
+            },
+        })
+    })()
+}
index 7fbd8dc4786baf1de47c2d7735e6700c8fe11b18..93d3345a8099121041059f62c9fbdf535ece6218 100644 (file)
@@ -8,10 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(recover, rand, std_panic)]
+#![feature(rand, std_panic)]
 
 use std::__rand::{thread_rng, Rng};
-use std::panic::{self, AssertRecoverSafe};
+use std::panic::{self, AssertUnwindSafe};
 
 use std::collections::BinaryHeap;
 use std::cmp;
@@ -70,8 +70,8 @@ fn test_integrity() {
             {
                 // push the panicking item to the heap and catch the panic
                 let thread_result = {
-                    let mut heap_ref = AssertRecoverSafe(&mut heap);
-                    panic::recover(move || {
+                    let mut heap_ref = AssertUnwindSafe(&mut heap);
+                    panic::catch_unwind(move || {
                         heap_ref.push(panic_item);
                     })
                 };
index 082a39f56312ec5a56b5607a2509941289e95d56..eb04514271c7532891401dcbaf8e7e82f9307aa3 100644 (file)
@@ -15,10 +15,10 @@ trait Contravariant {
     fn foo(&self) { }
 }
 
-impl Contravariant for for<'a,'b> fn(&'a u8, &'b u8) {
+impl Contravariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
 }
 
-impl Contravariant for for<'a> fn(&'a u8, &'a u8) {
+impl Contravariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -27,10 +27,10 @@ trait Covariant {
     fn foo(&self) { }
 }
 
-impl Covariant for for<'a,'b> fn(&'a u8, &'b u8) {
+impl Covariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
 }
 
-impl Covariant for for<'a> fn(&'a u8, &'a u8) {
+impl Covariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -39,10 +39,10 @@ trait Invariant {
     fn foo(&self) { }
 }
 
-impl Invariant for for<'a,'b> fn(&'a u8, &'b u8) {
+impl Invariant for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
 }
 
-impl Invariant for for<'a> fn(&'a u8, &'a u8) {
+impl Invariant for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
 }
 
 fn main() { }
diff --git a/src/test/run-pass/const-byte-str-cast.rs b/src/test/run-pass/const-byte-str-cast.rs
new file mode 100644 (file)
index 0000000..2f265b9
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[deny(warnings)]
+
+pub fn main() {
+    let _ = b"x" as &[u8];
+}
index a31df0fd93e16a1aaca8508d97feec1bf52ae3bb..2f8ecbe693f0752263d959df10d476b3db94554b 100644 (file)
@@ -458,7 +458,7 @@ struct S<'a> {
 }
 
 impl<'a> Named for S<'a> {
-    fn new<'b>(name: &'static str) -> S<'b> {
+    fn new(name: &'static str) -> S<'a> {
         S { name: name, mark: Cell::new(0), next: Cell::new(None) }
     }
     fn name(&self) -> &str { self.name }
@@ -476,7 +476,7 @@ struct S2<'a> {
 }
 
 impl<'a> Named for S2<'a> {
-    fn new<'b>(name: &'static str) -> S2<'b> {
+    fn new(name: &'static str) -> S2<'a> {
         S2 { name: name, mark: Cell::new(0), next: Cell::new((None, None)) }
     }
     fn name(&self) -> &str { self.name }
@@ -496,7 +496,7 @@ struct V<'a> {
 }
 
 impl<'a> Named for V<'a> {
-    fn new<'b>(name: &'static str) -> V<'b> {
+    fn new(name: &'static str) -> V<'a> {
         V { name: name,
             mark: Cell::new(0),
             contents: vec![Cell::new(None), Cell::new(None)]
@@ -518,7 +518,7 @@ struct H<'a> {
 }
 
 impl<'a> Named for H<'a> {
-    fn new<'b>(name: &'static str) -> H<'b> {
+    fn new(name: &'static str) -> H<'a> {
         H { name: name, mark: Cell::new(0), next: Cell::new(None) }
     }
     fn name(&self) -> &str { self.name }
@@ -549,7 +549,7 @@ struct HM<'a> {
 }
 
 impl<'a> Named for HM<'a> {
-    fn new<'b>(name: &'static str) -> HM<'b> {
+    fn new(name: &'static str) -> HM<'a> {
         HM { name: name,
              mark: Cell::new(0),
              contents: Cell::new(None)
@@ -583,7 +583,7 @@ struct VD<'a> {
 }
 
 impl<'a> Named for VD<'a> {
-    fn new<'b>(name: &'static str) -> VD<'b> {
+    fn new(name: &'static str) -> VD<'a> {
         VD { name: name,
              mark: Cell::new(0),
              contents: Cell::new(None)
@@ -604,7 +604,7 @@ struct VM<'a> {
 }
 
 impl<'a> Named for VM<'a> {
-    fn new<'b>(name: &'static str) -> VM<'b> {
+    fn new(name: &'static str) -> VM<'a> {
         VM { name: name,
              mark: Cell::new(0),
              contents: Cell::new(None)
@@ -625,7 +625,7 @@ struct LL<'a> {
 }
 
 impl<'a> Named for LL<'a> {
-    fn new<'b>(name: &'static str) -> LL<'b> {
+    fn new(name: &'static str) -> LL<'a> {
         LL { name: name,
              mark: Cell::new(0),
              contents: Cell::new(None)
@@ -646,7 +646,7 @@ struct BH<'a> {
 }
 
 impl<'a> Named for BH<'a> {
-    fn new<'b>(name: &'static str) -> BH<'b> {
+    fn new(name: &'static str) -> BH<'a> {
         BH { name: name,
              mark: Cell::new(0),
              contents: Cell::new(None)
@@ -687,7 +687,7 @@ struct BTM<'a> {
 }
 
 impl<'a> Named for BTM<'a> {
-    fn new<'b>(name: &'static str) -> BTM<'b> {
+    fn new(name: &'static str) -> BTM<'a> {
         BTM { name: name,
              mark: Cell::new(0),
              contents: Cell::new(None)
@@ -728,7 +728,7 @@ struct BTS<'a> {
 }
 
 impl<'a> Named for BTS<'a> {
-    fn new<'b>(name: &'static str) -> BTS<'b> {
+    fn new(name: &'static str) -> BTS<'a> {
         BTS { name: name,
              mark: Cell::new(0),
              contents: Cell::new(None)
diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs
new file mode 100644 (file)
index 0000000..f917531
--- /dev/null
@@ -0,0 +1,156 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+use std::cell::{Cell, RefCell};
+use std::panic;
+use std::usize;
+
+struct InjectedFailure;
+
+struct Allocator {
+    data: RefCell<Vec<bool>>,
+    failing_op: usize,
+    cur_ops: Cell<usize>,
+}
+
+impl panic::UnwindSafe for Allocator {}
+impl panic::RefUnwindSafe for Allocator {}
+
+impl Drop for Allocator {
+    fn drop(&mut self) {
+        let data = self.data.borrow();
+        if data.iter().any(|d| *d) {
+            panic!("missing free: {:?}", data);
+        }
+    }
+}
+
+impl Allocator {
+    fn new(failing_op: usize) -> Self {
+        Allocator {
+            failing_op: failing_op,
+            cur_ops: Cell::new(0),
+            data: RefCell::new(vec![])
+        }
+    }
+    fn alloc(&self) -> Ptr {
+        self.cur_ops.set(self.cur_ops.get() + 1);
+
+        if self.cur_ops.get() == self.failing_op {
+            panic!(InjectedFailure);
+        }
+
+        let mut data = self.data.borrow_mut();
+        let addr = data.len();
+        data.push(true);
+        Ptr(addr, self)
+    }
+}
+
+struct Ptr<'a>(usize, &'a Allocator);
+impl<'a> Drop for Ptr<'a> {
+    fn drop(&mut self) {
+        match self.1.data.borrow_mut()[self.0] {
+            false => {
+                panic!("double free at index {:?}", self.0)
+            }
+            ref mut d => *d = false
+        }
+
+        self.1.cur_ops.set(self.1.cur_ops.get()+1);
+
+        if self.1.cur_ops.get() == self.1.failing_op {
+            panic!(InjectedFailure);
+        }
+    }
+}
+
+#[rustc_mir]
+fn dynamic_init(a: &Allocator, c: bool) {
+    let _x;
+    if c {
+        _x = Some(a.alloc());
+    }
+}
+
+#[rustc_mir]
+fn dynamic_drop(a: &Allocator, c: bool) {
+    let x = a.alloc();
+    if c {
+        Some(x)
+    } else {
+        None
+    };
+}
+
+#[rustc_mir]
+fn assignment2(a: &Allocator, c0: bool, c1: bool) {
+    let mut _v = a.alloc();
+    let mut _w = a.alloc();
+    if c0 {
+        drop(_v);
+    }
+    _v = _w;
+    if c1 {
+        _w = a.alloc();
+    }
+}
+
+#[rustc_mir]
+fn assignment1(a: &Allocator, c0: bool) {
+    let mut _v = a.alloc();
+    let mut _w = a.alloc();
+    if c0 {
+        drop(_v);
+    }
+    _v = _w;
+}
+
+fn run_test<F>(mut f: F)
+    where F: FnMut(&Allocator)
+{
+    let first_alloc = Allocator::new(usize::MAX);
+    f(&first_alloc);
+
+    for failing_op in 1..first_alloc.cur_ops.get()+1 {
+        let alloc = Allocator::new(failing_op);
+        let alloc = &alloc;
+        let f = panic::AssertUnwindSafe(&mut f);
+        let result = panic::catch_unwind(move || {
+            f.0(alloc);
+        });
+        match result {
+            Ok(..) => panic!("test executed {} ops but now {}",
+                             first_alloc.cur_ops.get(), alloc.cur_ops.get()),
+            Err(e) => {
+                if e.downcast_ref::<InjectedFailure>().is_none() {
+                    panic::resume_unwind(e);
+                }
+            }
+        }
+    }
+}
+
+fn main() {
+    run_test(|a| dynamic_init(a, false));
+    run_test(|a| dynamic_init(a, true));
+    run_test(|a| dynamic_drop(a, false));
+    run_test(|a| dynamic_drop(a, true));
+
+    run_test(|a| assignment2(a, false, false));
+    run_test(|a| assignment2(a, false, true));
+    run_test(|a| assignment2(a, true, false));
+    run_test(|a| assignment2(a, true, true));
+
+    run_test(|a| assignment1(a, false));
+    run_test(|a| assignment1(a, true));
+}
diff --git a/src/test/run-pass/exhaustive-bool-match-sanity.rs b/src/test/run-pass/exhaustive-bool-match-sanity.rs
new file mode 100644 (file)
index 0000000..d88a5f1
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Issue #33540
+// We previously used to generate a 3-armed boolean `SwitchInt` in the
+// MIR of the function `foo` below. #33583 changed rustc to
+// generate an `If` terminator instead. This test is to just ensure
+// sanity in that we generate an if-else chain giving the correct
+// results.
+
+#![feature(rustc_attrs)]
+
+#[rustc_mir]
+fn foo(x: bool, y: bool) -> u32 {
+    match (x, y) {
+        (false, _) => 0,
+        (_, false) => 1,
+        (true, true) => 2
+    }
+}
+
+fn main() {
+    assert_eq!(foo(false, true), 0);
+    assert_eq!(foo(false, false), 0);
+    assert_eq!(foo(true, false), 1);
+    assert_eq!(foo(true, true), 2);
+}
diff --git a/src/test/run-pass/hygiene.rs b/src/test/run-pass/hygiene.rs
new file mode 100644 (file)
index 0000000..4507ba5
--- /dev/null
@@ -0,0 +1,116 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(unused)]
+
+fn f() {
+    let x = 0;
+    macro_rules! foo { () => {
+        assert_eq!(x, 0);
+    } }
+
+    let x = 1;
+    foo!();
+}
+
+fn g() {
+    let x = 0;
+    macro_rules! m { ($x:ident) => {
+        macro_rules! m2 { () => { ($x, x) } }
+        let x = 1;
+        macro_rules! m3 { () => { ($x, x) } }
+    } }
+
+    let x = 2;
+    m!(x);
+
+    let x = 3;
+    assert_eq!(m2!(), (2, 0));
+    assert_eq!(m3!(), (2, 1));
+
+    let x = 4;
+    m!(x);
+    assert_eq!(m2!(), (4, 0));
+    assert_eq!(m3!(), (4, 1));
+}
+
+mod foo {
+    macro_rules! m {
+        ($f:ident : |$x:ident| $e:expr) => {
+            pub fn $f() -> (i32, i32) {
+                let x = 0;
+                let $x = 1;
+                (x, $e)
+            }
+        }
+    }
+
+    m!(f: |x| x + 10);
+}
+
+fn interpolated_pattern() {
+    let x = 0;
+    macro_rules! m {
+        ($p:pat, $e:expr) => {
+            let $p = 1;
+            assert_eq!((x, $e), (0, 1));
+        }
+    }
+
+    m!(x, x);
+}
+
+fn patterns_in_macro_generated_macros() {
+    let x = 0;
+    macro_rules! m {
+        ($a:expr, $b:expr) => {
+            assert_eq!(x, 0);
+            let x = $a;
+            macro_rules! n {
+                () => {
+                    (x, $b)
+                }
+            }
+        }
+    }
+
+    let x = 1;
+    m!(2, x);
+
+    let x = 3;
+    assert_eq!(n!(), (2, 1));
+}
+
+fn match_hygiene() {
+    let x = 0;
+
+    macro_rules! m {
+        ($p:pat, $e:expr) => {
+            for result in &[Ok(1), Err(1)] {
+                match *result {
+                    $p => { assert_eq!(($e, x), (1, 0)); }
+                    Err(x) => { assert_eq!(($e, x), (2, 1)); }
+                }
+            }
+        }
+    }
+
+    let x = 2;
+    m!(Ok(x), x);
+}
+
+fn main() {
+    f();
+    g();
+    assert_eq!(foo::f(), (0, 11));
+    interpolated_pattern();
+    patterns_in_macro_generated_macros();
+    match_hygiene();
+}
index 7b00ea4a520cddb5f3be7c429073c855af69004b..cee0caeb465f56d3206266d85cc4f6f769730fb3 100644 (file)
@@ -16,12 +16,12 @@ fn main() {
 
     let mut result = vec!();
     loop {
-        x = match x {
-            [1, n, 3, rest..] => {
+        x = match *x {
+            [1, n, 3, ref rest..] => {
                 result.push(n);
                 rest
             }
-            [n, rest..] => {
+            [n, ref rest..] => {
                 result.push(n);
                 rest
             }
index b55754ee59b35c11e68f316a4fb7e0e2a1988b30..508360cb70110a96ac10973133548c07053a1afc 100644 (file)
@@ -16,9 +16,9 @@ fn main() {
 }
 
 fn count_members(v: &[usize]) -> usize {
-    match v {
+    match *v {
         []         => 0,
         [_]        => 1,
-        [_x, xs..] => 1 + count_members(xs)
+        [_, ref xs..] => 1 + count_members(xs)
     }
 }
index 384bd9df7cfedadb9bf042907d7a7b693bab97eb..e596bee8bfe9fc7afc4936c33c7537873306aabb 100644 (file)
@@ -9,14 +9,15 @@
 // except according to those terms.
 
 
-#![feature(slice_patterns)]
+#![feature(slice_patterns, rustc_attrs)]
 
+#[rustc_mir]
 fn main() {
     let x: (isize, &[isize]) = (2, &[1, 2]);
     assert_eq!(match x {
-        (0, [_, _]) => 0,
+        (0, &[_, _]) => 0,
         (1, _) => 1,
-        (2, [_, _]) => 2,
+        (2, &[_, _]) => 2,
         (2, _) => 3,
         _ => 4
     }, 2);
index af767464db35170ca31443c7eafdde94a778a08f..19cd1cf3df717721c9bbfcdb85825b0699aa437a 100644 (file)
@@ -26,5 +26,5 @@ fn main() {
         std::intrinsics::type_name::<NT>(),
         // DST
         std::intrinsics::type_name::<DST>()
-    )}, ("[u8]", "str", "std::marker::Send + 'static", "NT", "DST"));
+    )}, ("[u8]", "str", "std::marker::Send", "NT", "DST"));
 }
diff --git a/src/test/run-pass/issue-21350.rs b/src/test/run-pass/issue-21350.rs
deleted file mode 100644 (file)
index ff205cd..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Make sure that "bare sequences" don't ICE in follow checking
-
-// pretty-expanded FIXME #23616
-
-macro_rules! bare {
-    $($id:expr),+ => ( $($id)+ )
-}
-
-fn main() { }
diff --git a/src/test/run-pass/issue-23477.rs b/src/test/run-pass/issue-23477.rs
new file mode 100644 (file)
index 0000000..d4671c9
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+// compiler-flags: -g
+
+pub struct Dst {
+    pub a: (),
+    pub b: (),
+    pub data: [u8],
+}
+
+pub unsafe fn borrow(bytes: &[u8]) -> &Dst {
+    let dst: &Dst = std::mem::transmute((bytes.as_ptr(), bytes.len()));
+    dst
+}
+
+fn main() {}
diff --git a/src/test/run-pass/issue-23598.rs b/src/test/run-pass/issue-23598.rs
deleted file mode 100644 (file)
index 5f1c79a..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-trait Collection where for<'a> &'a Self: IntoIterator {
-    fn my_iter(&self) -> <&Self as IntoIterator>::IntoIter {
-        self.into_iter()
-    }
-}
-
-impl<T> Collection for [T] { }
-
-fn main() {
-    let v = [0usize];
-    let _ = v.my_iter();
-}
diff --git a/src/test/run-pass/issue-23958.rs b/src/test/run-pass/issue-23958.rs
new file mode 100644 (file)
index 0000000..5f1c79a
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Collection where for<'a> &'a Self: IntoIterator {
+    fn my_iter(&self) -> <&Self as IntoIterator>::IntoIter {
+        self.into_iter()
+    }
+}
+
+impl<T> Collection for [T] { }
+
+fn main() {
+    let v = [0usize];
+    let _ = v.my_iter();
+}
diff --git a/src/test/run-pass/issue-30240.rs b/src/test/run-pass/issue-30240.rs
new file mode 100644 (file)
index 0000000..3be661c
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let &ref a = &[0i32] as &[_];
+    assert_eq!(a, &[0i32] as &[_]);
+
+    let &ref a = "hello";
+    assert_eq!(a, "hello");
+
+    match "foo" {
+        "fool" => unreachable!(),
+        "foo" => {},
+        ref _x => unreachable!()
+    }
+}
diff --git a/src/test/run-pass/issue-30276.rs b/src/test/run-pass/issue-30276.rs
new file mode 100644 (file)
index 0000000..5dd0cd8
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Test([i32]);
+fn main() {
+    let _x: fn(_) -> Test = Test;
+}
diff --git a/src/test/run-pass/issue-32805.rs b/src/test/run-pass/issue-32805.rs
new file mode 100644 (file)
index 0000000..ea49cf3
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+#[rustc_mir]
+fn const_mir() -> f32 { 9007199791611905.0 }
+
+#[rustc_no_mir]
+fn const_old() -> f32 { 9007199791611905.0 }
+
+fn main() {
+    let original = "9007199791611905.0"; // (1<<53)+(1<<29)+1
+    let expected = "9007200000000000";
+
+    assert_eq!(const_mir().to_string(), expected);
+    assert_eq!(const_old().to_string(), expected);
+    assert_eq!(original.parse::<f32>().unwrap().to_string(), expected);
+}
diff --git a/src/test/run-pass/issue-33770.rs b/src/test/run-pass/issue-33770.rs
new file mode 100644 (file)
index 0000000..f5635fd
--- /dev/null
@@ -0,0 +1,100 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::process::{Command, Stdio};
+use std::env;
+use std::sync::{Mutex, RwLock};
+use std::time::Duration;
+use std::thread;
+
+fn test_mutex() {
+    let m = Mutex::new(0);
+    let _g = m.lock().unwrap();
+    let _g2 = m.lock().unwrap();
+}
+
+fn test_try_mutex() {
+    let m = Mutex::new(0);
+    let _g = m.lock().unwrap();
+    let _g2 = m.try_lock().unwrap();
+}
+
+fn test_rwlock_ww() {
+    let m = RwLock::new(0);
+    let _g = m.write().unwrap();
+    let _g2 = m.write().unwrap();
+}
+
+fn test_try_rwlock_ww() {
+    let m = RwLock::new(0);
+    let _g = m.write().unwrap();
+    let _g2 = m.try_write().unwrap();
+}
+
+fn test_rwlock_rw() {
+    let m = RwLock::new(0);
+    let _g = m.read().unwrap();
+    let _g2 = m.write().unwrap();
+}
+
+fn test_try_rwlock_rw() {
+    let m = RwLock::new(0);
+    let _g = m.read().unwrap();
+    let _g2 = m.try_write().unwrap();
+}
+
+fn test_rwlock_wr() {
+    let m = RwLock::new(0);
+    let _g = m.write().unwrap();
+    let _g2 = m.read().unwrap();
+}
+
+fn test_try_rwlock_wr() {
+    let m = RwLock::new(0);
+    let _g = m.write().unwrap();
+    let _g2 = m.try_read().unwrap();
+}
+
+fn main() {
+    let args: Vec<String> = env::args().collect();
+    if args.len() > 1 {
+        match &*args[1] {
+            "mutex" => test_mutex(),
+            "try_mutex" => test_try_mutex(),
+            "rwlock_ww" => test_rwlock_ww(),
+            "try_rwlock_ww" => test_try_rwlock_ww(),
+            "rwlock_rw" => test_rwlock_rw(),
+            "try_rwlock_rw" => test_try_rwlock_rw(),
+            "rwlock_wr" => test_rwlock_wr(),
+            "try_rwlock_wr" => test_try_rwlock_wr(),
+            _ => unreachable!(),
+        }
+        // If we reach this point then the test failed
+        println!("TEST FAILED: {}", args[1]);
+    } else {
+        let mut v = vec![];
+        v.push(Command::new(&args[0]).arg("mutex").stderr(Stdio::null()).spawn().unwrap());
+        v.push(Command::new(&args[0]).arg("try_mutex").stderr(Stdio::null()).spawn().unwrap());
+        v.push(Command::new(&args[0]).arg("rwlock_ww").stderr(Stdio::null()).spawn().unwrap());
+        v.push(Command::new(&args[0]).arg("try_rwlock_ww").stderr(Stdio::null()).spawn().unwrap());
+        v.push(Command::new(&args[0]).arg("rwlock_rw").stderr(Stdio::null()).spawn().unwrap());
+        v.push(Command::new(&args[0]).arg("try_rwlock_rw").stderr(Stdio::null()).spawn().unwrap());
+        v.push(Command::new(&args[0]).arg("rwlock_wr").stderr(Stdio::null()).spawn().unwrap());
+        v.push(Command::new(&args[0]).arg("try_rwlock_wr").stderr(Stdio::null()).spawn().unwrap());
+
+        thread::sleep(Duration::new(1, 0));
+
+        // Make sure all subprocesses either panicked or were killed because they deadlocked
+        for mut c in v {
+            c.kill().ok();
+            assert!(!c.wait().unwrap().success());
+        }
+    }
+}
diff --git a/src/test/run-pass/issue-34074.rs b/src/test/run-pass/issue-34074.rs
new file mode 100644 (file)
index 0000000..169a87f
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Make sure several unnamed function arguments don't conflict with each other
+
+trait Tr {
+    fn f(u8, u8) {}
+}
+
+fn main() {
+}
diff --git a/src/test/run-pass/issue-34932.rs b/src/test/run-pass/issue-34932.rs
new file mode 100644 (file)
index 0000000..e83939e
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags:--test
+// rustc-env:RUSTC_BOOTSTRAP_KEY=
+// ignore-pretty : (#23623) problems when  ending with // comments
+
+#![cfg(any())] // This test should be configured away
+#![feature(rustc_attrs)] // Test that this is allowed on stable/beta
+#![feature(iter_arith_traits)] // Test that this is not unused
+#![deny(unused_features)]
+
+#[test]
+fn dummy() {
+    let () = "this should not reach type-checking";
+}
index badc013cd621f6297af82991af69264dc35b0073..0008825226ba0161fdcd88b4c92e457c42331ec6 100644 (file)
@@ -11,6 +11,7 @@
 
 #![feature(advanced_slice_patterns)]
 #![feature(slice_patterns)]
+#![feature(rustc_attrs)]
 
 use std::ops::Add;
 
@@ -21,6 +22,7 @@ fn bar(a: &'static str, b: &'static str) -> [&'static str; 4] {
     [a, b, b, a]
 }
 
+#[rustc_mir]
 fn main() {
     assert_eq!(foo([1, 2, 3]), (1, 3, 6));
 
index 7589bce31f480ca7faeb98051748ee4878a82777..8d15fe30a1b07dd9116bc5adee951cebfa9ceb13 100644 (file)
@@ -19,13 +19,11 @@ use std::thread;
 macro_rules! check {
     ($($e:expr),*) => {
         $(assert!(thread::spawn({
-            #[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
             move|| { $e; }
         }).join().is_err());)*
     }
 }
 
-#[rustc_no_mir] // FIXME #29769 MIR overflow checking is TBD.
 fn main() {
     check![
         isize::min_value() / -1,
diff --git a/src/test/run-pass/issue34796.rs b/src/test/run-pass/issue34796.rs
new file mode 100644 (file)
index 0000000..0fb6ccc
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This test case exposes conditions where the encoding of a trait object type
+// with projection predicates would differ between this crate and the upstream
+// crate, because the predicates were encoded in different order within each
+// crate. This led to different symbol hashes of functions using these type,
+// which in turn led to linker errors because the two crates would not agree on
+// the symbol name.
+// The fix was to make the order in which predicates get encoded stable.
+
+// aux-build:issue34796aux.rs
+extern crate issue34796aux;
+
+fn mk<T>() -> T { loop {} }
+
+struct Data<T, E> {
+    data: T,
+    error: E,
+}
+
+fn main() {
+    issue34796aux::bar(|()| {
+        Data::<(), std::io::Error> {
+            data: mk(),
+            error: mk(),
+        }
+    })
+}
diff --git a/src/test/run-pass/match-unsized.rs b/src/test/run-pass/match-unsized.rs
new file mode 100644 (file)
index 0000000..7253672
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    let data: &'static str = "Hello, World!";
+    match data {
+        &ref xs => {
+            assert_eq!(data, xs);
+        }
+    }
+}
index 43e0b442251bd112e948324a2a7b68ba8d3f570e..010c1455210084b521a898b125e183d8d1f2f1cb 100644 (file)
 
 #![feature(advanced_slice_patterns)]
 #![feature(slice_patterns)]
+#![feature(rustc_attrs)]
 
+#[rustc_mir]
 fn match_vecs<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str {
     match (l1, l2) {
-        ([], []) => "both empty",
-        ([], [..]) | ([..], []) => "one empty",
-        ([..], [..]) => "both non-empty"
+        (&[], &[]) => "both empty",
+        (&[], &[..]) | (&[..], &[]) => "one empty",
+        (&[..], &[..]) => "both non-empty"
     }
 }
 
+#[rustc_mir]
 fn match_vecs_cons<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str {
     match (l1, l2) {
-        ([], []) => "both empty",
-        ([], [_, ..]) | ([_, ..], []) => "one empty",
-        ([_, ..], [_, ..]) => "both non-empty"
+        (&[], &[]) => "both empty",
+        (&[], &[_, ..]) | (&[_, ..], &[]) => "one empty",
+        (&[_, ..], &[_, ..]) => "both non-empty"
     }
 }
 
+#[rustc_mir]
 fn match_vecs_snoc<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str {
     match (l1, l2) {
-        ([], []) => "both empty",
-        ([], [.., _]) | ([.., _], []) => "one empty",
-        ([.., _], [.., _]) => "both non-empty"
+        (&[], &[]) => "both empty",
+        (&[], &[.., _]) | (&[.., _], &[]) => "one empty",
+        (&[.., _], &[.., _]) => "both non-empty"
     }
 }
 
+#[rustc_mir]
 fn match_nested_vecs_cons<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
     match (l1, l2) {
-        (Some([]), Ok([])) => "Some(empty), Ok(empty)",
-        (Some([_, ..]), Ok(_)) | (Some([_, ..]), Err(())) => "Some(non-empty), any",
-        (None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)",
-        (None, Ok([_, _, ..])) => "None, Ok(at least two elements)",
+        (Some(&[]), Ok(&[])) => "Some(empty), Ok(empty)",
+        (Some(&[_, ..]), Ok(_)) | (Some(&[_, ..]), Err(())) => "Some(non-empty), any",
+        (None, Ok(&[])) | (None, Err(())) | (None, Ok(&[_])) => "None, Ok(less than one element)",
+        (None, Ok(&[_, _, ..])) => "None, Ok(at least two elements)",
         _ => "other"
     }
 }
 
+#[rustc_mir]
 fn match_nested_vecs_snoc<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
     match (l1, l2) {
-        (Some([]), Ok([])) => "Some(empty), Ok(empty)",
-        (Some([.., _]), Ok(_)) | (Some([.., _]), Err(())) => "Some(non-empty), any",
-        (None, Ok([])) | (None, Err(())) | (None, Ok([_])) => "None, Ok(less than one element)",
-        (None, Ok([.., _, _])) => "None, Ok(at least two elements)",
+        (Some(&[]), Ok(&[])) => "Some(empty), Ok(empty)",
+        (Some(&[.., _]), Ok(_)) | (Some(&[.., _]), Err(())) => "Some(non-empty), any",
+        (None, Ok(&[])) | (None, Err(())) | (None, Ok(&[_])) => "None, Ok(less than one element)",
+        (None, Ok(&[.., _, _])) => "None, Ok(at least two elements)",
         _ => "other"
     }
 }
diff --git a/src/test/run-pass/mir_call_with_associated_type.rs b/src/test/run-pass/mir_call_with_associated_type.rs
new file mode 100644 (file)
index 0000000..08401c2
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+trait Trait {
+    type Type;
+}
+
+impl<'a> Trait for &'a () {
+    type Type = u32;
+}
+
+#[rustc_mir]
+fn foo<'a>(t: <&'a () as Trait>::Type) -> <&'a () as Trait>::Type {
+    t
+}
+
+#[rustc_mir]
+fn main() {
+    assert_eq!(foo(4), 4);
+}
diff --git a/src/test/run-pass/mir_cast_fn_ret.rs b/src/test/run-pass/mir_cast_fn_ret.rs
new file mode 100644 (file)
index 0000000..8a72396
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+pub extern "C" fn tuple2() -> (u16, u8) {
+    (1, 2)
+}
+
+pub extern "C" fn tuple3() -> (u8, u8, u8) {
+    (1, 2, 3)
+}
+
+#[rustc_mir]
+pub fn test2() -> u8 {
+    tuple2().1
+}
+
+#[rustc_mir]
+pub fn test3() -> u8 {
+    tuple3().2
+}
+
+fn main() {
+    assert_eq!(test2(), 2);
+    assert_eq!(test3(), 3);
+}
index 4e9c0bce646e6f98782f4b72b85522eb7dfff927..0ce9e88ef3dbea7cc8d03c9f3212e0143d556b8a 100644 (file)
@@ -15,21 +15,23 @@ struct Point {
     _y: i32,
 }
 
+#[derive(PartialEq, Eq, Debug)]
+struct Newtype<T>(T);
+
 const STRUCT: Point = Point { _x: 42, _y: 42 };
 const TUPLE1: (i32, i32) = (42, 42);
 const TUPLE2: (&'static str, &'static str) = ("hello","world");
+const PAIR_NEWTYPE: (Newtype<i32>, Newtype<i32>) = (Newtype(42), Newtype(42));
 
 #[rustc_mir]
-fn mir() -> (Point, (i32, i32), (&'static str, &'static str)){
+fn mir() -> (Point, (i32, i32), (&'static str, &'static str), (Newtype<i32>, Newtype<i32>)) {
     let struct1 = STRUCT;
     let tuple1 = TUPLE1;
     let tuple2 = TUPLE2;
-    (struct1, tuple1, tuple2)
+    let pair_newtype = PAIR_NEWTYPE;
+    (struct1, tuple1, tuple2, pair_newtype)
 }
 
-#[derive(PartialEq, Eq, Debug)]
-struct Newtype<T>(T);
-
 const NEWTYPE: Newtype<&'static str> = Newtype("foobar");
 
 #[rustc_mir]
@@ -39,7 +41,7 @@ fn test_promoted_newtype_str_ref() {
 }
 
 fn main(){
-    assert_eq!(mir(), (STRUCT, TUPLE1, TUPLE2));
+    assert_eq!(mir(), (STRUCT, TUPLE1, TUPLE2, PAIR_NEWTYPE));
     test_promoted_newtype_str_ref();
 }
 
index 31e2c8925711cc28959bda1a13351178f03ceb14..2371909b31b779f96d46f57240526edc607775a5 100644 (file)
@@ -30,6 +30,7 @@ fn test2(a: isize) -> isize {
     callee(a)
 }
 
+#[derive(PartialEq, Eq, Debug)]
 struct Foo;
 impl Foo {
     fn inherent_method(&self, a: isize) -> isize { a }
@@ -147,6 +148,29 @@ fn test_fn_transmute_zst(x: ()) -> [(); 1] {
     })
 }
 
+#[rustc_mir]
+fn test_fn_ignored_pair() -> ((), ()) {
+    ((), ())
+}
+
+#[rustc_mir]
+fn test_fn_ignored_pair_0() {
+    test_fn_ignored_pair().0
+}
+
+#[rustc_mir]
+fn id<T>(x: T) -> T { x }
+
+#[rustc_mir]
+fn ignored_pair_named() -> (Foo, Foo) {
+    (Foo, Foo)
+}
+
+#[rustc_mir]
+fn test_fn_ignored_pair_named() -> (Foo, Foo) {
+    id(ignored_pair_named())
+}
+
 fn main() {
     assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..]));
     assert_eq!(test2(98), 98);
@@ -169,4 +193,7 @@ fn main() {
 
     assert_eq!(test_fn_nil_call(&(|| 42)), 42);
     assert_eq!(test_fn_transmute_zst(()), [()]);
+
+    assert_eq!(test_fn_ignored_pair_0(), ());
+    assert_eq!(test_fn_ignored_pair_named(), (Foo, Foo));
 }
index 8e0b14128c83b0896cb93b218ad8c5b61829c460..93e2a854ccb2a3efe0bbf5b0e544e8a94f1cd381 100644 (file)
@@ -15,7 +15,7 @@ fn check_for_no_backtrace(test: std::process::Output) {
 
     assert_eq!(it.next().map(|l| l.starts_with("thread '<unnamed>' panicked at")), Some(true));
     assert_eq!(it.next(), Some("note: Run with `RUST_BACKTRACE=1` for a backtrace."));
-    assert_eq!(it.next().map(|l| l.starts_with("thread '<main>' panicked at")), Some(true));
+    assert_eq!(it.next().map(|l| l.starts_with("thread 'main' panicked at")), Some(true));
     assert_eq!(it.next(), None);
 }
 
index 2c87c6b92686ca6beff5ec22a731364b745bb352..0210017b47efb63f20519c12d648a24777f4f353 100644 (file)
 
 // ignore-emscripten no threads support
 
-#![feature(std_panic, recover, panic_propagate, panic_handler, const_fn)]
+#![feature(panic_handler)]
 
-use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
 use std::panic;
 use std::thread;
 
-static A: AtomicUsize = AtomicUsize::new(0);
+static A: AtomicUsize = ATOMIC_USIZE_INIT;
 
 fn main() {
-    panic::set_handler(|_| {
+    panic::set_hook(Box::new(|_| {
         A.fetch_add(1, Ordering::SeqCst);
-    });
+    }));
 
     let result = thread::spawn(|| {
-        let result = panic::recover(|| {
+        let result = panic::catch_unwind(|| {
             panic!("hi there");
         });
 
-        panic::propagate(result.unwrap_err());
+        panic::resume_unwind(result.unwrap_err());
     }).join();
 
     let msg = *result.unwrap_err().downcast::<&'static str>().unwrap();
diff --git a/src/test/run-pass/pat-tuple-1.rs b/src/test/run-pass/pat-tuple-1.rs
new file mode 100644 (file)
index 0000000..c379621
--- /dev/null
@@ -0,0 +1,104 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+fn tuple() {
+    let x = (1, 2, 3);
+    match x {
+        (a, b, ..) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+        }
+    }
+    match x {
+        (.., b, c) => {
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        (a, .., c) => {
+            assert_eq!(a, 1);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        (a, b, c) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        (a, b, c, ..) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        (.., a, b, c) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+}
+
+fn tuple_struct() {
+    struct S(u8, u8, u8);
+
+    let x = S(1, 2, 3);
+    match x {
+        S(a, b, ..) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+        }
+    }
+    match x {
+        S(.., b, c) => {
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        S(a, .., c) => {
+            assert_eq!(a, 1);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        S(a, b, c) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        S(a, b, c, ..) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+    match x {
+        S(.., a, b, c) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+    }
+}
+
+fn main() {
+    tuple();
+    tuple_struct();
+}
diff --git a/src/test/run-pass/pat-tuple-2.rs b/src/test/run-pass/pat-tuple-2.rs
new file mode 100644 (file)
index 0000000..881e96a
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+fn tuple() {
+    let x = (1,);
+    match x {
+        (2, ..) => panic!(),
+        (..) => ()
+    }
+}
+
+fn tuple_struct() {
+    struct S(u8);
+
+    let x = S(1);
+    match x {
+        S(2, ..) => panic!(),
+        S(..) => ()
+    }
+}
+
+fn main() {
+    tuple();
+    tuple_struct();
+}
diff --git a/src/test/run-pass/pat-tuple-3.rs b/src/test/run-pass/pat-tuple-3.rs
new file mode 100644 (file)
index 0000000..94d33d4
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+fn tuple() {
+    let x = (1, 2, 3);
+    let branch = match x {
+        (1, 1, ..) => 0,
+        (1, 2, 3, ..) => 1,
+        (1, 2, ..) => 2,
+        _ => 3
+    };
+    assert_eq!(branch, 1);
+}
+
+fn tuple_struct() {
+    struct S(u8, u8, u8);
+
+    let x = S(1, 2, 3);
+    let branch = match x {
+        S(1, 1, ..) => 0,
+        S(1, 2, 3, ..) => 1,
+        S(1, 2, ..) => 2,
+        _ => 3
+    };
+    assert_eq!(branch, 1);
+}
+
+fn main() {
+    tuple();
+    tuple_struct();
+}
diff --git a/src/test/run-pass/pat-tuple-4.rs b/src/test/run-pass/pat-tuple-4.rs
new file mode 100644 (file)
index 0000000..ffd82fe
--- /dev/null
@@ -0,0 +1,68 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+fn tuple() {
+    let x = (1, 2, 3);
+    match x {
+        (1, 2, 4) => unreachable!(),
+        (0, 2, 3, ..) => unreachable!(),
+        (0, .., 3) => unreachable!(),
+        (0, ..) => unreachable!(),
+        (1, 2, 3) => (),
+        (_, _, _) => unreachable!(),
+    }
+    match x {
+        (..) => (),
+    }
+    match x {
+        (_, _, _, ..) => (),
+    }
+    match x {
+        (a, b, c) => {
+            assert_eq!(1, a);
+            assert_eq!(2, b);
+            assert_eq!(3, c);
+        }
+    }
+}
+
+fn tuple_struct() {
+    struct S(u8, u8, u8);
+
+    let x = S(1, 2, 3);
+    match x {
+        S(1, 2, 4) => unreachable!(),
+        S(0, 2, 3, ..) => unreachable!(),
+        S(0, .., 3) => unreachable!(),
+        S(0, ..) => unreachable!(),
+        S(1, 2, 3) => (),
+        S(_, _, _) => unreachable!(),
+    }
+    match x {
+        S(..) => (),
+    }
+    match x {
+        S(_, _, _, ..) => (),
+    }
+    match x {
+        S(a, b, c) => {
+            assert_eq!(1, a);
+            assert_eq!(2, b);
+            assert_eq!(3, c);
+        }
+    }
+}
+
+fn main() {
+    tuple();
+    tuple_struct();
+}
diff --git a/src/test/run-pass/pat-tuple-5.rs b/src/test/run-pass/pat-tuple-5.rs
new file mode 100644 (file)
index 0000000..41c4d02
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+fn tuple() {
+    struct S;
+    struct Z;
+    struct W;
+    let x = (S, Z, W);
+    match x { (S, ..) => {} }
+    match x { (.., W) => {} }
+    match x { (S, .., W) => {} }
+    match x { (.., Z, _) => {} }
+}
+
+fn tuple_struct() {
+    struct SS(S, Z, W);
+
+    struct S;
+    struct Z;
+    struct W;
+    let x = SS(S, Z, W);
+    match x { SS(S, ..) => {} }
+    match x { SS(.., W) => {} }
+    match x { SS(S, .., W) => {} }
+    match x { SS(.., Z, _) => {} }
+}
+
+fn main() {
+    tuple();
+    tuple_struct();
+}
diff --git a/src/test/run-pass/pat-tuple-6.rs b/src/test/run-pass/pat-tuple-6.rs
new file mode 100644 (file)
index 0000000..6f3f2b3
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(dotdot_in_tuple_patterns)]
+
+fn tuple() {
+    let x = (1, 2, 3, 4, 5);
+    match x {
+        (a, .., b, c) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 4);
+            assert_eq!(c, 5);
+        }
+    }
+    match x {
+        (a, b, c, .., d) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+            assert_eq!(d, 5);
+        }
+    }
+}
+
+fn tuple_struct() {
+    struct S(u8, u8, u8, u8, u8);
+
+    let x = S(1, 2, 3, 4, 5);
+    match x {
+        S(a, .., b, c) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 4);
+            assert_eq!(c, 5);
+        }
+    }
+    match x {
+        S(a, b, c, .., d) => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+            assert_eq!(d, 5);
+        }
+    }
+}
+
+fn main() {
+    tuple();
+    tuple_struct();
+}
diff --git a/src/test/run-pass/project-cache-issue-31849.rs b/src/test/run-pass/project-cache-issue-31849.rs
new file mode 100644 (file)
index 0000000..d03424b
--- /dev/null
@@ -0,0 +1,75 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #31849: the problem here was actually a performance
+// cliff, but I'm adding the test for reference.
+
+pub trait Upcast<T> {
+    fn upcast(self) -> T;
+}
+
+impl<S1, S2, T1, T2> Upcast<(T1, T2)> for (S1,S2)
+    where S1: Upcast<T1>,
+          S2: Upcast<T2>,
+{
+    fn upcast(self) -> (T1, T2) { (self.0.upcast(), self.1.upcast()) }
+}
+
+impl Upcast<()> for ()
+{
+    fn upcast(self) -> () { () }
+}
+
+pub trait ToStatic {
+    type Static: 'static;
+    fn to_static(self) -> Self::Static where Self: Sized;
+}
+
+impl<T, U> ToStatic for (T, U)
+    where T: ToStatic,
+          U: ToStatic
+{
+    type Static = (T::Static, U::Static);
+    fn to_static(self) -> Self::Static { (self.0.to_static(), self.1.to_static()) }
+}
+
+impl ToStatic for ()
+{
+    type Static = ();
+    fn to_static(self) -> () { () }
+}
+
+
+trait Factory {
+    type Output;
+    fn build(&self) -> Self::Output;
+}
+
+impl<S,T> Factory for (S, T)
+    where S: Factory,
+          T: Factory,
+          S::Output: ToStatic,
+          <S::Output as ToStatic>::Static: Upcast<S::Output>,
+{
+    type Output = (S::Output, T::Output);
+    fn build(&self) -> Self::Output { (self.0.build().to_static().upcast(), self.1.build()) }
+}
+
+impl Factory for () {
+    type Output = ();
+    fn build(&self) -> Self::Output { () }
+}
+
+fn main() {
+    // More parens, more time.
+    let it = ((((((((((),()),()),()),()),()),()),()),()),());
+    it.build();
+}
+
index 07233a43b88e29308b81c84ad0ecba3ca52e9ee9..aaf129e7b8e4abf413e65847100a8dcf03f95ab8 100644 (file)
@@ -117,11 +117,6 @@ pub fn main() {
     assert_eq!(nonsense.next(), None);
     assert_eq!(nonsense, RangeInclusive::Empty { at: 10 });
 
-    // conversion
-    assert_eq!(0...9, (0..10).into());
-    assert_eq!(0...0, (0..1).into());
-    assert_eq!(RangeInclusive::Empty { at: 1 }, (1..0).into());
-
     // output
     assert_eq!(format!("{:?}", 0...10), "0...10");
     assert_eq!(format!("{:?}", ...10), "...10");
index 8a23403359f9a148f16ecafd46a0ad802b7e099b..75a2e36ffb7a4a3f1ad0d5497fab556a5a0c4063 100644 (file)
@@ -10,8 +10,6 @@
 
 // aux-build:reachable-unnameable-items.rs
 
-#![feature(recover)]
-
 extern crate reachable_unnameable_items;
 use reachable_unnameable_items::*;
 
@@ -37,5 +35,5 @@ fn main() {
 
     let none = None;
     function_accepting_unnameable_type(none);
-    let _guard = std::panic::recover(|| none.unwrap().method_of_unnameable_type3());
+    let _guard = std::panic::catch_unwind(|| none.unwrap().method_of_unnameable_type3());
 }
index c67bc8c8368e8738191e24ed30e0fac935892095..23d5a08e216443bf5e8a5b74c04fc4e5ab969491 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(std_panic, recover, start)]
+#![feature(start)]
 
 use std::ffi::CStr;
 use std::process::{Command, Output};
@@ -22,8 +22,8 @@ fn start(argc: isize, argv: *const *const u8) -> isize {
             match **argv.offset(1) as char {
                 '1' => {}
                 '2' => println!("foo"),
-                '3' => assert!(panic::recover(|| {}).is_ok()),
-                '4' => assert!(panic::recover(|| panic!()).is_err()),
+                '3' => assert!(panic::catch_unwind(|| {}).is_ok()),
+                '4' => assert!(panic::catch_unwind(|| panic!()).is_err()),
                 '5' => assert!(Command::new("test").spawn().is_err()),
                 _ => panic!()
             }
diff --git a/src/test/run-pass/sleep.rs b/src/test/run-pass/sleep.rs
new file mode 100644 (file)
index 0000000..8b06b02
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::thread::{self, sleep};
+use std::time::Duration;
+use std::sync::{Arc, Mutex};
+use std::u64;
+
+fn main() {
+    let finished = Arc::new(Mutex::new(false));
+    let t_finished = finished.clone();
+    thread::spawn(move || {
+        sleep(Duration::new(u64::MAX, 0));
+        *t_finished.lock().unwrap() = true;
+    });
+    sleep(Duration::from_millis(100));
+    assert_eq!(*finished.lock().unwrap(), false);
+}
diff --git a/src/test/run-pass/test-vs-cfg-test.rs b/src/test/run-pass/test-vs-cfg-test.rs
new file mode 100644 (file)
index 0000000..708fde5
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --cfg test
+
+// Make sure `--cfg test` does not inject test harness
+
+#[test]
+fn test() { panic!(); }
+
+fn main() {}
diff --git a/src/test/run-pass/thread-local-syntax.rs b/src/test/run-pass/thread-local-syntax.rs
new file mode 100644 (file)
index 0000000..a596724
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![deny(missing_docs)]
+//! this tests the syntax of `thread_local!`
+
+thread_local! {
+    // no docs
+    #[allow(unused)]
+    static FOO: i32 = 42;
+    /// docs
+    pub static BAR: String = String::from("bar");
+}
+thread_local!(static BAZ: u32 = 0);
+
+fn main() {}
diff --git a/src/test/run-pass/trait-item-inside-macro.rs b/src/test/run-pass/trait-item-inside-macro.rs
new file mode 100644 (file)
index 0000000..7c13576
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Issue #34183
+
+macro_rules! foo {
+    () => {
+        fn foo() { }
+    }
+}
+
+macro_rules! bar {
+    () => {
+        fn bar();
+    }
+}
+
+trait Bleh {
+    foo!();
+    bar!();
+}
+
+struct Test;
+
+impl Bleh for Test {
+    fn bar() {}
+}
+
+fn main() {
+    Test::bar();
+    Test::foo();
+}
index 4d369ba4d872db10d68159c890bd72c48e6ed335..0f751501293f538cdda52298994a86599dc74363 100644 (file)
@@ -23,8 +23,6 @@ pub fn main() {
     assert_eq!(s.chars().count(), 4);
     assert_eq!(schs.len(), 4);
     assert_eq!(schs.iter().cloned().collect::<String>(), s);
-    assert_eq!(s.char_at(0), 'e');
-    assert_eq!(s.char_at(1), 'é');
 
     assert!((str::from_utf8(s.as_bytes()).is_ok()));
     // invalid prefix
index abd1709825c974b605f03b011c5113f0bb3afc25..0131563d36d35e05838561731a5ca21c156b7fbd 100644 (file)
@@ -8,60 +8,39 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// ignore-msvc -- sprintf isn't a symbol in msvcrt? maybe a #define?
-
-#![feature(libc, std_misc)]
-
-extern crate libc;
-
-use std::ffi::{CStr, CString};
-use libc::{c_char, c_int};
-
-
+#[link(name = "rust_test_helpers")]
 extern {
-    fn sprintf(s: *mut c_char, format: *const c_char, ...) -> c_int;
-}
-
-unsafe fn check<T, F>(expected: &str, f: F) where F: FnOnce(*mut c_char) -> T {
-    let mut x = [0 as c_char; 50];
-    f(&mut x[0] as *mut c_char);
-    assert_eq!(expected.as_bytes(), CStr::from_ptr(x.as_ptr()).to_bytes());
+    fn rust_interesting_average(_: u64, ...) -> f64;
 }
 
 pub fn main() {
-
+    // Call without variadic arguments
     unsafe {
-        // Call with just the named parameter
-        let c = CString::new(&b"Hello World\n"[..]).unwrap();
-        check("Hello World\n", |s| sprintf(s, c.as_ptr()));
-
-        // Call with variable number of arguments
-        let c = CString::new(&b"%d %f %c %s\n"[..]).unwrap();
-        check("42 42.500000 a %d %f %c %s\n\n", |s| {
-            sprintf(s, c.as_ptr(), 42, 42.5f64, 'a' as c_int, c.as_ptr());
-        });
+        assert!(rust_interesting_average(0).is_nan());
+    }
 
-        // Make a function pointer
-        let x: unsafe extern fn(*mut c_char, *const c_char, ...) -> c_int = sprintf;
+    // Call with direct arguments
+    unsafe {
+        assert_eq!(rust_interesting_average(1, 10i64, 10.0f64) as i64, 20);
+    }
 
-        // A function that takes a function pointer
-        unsafe fn call(fp: unsafe extern fn(*mut c_char, *const c_char, ...) -> c_int) {
-            // Call with just the named parameter
-            let c = CString::new(&b"Hello World\n"[..]).unwrap();
-            check("Hello World\n", |s| fp(s, c.as_ptr()));
+    // Call with named arguments, variable number of them
+    let (x1, x2, x3, x4) = (10i64, 10.0f64, 20i64, 20.0f64);
+    unsafe {
+        assert_eq!(rust_interesting_average(2, x1, x2, x3, x4) as i64, 30);
+    }
 
-            // Call with variable number of arguments
-            let c = CString::new(&b"%d %f %c %s\n"[..]).unwrap();
-            check("42 42.500000 a %d %f %c %s\n\n", |s| {
-                fp(s, c.as_ptr(), 42, 42.5f64, 'a' as c_int, c.as_ptr());
-            });
-        }
+    // A function that takes a function pointer
+    unsafe fn call(fp: unsafe extern fn(u64, ...) -> f64) {
+        let (x1, x2, x3, x4) = (10i64, 10.0f64, 20i64, 20.0f64);
+        assert_eq!(fp(2, x1, x2, x3, x4) as i64, 30);
+    }
 
-        // Pass sprintf directly
-        call(sprintf);
+    unsafe {
+        call(rust_interesting_average);
 
-        // Pass sprintf indirectly
+        // Make a function pointer, pass indirectly
+        let x: unsafe extern fn(u64, ...) -> f64 = rust_interesting_average;
         call(x);
     }
-
 }
index ee70ea58750d9d5bd0560d3e1bf8868fb313680c..7a6129d311ee386e83a51ca4fb706ac2fa0809b5 100644 (file)
 
 #![feature(advanced_slice_patterns)]
 #![feature(slice_patterns)]
+#![feature(rustc_attrs)]
 
+use std::fmt::Debug;
+
+#[rustc_mir(graphviz="mir.gv")]
 fn foldl<T, U, F>(values: &[T],
                   initial: U,
                   mut function: F)
                   -> U where
-    U: Clone,
+    U: Clone+Debug, T:Debug,
     F: FnMut(U, &T) -> U,
-{
-    match values {
-        [ref head, tail..] =>
+{    match values {
+        &[ref head, ref tail..] =>
             foldl(tail, function(initial, head), function),
-        [] => initial.clone()
+        &[] => {
+            // FIXME: call guards
+            let res = initial.clone(); res
+        }
     }
 }
 
+#[rustc_mir]
 fn foldr<T, U, F>(values: &[T],
                   initial: U,
                   mut function: F)
@@ -34,9 +41,12 @@ fn foldr<T, U, F>(values: &[T],
     F: FnMut(&T, U) -> U,
 {
     match values {
-        [head.., ref tail] =>
+        &[ref head.., ref tail] =>
             foldr(head, function(tail, initial), function),
-        [] => initial.clone()
+        &[] => {
+            // FIXME: call guards
+            let res = initial.clone(); res
+        }
     }
 }
 
index e7553c8e157e3ce3ba99771069d8d0fac65a472c..1093bc7c18b867e6f6e817e32527df9e09d6b52d 100644 (file)
@@ -8,14 +8,15 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(slice_patterns)]
+#![feature(slice_patterns, rustc_attrs)]
 
+#[rustc_mir]
 pub fn main() {
     let x = &[1, 2, 3, 4, 5];
     let x: &[isize] = &[1, 2, 3, 4, 5];
     if !x.is_empty() {
         let el = match x {
-            [1, ref tail..] => &tail[0],
+            &[1, ref tail..] => &tail[0],
             _ => unreachable!()
         };
         println!("{}", *el);
index eedf27f85770044a2867fe853eaa5cabb286b134..075709a63b5f5d2978947ea1062e189fc7440a6a 100644 (file)
@@ -11,7 +11,9 @@
 
 #![feature(advanced_slice_patterns)]
 #![feature(slice_patterns)]
+#![feature(rustc_attrs)]
 
+#[rustc_mir]
 fn a() {
     let x = [1];
     match x {
@@ -21,6 +23,7 @@ fn a() {
     }
 }
 
+#[rustc_mir]
 fn b() {
     let x = [1, 2, 3];
     match x {
@@ -56,6 +59,48 @@ fn b() {
     }
 }
 
+
+#[rustc_mir]
+fn b_slice() {
+    let x : &[_] = &[1, 2, 3];
+    match x {
+        &[a, b, ref c..] => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            let expected: &[_] = &[3];
+            assert_eq!(c, expected);
+        }
+        _ => unreachable!()
+    }
+    match x {
+        &[ref a.., b, c] => {
+            let expected: &[_] = &[1];
+            assert_eq!(a, expected);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+        _ => unreachable!()
+    }
+    match x {
+        &[a, ref b.., c] => {
+            assert_eq!(a, 1);
+            let expected: &[_] = &[2];
+            assert_eq!(b, expected);
+            assert_eq!(c, 3);
+        }
+        _ => unreachable!()
+    }
+    match x {
+        &[a, b, c] => {
+            assert_eq!(a, 1);
+            assert_eq!(b, 2);
+            assert_eq!(c, 3);
+        }
+        _ => unreachable!()
+    }
+}
+
+#[rustc_mir]
 fn c() {
     let x = [1];
     match x {
@@ -64,6 +109,7 @@ fn c() {
     }
 }
 
+#[rustc_mir]
 fn d() {
     let x = [1, 2, 3];
     let branch = match x {
@@ -75,17 +121,40 @@ fn d() {
     assert_eq!(branch, 1);
 }
 
+#[rustc_mir]
 fn e() {
     let x: &[isize] = &[1, 2, 3];
-    match x {
-        [1, 2] => (),
-        [..] => ()
-    }
+    let a = match *x {
+        [1, 2] => 0,
+        [..] => 1,
+    };
+
+    assert_eq!(a, 1);
+
+    let b = match *x {
+        [2, ..] => 0,
+        [1, 2, ..] => 1,
+        [_] => 2,
+        [..] => 3
+    };
+
+    assert_eq!(b, 1);
+
+
+    let c = match *x {
+        [_, _, _, _, ..] => 0,
+        [1, 2, ..] => 1,
+        [_] => 2,
+        [..] => 3
+    };
+
+    assert_eq!(c, 1);
 }
 
 pub fn main() {
     a();
     b();
+    b_slice();
     c();
     d();
     e();
index 6cc7e3a072cf040d4dc60f574d17d66764295c96..6084a0d07a114911f2e180062d159f3e42f11f6c 100644 (file)
 
 
 #![feature(slice_patterns)]
+#![feature(rustc_attrs)]
 
 struct Foo {
-    string: String
+    string: &'static str
 }
 
+#[rustc_mir]
 pub fn main() {
     let x = [
-        Foo { string: "foo".to_string() },
-        Foo { string: "bar".to_string() },
-        Foo { string: "baz".to_string() }
+        Foo { string: "foo" },
+        Foo { string: "bar" },
+        Foo { string: "baz" }
     ];
     match x {
-        [ref first, tail..] => {
-            assert_eq!(first.string, "foo".to_string());
+        [ref first, ref tail..] => {
+            assert_eq!(first.string, "foo");
             assert_eq!(tail.len(), 2);
-            assert_eq!(tail[0].string, "bar".to_string());
-            assert_eq!(tail[1].string, "baz".to_string());
+            assert_eq!(tail[0].string, "bar");
+            assert_eq!(tail[1].string, "baz");
 
-            match tail {
-                [Foo { .. }, _, Foo { .. }, _tail..] => {
+            match *(tail as &[_]) {
+                [Foo { .. }, _, Foo { .. }, ref _tail..] => {
                     unreachable!();
                 }
                 [Foo { string: ref a }, Foo { string: ref b }] => {
diff --git a/src/test/run-pass/xcrate_generic_fn_nested_return.rs b/src/test/run-pass/xcrate_generic_fn_nested_return.rs
new file mode 100644 (file)
index 0000000..181c916
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:xcrate_generic_fn_nested_return.rs
+
+extern crate xcrate_generic_fn_nested_return as test;
+
+pub fn main() {
+    assert!(test::decode::<()>().is_err());
+}
index 697508ae4888916085dc2067a8e0cf309b4e7453..00f4aa98a3e062bf2694beca39bb2882f682f2d6 100644 (file)
@@ -8,15 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
+#![feature(rustc_attrs)]
 #![feature(slice_patterns)]
 
+#[rustc_mir]
 fn main() {
     let x = [(), ()];
 
     // The subslice used to go out of bounds for zero-sized array items, check that this doesn't
     // happen anymore
     match x {
-        [_, y..] => assert_eq!(&x[1] as *const (), &y[0] as *const ())
+        [_, ref y..] => assert_eq!(&x[1] as *const (), &y[0] as *const ())
     }
 }
diff --git a/src/test/rustdoc/auxiliary/issue-34274.rs b/src/test/rustdoc/auxiliary/issue-34274.rs
new file mode 100644 (file)
index 0000000..72026b6
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern {
+    pub fn extern_c_fn();
+}
diff --git a/src/test/rustdoc/auxiliary/src-links-external.rs b/src/test/rustdoc/auxiliary/src-links-external.rs
new file mode 100644 (file)
index 0000000..94b7278
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub struct Foo;
diff --git a/src/test/rustdoc/deprecated-impls.rs b/src/test/rustdoc/deprecated-impls.rs
new file mode 100644 (file)
index 0000000..bcf0645
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name = "foo"]
+
+// @has foo/struct.Foo0.html
+pub struct Foo0;
+
+impl Foo0 {
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.1: fn_with_doc'
+    // @has - 'fn_with_doc short'
+    // @has - 'fn_with_doc full'
+    /// fn_with_doc short
+    ///
+    /// fn_with_doc full
+    #[deprecated(since = "1.0.1", note = "fn_with_doc")]
+    pub fn fn_with_doc() {}
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.2: fn_without_doc'
+    #[deprecated(since = "1.0.2", note = "fn_without_doc")]
+    pub fn fn_without_doc() {}
+}
+
+pub trait Bar {
+    /// fn_empty_with_doc short
+    ///
+    /// fn_empty_with_doc full
+    #[deprecated(since = "1.0.3", note = "fn_empty_with_doc")]
+    fn fn_empty_with_doc();
+
+    #[deprecated(since = "1.0.4", note = "fn_empty_without_doc")]
+    fn fn_empty_without_doc();
+
+    /// fn_def_with_doc short
+    ///
+    /// fn_def_with_doc full
+    #[deprecated(since = "1.0.5", note = "fn_def_with_doc")]
+    fn fn_def_with_doc() {}
+
+    #[deprecated(since = "1.0.6", note = "fn_def_without_doc")]
+    fn fn_def_without_doc() {}
+
+    /// fn_def_def_with_doc short
+    ///
+    /// fn_def_def_with_doc full
+    #[deprecated(since = "1.0.7", note = "fn_def_def_with_doc")]
+    fn fn_def_def_with_doc() {}
+
+    #[deprecated(since = "1.0.8", note = "fn_def_def_without_doc")]
+    fn fn_def_def_without_doc() {}
+}
+
+// @has foo/struct.Foo1.html
+pub struct Foo1;
+
+impl Bar for Foo1 {
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.3: fn_empty_with_doc'
+    // @has - 'fn_empty_with_doc_impl short'
+    // @has - 'fn_empty_with_doc_impl full'
+    /// fn_empty_with_doc_impl short
+    ///
+    /// fn_empty_with_doc_impl full
+    fn fn_empty_with_doc() {}
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.4: fn_empty_without_doc'
+    fn fn_empty_without_doc() {}
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.5: fn_def_with_doc'
+    // @has - 'fn_def_with_doc_impl short'
+    // @has - 'fn_def_with_doc_impl full'
+    /// fn_def_with_doc_impl short
+    ///
+    /// fn_def_with_doc_impl full
+    fn fn_def_with_doc() {}
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.6: fn_def_without_doc'
+    fn fn_def_without_doc() {}
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.7: fn_def_def_with_doc'
+    // @has - 'fn_def_def_with_doc short'
+    // @!has - 'fn_def_def_with_doc full'
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.8: fn_def_def_without_doc'
+}
+
+// @has foo/struct.Foo2.html
+pub struct Foo2;
+
+impl Bar for Foo2 {
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.3: fn_empty_with_doc'
+    // @has - 'fn_empty_with_doc short'
+    // @!has - 'fn_empty_with_doc full'
+    fn fn_empty_with_doc() {}
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.4: fn_empty_without_doc'
+    // @has - 'fn_empty_without_doc_impl short'
+    // @has - 'fn_empty_without_doc_impl full'
+    /// fn_empty_without_doc_impl short
+    ///
+    /// fn_empty_without_doc_impl full
+    fn fn_empty_without_doc() {}
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.5: fn_def_with_doc'
+    // @has - 'fn_def_with_doc short'
+    // @!has - 'fn_def_with full'
+    fn fn_def_with_doc() {}
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.6: fn_def_without_doc'
+    // @has - 'fn_def_without_doc_impl short'
+    // @has - 'fn_def_without_doc_impl full'
+    /// fn_def_without_doc_impl short
+    ///
+    /// fn_def_without_doc_impl full
+    fn fn_def_without_doc() {}
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.7: fn_def_def_with_doc'
+    // @has - 'fn_def_def_with_doc short'
+    // @!has - 'fn_def_def_with_doc full'
+
+    // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.8: fn_def_def_without_doc'
+}
diff --git a/src/test/rustdoc/hidden-impls.rs b/src/test/rustdoc/hidden-impls.rs
new file mode 100644 (file)
index 0000000..203c56e
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name = "foo"]
+
+mod hidden {
+    #[derive(Clone)]
+    pub struct Foo;
+}
+
+#[doc(hidden)]
+pub mod __hidden {
+    pub use hidden::Foo;
+}
+
+// @has foo/trait.Clone.html
+// @!has - 'Foo'
+// @has implementors/foo/trait.Clone.js
+// @!has - 'Foo'
+pub use std::clone::Clone;
diff --git a/src/test/rustdoc/hidden-methods.rs b/src/test/rustdoc/hidden-methods.rs
new file mode 100644 (file)
index 0000000..18f5f08
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name = "foo"]
+
+#[doc(hidden)]
+pub mod hidden {
+    pub struct Foo;
+
+    impl Foo {
+        #[doc(hidden)]
+        pub fn this_should_be_hidden() {}
+    }
+
+    pub struct Bar;
+
+    impl Bar {
+        fn this_should_be_hidden() {}
+    }
+}
+
+// @has foo/struct.Foo.html
+// @!has - 'Methods'
+// @!has - 'impl Foo'
+// @!has - 'this_should_be_hidden'
+pub use hidden::Foo;
+
+// @has foo/struct.Bar.html
+// @!has - 'Methods'
+// @!has - 'impl Bar'
+// @!has - 'this_should_be_hidden'
+pub use hidden::Bar;
diff --git a/src/test/rustdoc/inline_cross/hidden-use.rs b/src/test/rustdoc/inline_cross/hidden-use.rs
new file mode 100644 (file)
index 0000000..dd668c2
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:rustdoc-hidden.rs
+// build-aux-docs
+// ignore-cross-compile
+
+extern crate rustdoc_hidden;
+
+// @has hidden_use/index.html
+// @!has - 'rustdoc_hidden'
+// @!has - 'Bar'
+// @!has hidden_use/struct.Bar.html
+#[doc(hidden)]
+pub use rustdoc_hidden::Bar;
diff --git a/src/test/rustdoc/inline_local/hidden-use.rs b/src/test/rustdoc/inline_local/hidden-use.rs
new file mode 100644 (file)
index 0000000..1b1dafc
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod private {
+    pub struct Foo {}
+}
+
+// @has hidden_use/index.html
+// @!has - 'private'
+// @!has - 'Foo'
+// @!has hidden_use/struct.Foo.html
+#[doc(hidden)]
+pub use private::Foo;
diff --git a/src/test/rustdoc/issue-34025.rs b/src/test/rustdoc/issue-34025.rs
new file mode 100644 (file)
index 0000000..8c0a770
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name = "foo"]
+
+// @!has 'foo/sys/index.html'
+// @!has 'foo/sys/sidebar-items.js'
+#[doc(hidden)]
+pub mod sys {
+    extern "C" {
+        // @!has 'foo/sys/fn.foo.html'
+        #[doc(hidden)]
+        pub fn foo();
+    }
+}
diff --git a/src/test/rustdoc/issue-34274.rs b/src/test/rustdoc/issue-34274.rs
new file mode 100644 (file)
index 0000000..971c89b
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:issue-34274.rs
+// build-aux-docs
+// ignore-cross-compile
+
+#![crate_name = "foo"]
+
+extern crate issue_34274;
+
+// @has foo/fn.extern_c_fn.html '//a/@href' '../issue_34274/fn.extern_c_fn.html?gotosrc='
+pub use issue_34274::extern_c_fn;
diff --git a/src/test/rustdoc/issue-34423.rs b/src/test/rustdoc/issue-34423.rs
new file mode 100644 (file)
index 0000000..460462d
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub struct Foo;
+
+pub trait Bar {
+    #[doc(hidden)]
+    fn bar() {}
+}
+
+impl Bar for Foo {
+    fn bar() {}
+}
diff --git a/src/test/rustdoc/issue-34473.rs b/src/test/rustdoc/issue-34473.rs
new file mode 100644 (file)
index 0000000..a6de638
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name = "foo"]
+
+mod second {
+    pub struct SomeTypeWithLongName;
+}
+
+// @has foo/index.html
+// @!has - SomeTypeWithLongName
+// @has foo/struct.SomeType.html
+// @!has - SomeTypeWithLongName
+// @!has foo/struct.SomeTypeWithLongName.html
+pub use second::{SomeTypeWithLongName as SomeType};
diff --git a/src/test/rustdoc/module-impls.rs b/src/test/rustdoc/module-impls.rs
new file mode 100644 (file)
index 0000000..7be3c50
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name = "foo"]
+
+pub use std::marker::Send;
+
+// @!has foo/index.html 'Implementations'
diff --git a/src/test/rustdoc/redirect-const.rs b/src/test/rustdoc/redirect-const.rs
new file mode 100644 (file)
index 0000000..c95e553
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name="foo"]
+
+pub use hidden::STATIC_FOO;
+pub use hidden::CONST_FOO;
+
+mod hidden {
+    // @has foo/hidden/static.STATIC_FOO.html
+    // @has - '//p/a' '../../foo/static.STATIC_FOO.html'
+    pub static STATIC_FOO: u64 = 0;
+    // @has foo/hidden/constant.CONST_FOO.html
+    // @has - '//p/a' '../../foo/constant.CONST_FOO.html'
+    pub const CONST_FOO: u64 = 0;
+}
diff --git a/src/test/rustdoc/redirect-rename.rs b/src/test/rustdoc/redirect-rename.rs
new file mode 100644 (file)
index 0000000..b7c702d
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_name = "foo"]
+
+mod hidden {
+    // @has foo/hidden/struct.Foo.html
+    // @has - '//p/a' '../../foo/struct.FooBar.html'
+    pub struct Foo {}
+
+    // @has foo/hidden/bar/index.html
+    // @has - '//p/a' '../../foo/baz/index.html'
+    pub mod bar {
+        // @has foo/hidden/bar/struct.Thing.html
+        // @has - '//p/a' '../../foo/baz/struct.Thing.html'
+        pub struct Thing {}
+    }
+}
+
+// @has foo/struct.FooBar.html
+pub use hidden::Foo as FooBar;
+
+// @has foo/baz/index.html
+// @has foo/baz/struct.Thing.html
+pub use hidden::bar as baz;
diff --git a/src/test/rustdoc/src-links-external.rs b/src/test/rustdoc/src-links-external.rs
new file mode 100644 (file)
index 0000000..e9db4f5
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:src-links-external.rs
+// build-aux-docs
+// ignore-cross-compile
+
+#![crate_name = "foo"]
+
+extern crate src_links_external;
+
+// @has foo/bar/index.html '//a/@href' '../src_links_external/index.html?gotosrc='
+pub use src_links_external as bar;
+
+// @has foo/bar/struct.Foo.html '//a/@href' '../src_links_external/struct.Foo.html?gotosrc='
diff --git a/src/test/ui/mismatched_types/issue-26480.rs b/src/test/ui/mismatched_types/issue-26480.rs
new file mode 100644 (file)
index 0000000..96db31f
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// rustc-env:RUST_NEW_ERROR_FORMAT
+extern {
+    fn write(fildes: i32, buf: *const i8, nbyte: u64) -> i64;
+}
+
+#[inline(always)]
+fn size_of<T>(_: T) -> usize {
+    ::std::mem::size_of::<T>()
+}
+
+macro_rules! write {
+    ($arr:expr) => {{
+        #[allow(non_upper_case_globals)]
+        const stdout: i32 = 1;
+        unsafe {
+            write(stdout, $arr.as_ptr() as *const i8,
+                  $arr.len() * size_of($arr[0]));
+        }
+    }}
+}
+
+macro_rules! cast {
+    ($x:expr) => ($x as ())
+}
+
+fn main() {
+    let hello = ['H', 'e', 'y'];
+    write!(hello);
+    cast!(2);
+}
diff --git a/src/test/ui/mismatched_types/issue-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr
new file mode 100644 (file)
index 0000000..c00594a
--- /dev/null
@@ -0,0 +1,15 @@
+error: mismatched types [--explain E0308]
+  --> $DIR/issue-26480.rs:27:19
+   |>
+27 |>                   $arr.len() * size_of($arr[0]));
+   |>                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u64, found usize
+$DIR/issue-26480.rs:38:5: 38:19: note: in this expansion of write! (defined in $DIR/issue-26480.rs)
+
+error: non-scalar cast: `_` as `()`
+  --> $DIR/issue-26480.rs:33:19
+   |>
+33 |>     ($x:expr) => ($x as ())
+   |>                   ^^^^^^^^
+$DIR/issue-26480.rs:39:5: 39:14: note: in this expansion of cast! (defined in $DIR/issue-26480.rs)
+
+error: aborting due to 2 previous errors
index 98bc11752e0edaa7dc15c9fbb44caefad8047c95..1af332ee5bea7036b7f216bb554b6c3420a4f67e 100644 (file)
@@ -1,5 +1,6 @@
 error: mismatched types [--explain E0308]
   --> $DIR/main.rs:14:18
+   |>
 14 |>     let x: u32 = (
    |>                  ^ expected u32, found ()
 note: expected type `u32`
index 7e688b5ec6b461c62c2c8fa16c53ca524d904876..8b7164819a7907390d3105335ae38039d47b9d65 100644 (file)
@@ -21,20 +21,18 @@ struct Test {
     lock: Option<&'static str>,
 }
 
-const TEST_REPOS: &'static [Test] = &[
-    Test {
-        name: "cargo",
-        repo: "https://github.com/rust-lang/cargo",
-        sha: "26288f799427f9cc6e8bdddd782a17a8156ebc64",
-        lock: None,
-    },
-    Test {
-        name: "iron",
-        repo: "https://github.com/iron/iron",
-        sha: "16c858ec2901e2992fe5e529780f59fa8ed12903",
-        lock: Some(include_str!("lockfiles/iron-Cargo.lock")),
-    },
-];
+const TEST_REPOS: &'static [Test] = &[Test {
+                                          name: "cargo",
+                                          repo: "https://github.com/rust-lang/cargo",
+                                          sha: "7d79da08238e3d47e0bc4406155bdcc45ccb8c82",
+                                          lock: None,
+                                      },
+                                      Test {
+                                          name: "iron",
+                                          repo: "https://github.com/iron/iron",
+                                          sha: "16c858ec2901e2992fe5e529780f59fa8ed12903",
+                                          lock: Some(include_str!("lockfiles/iron-Cargo.lock")),
+                                      }];
 
 
 fn main() {
@@ -52,8 +50,10 @@ fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) {
     println!("testing {}", test.repo);
     let dir = clone_repo(test, out_dir);
     if let Some(lockfile) = test.lock {
-        File::create(&dir.join("Cargo.lock")).expect("")
-            .write_all(lockfile.as_bytes()).expect("");
+        File::create(&dir.join("Cargo.lock"))
+            .expect("")
+            .write_all(lockfile.as_bytes())
+            .expect("");
     }
     if !run_cargo_test(cargo, &dir) {
         panic!("tests failed for {}", test.repo);
@@ -65,10 +65,10 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
 
     if !out_dir.join(".git").is_dir() {
         let status = Command::new("git")
-            .arg("init")
-            .arg(&out_dir)
-            .status()
-            .expect("");
+                         .arg("init")
+                         .arg(&out_dir)
+                         .status()
+                         .expect("");
         assert!(status.success());
     }
 
@@ -77,23 +77,23 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
     for depth in &[0, 1, 10, 100, 1000, 100000] {
         if *depth > 0 {
             let status = Command::new("git")
-                .arg("fetch")
-                .arg(test.repo)
-                .arg("master")
-                .arg(&format!("--depth={}", depth))
-                .current_dir(&out_dir)
-                .status()
-                .expect("");
+                             .arg("fetch")
+                             .arg(test.repo)
+                             .arg("master")
+                             .arg(&format!("--depth={}", depth))
+                             .current_dir(&out_dir)
+                             .status()
+                             .expect("");
             assert!(status.success());
         }
 
         let status = Command::new("git")
-            .arg("reset")
-            .arg(test.sha)
-            .arg("--hard")
-            .current_dir(&out_dir)
-            .status()
-            .expect("");
+                         .arg("reset")
+                         .arg(test.sha)
+                         .arg("--hard")
+                         .current_dir(&out_dir)
+                         .status()
+                         .expect("");
 
         if status.success() {
             found = true;
@@ -105,11 +105,11 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
         panic!("unable to find commit {}", test.sha)
     }
     let status = Command::new("git")
-        .arg("clean")
-        .arg("-fdx")
-        .current_dir(&out_dir)
-        .status()
-        .unwrap();
+                     .arg("clean")
+                     .arg("-fdx")
+                     .current_dir(&out_dir)
+                     .status()
+                     .unwrap();
     assert!(status.success());
 
     out_dir
index 84b78547ab9a4af718bb6cbdd508650c60eddf50..e5b628bb0029545022dab31220dc8e89ef668470 100644 (file)
@@ -15,7 +15,7 @@ use std::path::Path;
 use runtest::{ProcRes};
 
 // These structs are a subset of the ones found in
-// `syntax::errors::json`.
+// `syntax::json`.
 
 #[derive(RustcEncodable, RustcDecodable)]
 struct Diagnostic {
index cc687b532047e616ac3adc874afb676b3b1006f3..6830f32bb2ce117bb522b0762ef4a57fdf04a15f 100644 (file)
@@ -254,6 +254,17 @@ pub fn run_tests(config: &Config) {
 
     match config.mode {
         DebugInfoLldb => {
+            if let Some(lldb_version) = config.lldb_version.as_ref() {
+                if is_blacklisted_lldb_version(&lldb_version[..]) {
+                    println!("WARNING: The used version of LLDB ({}) has a \
+                              known issue that breaks debuginfo tests. See \
+                              issue #32520 for more information. Skipping all \
+                              LLDB-based tests!",
+                             lldb_version);
+                    return
+                }
+            }
+
             // Some older versions of LLDB seem to have problems with multiple
             // instances running in parallel, so only run one test thread at a
             // time.
@@ -524,3 +535,7 @@ fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
     }
     None
 }
+
+fn is_blacklisted_lldb_version(version: &str) -> bool {
+    version == "350"
+}
index 88ca0f9caf134f0044003bbef34991dae7143765..577da5c5af11d2b012eed6d4f860731c3050a452 100644 (file)
@@ -371,9 +371,16 @@ actual:\n\
         } else {
             &*self.config.target
         };
+
+        let out_dir = self.output_base_name().with_extension("pretty-out");
+        let _ = fs::remove_dir_all(&out_dir);
+        self.create_dir_racy(&out_dir);
+
         // FIXME (#9639): This needs to handle non-utf8 paths
         let mut args = vec!("-".to_owned(),
                             "-Zno-trans".to_owned(),
+                            "--out-dir".to_owned(),
+                            out_dir.to_str().unwrap().to_owned(),
                             format!("--target={}", target),
                             "-L".to_owned(),
                             self.config.build_base.to_str().unwrap().to_owned(),
@@ -1005,8 +1012,7 @@ actual:\n\
 
         // Parse the JSON output from the compiler and extract out the messages.
         let actual_errors = json::parse_output(&file_name, &proc_res.stderr, &proc_res);
-        let mut unexpected = 0;
-        let mut not_found = 0;
+        let mut unexpected = Vec::new();
         let mut found = vec![false; expected_errors.len()];
         for actual_error in &actual_errors {
             let opt_index =
@@ -1038,12 +1044,13 @@ actual:\n\
                                      .map_or(String::from("message"),
                                              |k| k.to_string()),
                                      actual_error.msg));
-                        unexpected += 1;
+                        unexpected.push(actual_error.clone());
                     }
                 }
             }
         }
 
+        let mut not_found = Vec::new();
         // anything not yet found is a problem
         for (index, expected_error) in expected_errors.iter().enumerate() {
             if !found[index] {
@@ -1055,18 +1062,22 @@ actual:\n\
                              .map_or("message".into(),
                                      |k| k.to_string()),
                              expected_error.msg));
-                not_found += 1;
+                not_found.push(expected_error.clone());
             }
         }
 
-        if unexpected > 0 || not_found > 0 {
+        if unexpected.len() > 0 || not_found.len() > 0 {
             self.error(
                 &format!("{} unexpected errors found, {} expected errors not found",
-                         unexpected, not_found));
+                         unexpected.len(), not_found.len()));
             print!("status: {}\ncommand: {}\n",
                    proc_res.status, proc_res.cmdline);
-            println!("actual errors (from JSON output): {:#?}\n", actual_errors);
-            println!("expected errors (from test file): {:#?}\n", expected_errors);
+            if unexpected.len() > 0 {
+                println!("unexpected errors (from JSON output): {:#?}\n", unexpected);
+            }
+            if not_found.len() > 0 {
+                println!("not found errors (from test file): {:#?}\n", not_found);
+            }
             panic!();
         }
     }
index a7c8c01fab8503febe4d85a50eb74e0d2ccbf4c8..80c37d5597592de4e873a4876f1b274219c8e837 100644 (file)
@@ -75,30 +75,20 @@ struct FileEntry {
 type Cache = HashMap<PathBuf, FileEntry>;
 
 impl FileEntry {
-    fn parse_ids(&mut self,
-                file: &Path,
-                contents: &str,
-                errors: &mut bool)
-{
+    fn parse_ids(&mut self, file: &Path, contents: &str, errors: &mut bool) {
         if self.ids.is_empty() {
             with_attrs_in_source(contents, " id", |fragment, i| {
                 let frag = fragment.trim_left_matches("#").to_owned();
                 if !self.ids.insert(frag) {
                     *errors = true;
-                    println!("{}:{}: id is not unique: `{}`",
-                             file.display(), i, fragment);
+                    println!("{}:{}: id is not unique: `{}`", file.display(), i, fragment);
                 }
             });
         }
     }
 }
 
-fn walk(cache: &mut Cache,
-        root: &Path,
-        dir: &Path,
-        url: &mut Url,
-        errors: &mut bool)
-{
+fn walk(cache: &mut Cache, root: &Path, dir: &Path, url: &mut Url, errors: &mut bool) {
     for entry in t!(dir.read_dir()).map(|e| t!(e)) {
         let path = entry.path();
         let kind = t!(entry.file_type());
@@ -122,8 +112,8 @@ fn check(cache: &mut Cache,
          root: &Path,
          file: &Path,
          base: &Url,
-         errors: &mut bool) -> Option<PathBuf>
-{
+         errors: &mut bool)
+         -> Option<PathBuf> {
     // ignore js files as they are not prone to errors as the rest of the
     // documentation is and they otherwise bring up false positives.
     if file.extension().and_then(|s| s.to_str()) == Some("js") {
@@ -148,22 +138,6 @@ fn check(cache: &mut Cache,
         return None;
     }
 
-    if file.ends_with("std/sys/ext/index.html") {
-        return None;
-    }
-
-    if let Some(file) = file.to_str() {
-        // FIXME(#31948)
-        if file.contains("ParseFloatError") {
-            return None;
-        }
-        // weird reexports, but this module is on its way out, so chalk it up to
-        // "rustdoc weirdness" and move on from there
-        if file.contains("scoped_tls") {
-            return None;
-        }
-    }
-
     let mut parser = UrlParser::new();
     parser.base_url(base);
 
@@ -173,24 +147,45 @@ fn check(cache: &mut Cache,
         Err(_) => return None,
     };
     {
-        cache.get_mut(&pretty_file).unwrap()
-                                   .parse_ids(&pretty_file, &contents, errors);
+        cache.get_mut(&pretty_file)
+             .unwrap()
+             .parse_ids(&pretty_file, &contents, errors);
     }
 
     // Search for anything that's the regex 'href[ ]*=[ ]*".*?"'
     with_attrs_in_source(&contents, " href", |url, i| {
+        // Ignore external URLs
+        if url.starts_with("http:") || url.starts_with("https:") ||
+           url.starts_with("javascript:") || url.starts_with("ftp:") ||
+           url.starts_with("irc:") || url.starts_with("data:") {
+            return;
+        }
         // Once we've plucked out the URL, parse it using our base url and
-        // then try to extract a file path. If either of these fail then we
-        // just keep going.
+        // then try to extract a file path.
         let (parsed_url, path) = match url_to_file_path(&parser, url) {
             Some((url, path)) => (url, PathBuf::from(path)),
-            None => return,
+            None => {
+                *errors = true;
+                println!("{}:{}: invalid link - {}",
+                         pretty_file.display(),
+                         i + 1,
+                         url);
+                return;
+            }
         };
 
         // Alright, if we've found a file name then this file had better
         // exist! If it doesn't then we register and print an error.
         if path.exists() {
             if path.is_dir() {
+                // Links to directories show as directory listings when viewing
+                // the docs offline so it's best to avoid them.
+                *errors = true;
+                let pretty_path = path.strip_prefix(root).unwrap_or(&path);
+                println!("{}:{}: directory link - {}",
+                         pretty_file.display(),
+                         i + 1,
+                         pretty_path.display());
                 return;
             }
             let res = load_file(cache, root, path.clone(), FromRedirect(false));
@@ -198,8 +193,11 @@ fn check(cache: &mut Cache,
                 Ok(res) => res,
                 Err(LoadError::IOError(err)) => panic!(format!("{}", err)),
                 Err(LoadError::BrokenRedirect(target, _)) => {
-                    print!("{}:{}: broken redirect to {}",
-                           pretty_file.display(), i + 1, target.display());
+                    *errors = true;
+                    println!("{}:{}: broken redirect to {}",
+                             pretty_file.display(),
+                             i + 1,
+                             target.display());
                     return;
                 }
                 Err(LoadError::IsRedirect) => unreachable!(),
@@ -219,9 +217,9 @@ fn check(cache: &mut Cache,
                 if !entry.ids.contains(fragment) {
                     *errors = true;
                     print!("{}:{}: broken link fragment  ",
-                           pretty_file.display(), i + 1);
-                    println!("`#{}` pointing to `{}`",
-                             fragment, pretty_path.display());
+                           pretty_file.display(),
+                           i + 1);
+                    println!("`#{}` pointing to `{}`", fragment, pretty_path.display());
                 };
             }
         } else {
@@ -237,7 +235,8 @@ fn check(cache: &mut Cache,
 fn load_file(cache: &mut Cache,
              root: &Path,
              file: PathBuf,
-             redirect: Redirect) -> Result<(PathBuf, String), LoadError> {
+             redirect: Redirect)
+             -> Result<(PathBuf, String), LoadError> {
     let mut contents = String::new();
     let pretty_file = PathBuf::from(file.strip_prefix(root).unwrap_or(&file));
 
@@ -245,7 +244,7 @@ fn load_file(cache: &mut Cache,
         Entry::Occupied(entry) => {
             contents = entry.get().source.clone();
             None
-        },
+        }
         Entry::Vacant(entry) => {
             let mut fp = try!(File::open(file.clone()).map_err(|err| {
                 if let FromRedirect(true) = redirect {
@@ -269,7 +268,7 @@ fn load_file(cache: &mut Cache,
                 });
             }
             maybe
-        },
+        }
     };
     let base = Url::from_file_path(&file).unwrap();
     let mut parser = UrlParser::new();
@@ -280,7 +279,7 @@ fn load_file(cache: &mut Cache,
             let path = PathBuf::from(redirect_file);
             load_file(cache, root, path, FromRedirect(true))
         }
-        None => Ok((pretty_file, contents))
+        None => Ok((pretty_file, contents)),
     }
 }
 
@@ -301,25 +300,22 @@ fn maybe_redirect(source: &str) -> Option<String> {
 }
 
 fn url_to_file_path(parser: &UrlParser, url: &str) -> Option<(Url, PathBuf)> {
-    parser.parse(url).ok().and_then(|parsed_url| {
-        parsed_url.to_file_path().ok().map(|f| (parsed_url, f))
-    })
+    parser.parse(url)
+          .ok()
+          .and_then(|parsed_url| parsed_url.to_file_path().ok().map(|f| (parsed_url, f)))
 }
 
-fn with_attrs_in_source<F: FnMut(&str, usize)>(contents: &str,
-                                               attr: &str,
-                                               mut f: F)
-{
+fn with_attrs_in_source<F: FnMut(&str, usize)>(contents: &str, attr: &str, mut f: F) {
     for (i, mut line) in contents.lines().enumerate() {
         while let Some(j) = line.find(attr) {
-            let rest = &line[j + attr.len() ..];
+            let rest = &line[j + attr.len()..];
             line = rest;
             let pos_equals = match rest.find("=") {
                 Some(i) => i,
                 None => continue,
             };
             if rest[..pos_equals].trim_left_matches(" ") != "" {
-                continue
+                continue;
             }
 
             let rest = &rest[pos_equals + 1..];
@@ -331,7 +327,7 @@ fn with_attrs_in_source<F: FnMut(&str, usize)>(contents: &str,
             let quote_delim = rest.as_bytes()[pos_quote] as char;
 
             if rest[..pos_quote].trim_left_matches(" ") != "" {
-                continue
+                continue;
             }
             let rest = &rest[pos_quote + 1..];
             let url = match rest.find(quote_delim) {
index e0092f8e29e732b86d78369a833e9c4cb28d278d..436dc1197533638504fb8c6e808f1e8d51137b1c 100644 (file)
@@ -10,7 +10,6 @@
 
 #![deny(warnings)]
 
-#![feature(iter_arith)]
 #![feature(rustc_private)]
 #![feature(rustdoc)]
 #![feature(question_mark)]
index 6a9d52cb0048f17eb913378a525f80e6c966b2fb..48016721d52c10783095b721b182b72566dd70af 100644 (file)
@@ -81,11 +81,10 @@ fn verify(tomlfile: &Path, libfile: &Path, bad: &mut bool) {
         }
 
         // This is intentional, this dependency just makes the crate available
-        // for others later on.
-        if krate == "alloc_jemalloc" && toml.contains("name = \"std\"") {
-            continue
-        }
-        if krate == "panic_abort" && toml.contains("name = \"std\"") {
+        // for others later on. Cover cases
+        let whitelisted = krate == "alloc_jemalloc";
+        let whitelisted = whitelisted || krate.starts_with("panic");
+        if toml.contains("name = \"std\"") && whitelisted {
             continue
         }